From 28f5c80f2034db182636abcd929aef0c9fed0d37 Mon Sep 17 00:00:00 2001 From: Tom Clark Date: Mon, 26 Oct 2026 17:22:22 +0100 Subject: [PATCH] REF: Upgrade to ironlab 0.16 and remove the figure workarounds Move every figure to ironlab 0.16.0 and drop the workarounds that 0.12 forced, recorded in thclark/ironlab#76-#80: - yfoil plot analysis draws Cp on a reversed axis instead of -Cp. - yfoil plot foil keeps equal aspect instead of limits widened from plot areas measured on 0.12 renders, has a curve key and a marker key of custom items, and cycles six dash patterns. - Key rows are fitted (fit_row) instead of hand-sized weights and trial PDF exports; a base or default case is drawn last but listed first on a row of its own; stacks sharing x show tick labels on the bottom row. - Aerofoil-series sheets share regular ticks and left-aligned titles, with the original titles restored. - Comparison sheets mark NaN values with axes-fraction ticks at the top edge, so the axes autoscale again, draw reference lines with hline/vline and set case names in monospace. - The transition-position strip names its rows by case on a categorical axis. - figures::save refuses a figure whose layout overflows a legend, clashes two entries or puts text off the page, without exporting a PDF. Build the branch-coverage branch map and case matrix as ironlab occupancy and cell figures in Rust (thclark/ironlab#74), and remove the matplotlib path (plot.py, studies/figures/) and the committed SVGs. The per-subroutine counts the matrix had in its margin are a published table, until ironlab has a table chart (thclark/ironlab#121). Regenerate every committed figure (schema 0.4 files are rejected), each study from the run its docs were published from so no table changes, and pin the docs' ironlab-web bundle to 0.16.0. Rename figure labels to tags after ironlab. --- CLAUDE.md | 12 +- Cargo.lock | 29 +- Cargo.toml | 2 +- docs/development/conventions/figures.md | 44 +- docs/development/testing/README.md | 6 +- docs/user-guide/commands/plotting.md | 16 +- docs/user-guide/figures/a5.fig | Bin 9450 -> 9693 bytes docs/user-guide/figures/compare.fig | Bin 6962 -> 7288 bytes docs/user-guide/figures/foil.fig | Bin 28734 -> 29445 bytes docs/user-guide/figures/intro.fig | Bin 9522 -> 9848 bytes docs/user-guide/figures/naca0012_a5.fig | Bin 9450 -> 9693 bytes docs/user-guide/figures/naca0012_polar.fig | Bin 3948 -> 4250 bytes docs/user-guide/figures/naca4412_foil.fig | Bin 10785 -> 10864 bytes docs/user-guide/figures/naca4412_polar.fig | Bin 4204 -> 4506 bytes docs/user-guide/get-started/install.md | 2 +- .../figures/karman-trefftz.fig | Bin 60057 -> 60972 bytes .../aerofoil-series/figures/naca-16.fig | Bin 54338 -> 55034 bytes .../figures/naca-4-digit-modified.fig | Bin 65276 -> 66225 bytes .../aerofoil-series/figures/naca-4-digit.fig | Bin 81332 -> 82379 bytes .../aerofoil-series/figures/naca-5-digit.fig | Bin 70848 -> 72015 bytes .../aerofoil-series/figures/naca-6.fig | Bin 73469 -> 74651 bytes .../aerofoil-series/figures/naca-6a.fig | Bin 49162 -> 50011 bytes .../aerofoil-series/generator-families.md | 16 +- docs/validation/aerofoil-series/tables/run.md | 2 +- .../branch-coverage/figures/branch-map.fig | Bin 0 -> 58842 bytes .../branch-coverage/figures/branch-map.svg | 9041 ----------------- .../branch-coverage/figures/case-matrix.fig | Bin 0 -> 8677 bytes .../branch-coverage/figures/case-matrix.svg | 5464 ---------- docs/validation/branch-coverage/index.md | 2 +- .../tables/subroutine-counts.md | 53 + .../branch-coverage/the-minimal-finite-set.md | 10 +- ...rs-naca0012_n240_polar30_re1e6_iter200.fig | Bin 36263 -> 37490 bytes ...a16-212_n160_polar30_re1e6_m07_iter100.fig | Bin 47601 -> 49243 bytes ...a16-212_n240_polar30_re1e6_m07_iter200.fig | Bin 51258 -> 52900 bytes ...olars-naca16-212_n60_polar30_re1e6_m07.fig | Bin 40207 -> 41636 bytes ...aca23012_n60_polar30_re1e6_xtr022_0001.fig | Bin 30710 -> 32023 bytes ...ars-naca4412_n160_inviscid_polar30_m07.fig | Bin 30184 -> 30778 bytes ...ars-naca4412_n240_inviscid_polar30_m07.fig | Bin 30184 -> 30778 bytes ...rs-naca4412_n240_polar30_re1e6_iter200.fig | Bin 43751 -> 45186 bytes ...aca4412_n240_polar30_re1e6_m06_iter200.fig | Bin 38202 -> 39448 bytes ...rs-naca4412_n240_polar30_re3e6_iter200.fig | Bin 49175 -> 50754 bytes ...lars-naca4412_n60_inviscid_polar30_m07.fig | Bin 30181 -> 30775 bytes ...-naca63-415_n160_polar30_re1e6_iter100.fig | Bin 22954 -> 24382 bytes ...a63-415_n160_polar30_re1e6_m05_iter100.fig | Bin 23731 -> 25126 bytes ...64a010_n160_inviscid_polar30_type2_m03.fig | Bin 32094 -> 32745 bytes ...-naca64a010_n160_polar30_re1e6_iter100.fig | Bin 23829 -> 25223 bytes ...0_n160_polar30_re1e6_m03_type2_iter100.fig | Bin 27127 -> 28319 bytes ...4a010_n160_polar30_re1e6_type2_iter100.fig | Bin 27507 -> 28929 bytes ...64a010_n240_inviscid_polar30_type2_m03.fig | Bin 32274 -> 32925 bytes ...4a010_n240_polar30_re1e6_type2_iter200.fig | Bin 20762 -> 22045 bytes ...a64a010_n60_inviscid_polar30_type2_m03.fig | Bin 30220 -> 30815 bytes ...ars-naca64a010_n60_polar30_re1e6_type2.fig | Bin 23723 -> 25175 bytes .../polars-kt_n160_inviscid_polar30.fig | Bin 30163 -> 30756 bytes ...ca0012_n160_polar30_re1e4_damp_iter100.fig | Bin 37981 -> 39133 bytes ...rs-naca0012_n160_polar30_re1e6_iter100.fig | Bin 46675 -> 48240 bytes ...0012_n160_polar30_re1e6_ncrit4_iter100.fig | Bin 46215 -> 47693 bytes ...rs-naca0012_n160_polar30_re1e8_iter100.fig | Bin 47311 -> 48802 bytes ...a16-212_n160_polar30_re1e6_m05_iter100.fig | Bin 48698 -> 50255 bytes ..._n160_polar30_re1e6_xtr022_022_iter100.fig | Bin 44427 -> 45991 bytes ...rs-naca4412_n160_clsweep_re1e6_iter100.fig | Bin 16394 -> 17325 bytes ...rs-naca4412_n160_polar30_re1e6_iter100.fig | Bin 42218 -> 43723 bytes ...aca4412_n160_polar30_re1e6_m05_iter100.fig | Bin 48339 -> 49891 bytes ...aca4412_n160_polar30_re1e6_m06_iter100.fig | Bin 44082 -> 45423 bytes ...412_n160_polar30_re1e6_ncrit11_iter100.fig | Bin 44024 -> 45530 bytes ...rs-naca4412_n160_polar30_re1e7_iter100.fig | Bin 46804 -> 48309 bytes ...rs-naca4412_n160_polar30_re3e6_iter100.fig | Bin 45694 -> 47112 bytes docs/validation/series-cases/index.md | 4 +- scripts/docs.sh | 4 +- src/output/figures.rs | 59 +- src/output/foil_plot.rs | 201 +- src/output/mod.rs | 5 +- src/output/plot.rs | 77 +- studies/README.md | 3 +- studies/aerofoil-series/Cargo.toml | 2 +- studies/aerofoil-series/src/sheet.rs | 189 +- studies/aerofoil-series/src/study.rs | 8 +- studies/branch-coverage/Cargo.toml | 3 +- studies/branch-coverage/plot.py | 195 - studies/branch-coverage/src/figures.rs | 258 + studies/branch-coverage/src/main.rs | 30 +- studies/branch-coverage/src/report.rs | 25 +- studies/figures/render.sh | 28 - studies/figures/style.py | 67 - studies/input-sensitivity/Cargo.toml | 2 +- studies/input-sensitivity/src/figures.rs | 78 +- studies/series-cases/Cargo.toml | 2 +- studies/series-cases/src/figures.rs | 387 +- studies/series-cases/src/report.rs | 4 +- studies/study-support/publish.rs | 2 +- .../xfoil-instrumentation-check/Cargo.toml | 2 +- .../xfoil-instrumentation-check/src/figure.rs | 42 +- tests/application/cli_plot.rs | 93 +- xtask/src/publish.rs | 2 +- 93 files changed, 1043 insertions(+), 15428 deletions(-) create mode 100644 docs/validation/branch-coverage/figures/branch-map.fig delete mode 100644 docs/validation/branch-coverage/figures/branch-map.svg create mode 100644 docs/validation/branch-coverage/figures/case-matrix.fig delete mode 100644 docs/validation/branch-coverage/figures/case-matrix.svg create mode 100644 docs/validation/branch-coverage/tables/subroutine-counts.md delete mode 100644 studies/branch-coverage/plot.py create mode 100644 studies/branch-coverage/src/figures.rs delete mode 100755 studies/figures/render.sh delete mode 100644 studies/figures/style.py diff --git a/CLAUDE.md b/CLAUDE.md index d33ad166..eaebcd52 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -462,8 +462,7 @@ and `cargo xtask publish [--run ] [--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//` or `target/fixtures//` 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`, @@ -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 diff --git a/Cargo.lock b/Cargo.lock index bb2199dd..e8d396ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -568,6 +568,7 @@ dependencies = [ name = "branch-coverage" version = "0.1.0" dependencies = [ + "ironlab", "serde", "serde_json", "toml", @@ -1779,9 +1780,9 @@ dependencies = [ [[package]] name = "ironlab" -version = "0.12.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e871db7a272d22e8f165c860f88446f15503d893d9b623ffe23371bc1c47594" +checksum = "d65b727b3a22c073d0313a7734fef667cd34702c209a228a49512cb58ebe0148" dependencies = [ "eframe", "ironlab-canvas", @@ -1796,9 +1797,9 @@ dependencies = [ [[package]] name = "ironlab-canvas" -version = "0.12.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63c461e95cdda6002a63189d7ee0105817e75aaef9267fb98bc2df151c28f34b" +checksum = "d3fd7c9db424ba9867b5f9e00188ddc85913c8f124c5c179fcfc44e561eeb6cf" dependencies = [ "bytemuck", "ecolor", @@ -1818,9 +1819,9 @@ dependencies = [ [[package]] name = "ironlab-ir" -version = "0.12.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7408e7779898f072a4f228f5e1168fc3907642ce1859c223c9913cf52345f1f9" +checksum = "98b1753e5234cc2dd7c0b5db5a00f833dd788f543dfaac4e9a5b78b0046dd955" dependencies = [ "prost", "schemars", @@ -1831,9 +1832,9 @@ dependencies = [ [[package]] name = "ironlab-pdf" -version = "0.12.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c330eaf6294ab321224398d6158ce790deda6229a01f7749039bcbd8363ef892" +checksum = "4d2ee6dfd4beac3197d166b256e8542b8da5c7201a4b64f9df223773b2b6fdcb" dependencies = [ "ironlab-ir", "ironlab-scene", @@ -1844,9 +1845,9 @@ dependencies = [ [[package]] name = "ironlab-scene" -version = "0.12.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d40368c81998b3d0e4b70d622997604c7ab4d2d1ed8e6de128b782358b399dc" +checksum = "6270aaa05242fce24dd390749b4cd51a68ff97ffdecec9f869baa7073961e84d" dependencies = [ "ironlab-ir", "ironlab-text", @@ -1856,9 +1857,9 @@ dependencies = [ [[package]] name = "ironlab-text" -version = "0.12.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb80068a4d4d5103b97f73ccb55e0da46fa400542b23af367976c108c118f84" +checksum = "51d73f342292dc6fe5fb0d3d1fa98bcd6c39ffc97c224acca8d2159ff2feb1d8" dependencies = [ "harfrust 0.13.3", "kurbo", @@ -1869,9 +1870,9 @@ dependencies = [ [[package]] name = "ironlab-viewer" -version = "0.12.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26921110578fa5352c220f697d81567acd299fdeaa966ecf21cfeb3f91071507" +checksum = "7c806b4882d451db5ef8c5211fed6fb5a02da9a1490b4dc1c580910a47aee411" dependencies = [ "eframe", "egui", diff --git a/Cargo.toml b/Cargo.toml index 5c0db7ef..acdd0940 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/docs/development/conventions/figures.md b/docs/development/conventions/figures.md index daaf18d3..76057f70 100644 --- a/docs/development/conventions/figures.md +++ b/docs/development/conventions/figures.md @@ -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 @@ -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*: @@ -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 `__`. -**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 @@ -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 diff --git a/docs/development/testing/README.md b/docs/development/testing/README.md index f183118d..aa67b915 100644 --- a/docs/development/testing/README.md +++ b/docs/development/testing/README.md @@ -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 | |---|---|---| diff --git a/docs/user-guide/commands/plotting.md b/docs/user-guide/commands/plotting.md index 30ba4354..48abda20 100644 --- a/docs/user-guide/commands/plotting.md +++ b/docs/user-guide/commands/plotting.md @@ -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 ``` @@ -65,12 +65,11 @@ yfoil plot analysis a5.json # writes a5.fig For the NACA 4412 at α = 5°, Re = 10⁶: -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 -\(-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 } @@ -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. diff --git a/docs/user-guide/figures/a5.fig b/docs/user-guide/figures/a5.fig index df26fe5d4962a18c138398fe35582b70c57605f9..4d6c7925cf47ba419882b3bfff550db51bf9f64a 100644 GIT binary patch delta 2025 zcmai!e>l|n7RTp13}3@AzU0Sju1RDnmq|93*^X!>DPlK+R?=-BEAnGk?A}LXw_l`g zBO)u+-BPHvO3z+JeWq?PW;CY!SVEiMLufF@;M%=kTis{vv-jE0KcDBE_c`Zzo%5XM z3~%n*LZj2Tj&9ks9cxzOT1;$}K3z|b^6SGr|DT}*zakn=y^(f-D$pX%(&$(aA*5OG zg1;1>tOHxw@k~qIN5EZqgWJ35K8TCa-~DqCKTOzd>QDsAkjM^)~jT#NOYt-wC+e7%I%DB7Oj1Tcxs{7(e@7HSsc?C zp4x??Bhc+orY84v!as2E(YzpNb z957+}OdLZ%ba;C zWd;e$IIMSJ2DM3bM+*~XP>flHHUHEM+Qw)a3(lQE>*m)_8XcHHDv{#ePxho=Gdy5d zph14&J-ruiO`{-_LxZ}JYQ))``2Eu~6{4;%+Mu{MF^*3AFuNKfMo}zgQTp$ce#CF* zt1#DgWP$k|q2aX3yR>6efew*Q?rH#`(S9q7bQNt`EAF5M3>tKD#jtJg8gE-+7nBp; zvO>lHJctOR91nR3W%x z2+4GF;!BwQ!6klNFa!r4*G&g!41!|2Lv^m;Vm~}L??u9&W%kU1f9vEl8G&z&@ z0?ww+>(lV(@bEBkYbvAzl-mpw?UZeB)MdZz-Ii7u8z}O)YS03yTdyme&NP9lVx+Dq z<|zm*ir0-Cegd}jJHIY#lLIy6n72i^9Qq8iuWYBv!Pi6-Fp|{>+C<3>!PfPSkbuSP z&to=%FUxRU;fxG8qZ_7b$7RrPIy9?zLI(VpOIAX)3`!pLo{rMU;Pd>UX17ThxI~w~ zNAHnA8}8HRfJl38x2pIn8EEeDCK%Q-I45r~f4KWG7~C3&yHL{rscQB6MLn#F6&)trBUkd!X)wLZQDKw{t zi941^ft$8JFi=Yh>7QOH4OL3u;mGCSjhzxWu_3=Gy;cI(V{d+Qu3Q3QuY%b1xf1X< zfBy%Y6bYERyb7@lk-%BowR>nT62P?t^Ivo_Bp}~=bM9(iJ>WxmmyTA|!_Mnhy(=>7 zAtC!<%9s3lkULqp?zXE3RgHX(7#9PNm*RG~Mhx7KEFlm@^r6Y?$Go7R27=hyyCdC3{V=OA2vdSqZz?voC zP@GA_ok~zVvCXeN;Y`u3S*fKFD#xf-cy&YQ$vY~n&8S50?FlsPY0UP6B zuXE|Y&2_gT6UdUJ{HF9G*%J#hqnpwRe4#E*U!_fuWK7J4%!mpL;^W$cF`e;ILc-o8 z_GigSpX}bVmx+V&U{Sk*KE`zyBK_l2eiupO=;S!CdCOl%Mq^22 zyyxW9Qpz@@dnyI$PYIp z-3A=S9})h7;YK7O^*4m>IbSTZ`yb&V3TOA6i_7f(gpl%w4R~Tv3n&DM!sHm|^e*FB cFCJEyE{+jeBu>*D10_7ww^8f$< delta 1783 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 GCG#%}L0H8A delta 1856 zcmZvcZA?>F7{@s+Er&ya_O?J<-Y(d};YEZ377Eg;pcP-nh%<(4bG@aMLa7bloL>fJ zSz@AzjwdtD#L3LI#AHhbcZ(*AFY!ywX1rv}Vm38l*<$?QG8UIDW*@evy*G9-^uzPN z`91&hKhHhsx$izA*H?*w8CqL9-ZSiTmQtOWIL$ek9}TFw<{v!8mJ((IA@ZC7MF(eC zis<2BL5k#pXi55X) zH0J4vLk@04amqujWjazWk9$xmV7@~kl`zz)kSY*2E2J9Mu!{0cFOF0rzu`lvUEYEt zZII{vD6WNV3UR{<);d_+u8?|2_A7+TfdQPTLAD1`8s)V?oTy3e+KbW*^Dil6BMc2G zqy@y66|xD|u(rxKhjFAf`SvR)9(m;;j?^wc9zp3?cugUlu!40nEWWM~FC-7$o$C^c z86g4{FJF#bxX^yXP6n;CTZ5;Q&yJluAN6OTPlJ(r$=`lo{Y8a68VvLHv%}xZD(uzZ zrKeSv<@@(lxJ85V((2z+_ZDtsGW+&x^!lGa3|~HXRJHmw_|)fm^x&&^RBJ$kKcBwj zSQ!6Fb#B#Q@autFBX3_-;Wn^@Y)#v>*626a7XN*7zZ%l7K`HRzmw(-QU-b{b5c+p$ zttY9TV|SK5Ra-kX`0L&7KURXDs4$q$pWC%w{-6eplh(ehM(H0PM|c$oMR^R5v^gwhLPtzZ!Ar?jZ%z7X&&4O zH|Z(Qhf||oleCrjDMc^i1zC`fz{Cr)fFUU2#c80X6o5U_q&IU5)Qo8H!^ygm)olrl`QLT#*6&v^s%#Iz(k`Wkst z7B~=<)FOET}}#!8@~{3c+UXO@j*Q z0NYQig(AjgW<$|LXf}2@VJ2wk;@$AwWN)(+Vuz?GR59o5bSN2 z$3o#qLuP@G_kbApHcPLv5nA)&g81R1FD~fiTObzqsHa$qTlR53d>+TwNdHkbAPDfS z@K4<9m%=PWU4om{j2)g1M@Gf}kFrkkHoo0g&WZ#RV*&UABks#bsV9bu(jYOPfc2mbge9c8Z^6O4N&QXCi4dGQ7HG F@*f{4)Kvfg diff --git a/docs/user-guide/figures/intro.fig b/docs/user-guide/figures/intro.fig index d49b6fc447040f4ea4fcca49de769994014dbc9e..6b3c7c2d16852bc5f790a47f762e5807b6c2aa3d 100644 GIT binary patch delta 989 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 Ma1@kUTUW9E1kMfoM*si- diff --git a/docs/user-guide/figures/naca0012_polar.fig b/docs/user-guide/figures/naca0012_polar.fig index dfbd39ac02c393240a5c782780d34f62423f477d..9d9569ad2ce53e90ea53e92c48b77b3bcf5b6fc7 100644 GIT binary patch delta 818 zcmaDOH%pO~i`78SVj`=jh7t#}0FxFs7bhnJ7<{ltZR5!n2I$(T_zXa*AplMn|7g8+jBqXMH86BmP) z^JE7uJ#h^NBOu2Lh#eR`AyzXm7+Fm&;8G38DrY4t0M@U-h*jEwF^WZqiGwjIEGUG7 zRfw01)6db_QNh5#&`5v@Xg9M4%VcX_bL9la48{T=E&|$I!5Gxf#K8u3A0ym-R+B}z zRcY%!PE7Z)Yj9w5A29s5fbL@?I{f%~s1SaE%cU=(6z;^K>91ExbJCblF?E;a)_L$l4@yvG>T)qt`b98BQA1ttrPYm#c_YR&EN<&z z|A<_(4PXSPkm)?PJtUZ2$SENG&0#Bh2C@wpDsWg_*28b` z-X?JIA-5SuY(>(FLX+Sx#mB9G6rFWNJK`0X4ER^Ex?P)YzP+{%7LikecXEfoV<;#W h`nibq>`;m%?xpIk`2@SZPKnT*gKZf=m!$cri(k6{Pdoqs diff --git a/docs/user-guide/figures/naca4412_foil.fig b/docs/user-guide/figures/naca4412_foil.fig index dfe60c3d72909dd5549bef3562d2a1de1b00a86a..bfdac4f7dd31f6d7562522f8c8d81812bb8bd3e8 100644 GIT binary patch delta 254 zcmZ1&@*#wki`78SVj`;_OCJ{p)5cH%Rz~K{X{s5{wFrQcPS7T2_-+YN*z0Fc<+jRzU2)=*a*DMpj&$T&yaU`pGH+i~@{OB0@r( ztNR((tU0-Dzu>(eLft1rkJu|PV%2LU3)BEK4u`Y@W0Fsh7Y7T_8B7|?R+D2il_r~M vN(v<~W-t~&G@aosGa delta 168 zcmewmvM_{|i`78SWFo5{QxDh1Z~<0E#?2Y5K#FN|C6@p{BlG4iNnU=D1||+hAr1}( z0R{<11x6_*E(R^t$*VO~U5!+^IJsC=D)p091Q-Pvr9^~;I6Dt1=a_%JxL=U>_8PX0 zx!3F!7_B&fazI@Qj1G(*j6pzFk{g!`2NTdV1`Wo^ewsFHU;*aI9-5ky_iIXWBw2E? N8R!`rO@61@1OQr{BR&8C diff --git a/docs/user-guide/figures/naca4412_polar.fig b/docs/user-guide/figures/naca4412_polar.fig index f1c7e807e2a07c178a0040d829b1721311025020..41a78c0942eb766919331508ab5a3c5e95a60e3e 100644 GIT binary patch delta 818 zcmaE(FiV-0i`78SVj`=jh7t#}0FxFs7bhnJ7<{ltZR5!kH29Ly*hG=qtQNr;1kL4ZMmQGrp4iHkwY zd9nkyp11~s5s+gA#14#}5UUv&jI1UXaI1!6m9vr+0P9y^#47E;7{wyQ#KD*p78Jt4 zD#Xjh>F4O|s9<7ZXe7V{w3}IjWwJG&xpD$y24evb7XfXqU<~SK;$Q>2j}h)ZtH~lf zs2>@d4WE}ti delta 501 zcmaiw!AiqG5Qa0e+0A5aZ3q@(6bVWo^bpz@5WU#qMSG|c1tB;45I%+=xe0v)50Z;d zARbf@@#+)w;3K%%YCKfxT!xt+KE9cot!cm|@5bphKib?N=AiExx~TutpP#Cl?hoJa z?(!S}$`?=#%Mbe$`0yQx%{Hcvc^O+O7S&LO>HlYB1E`u()W1 z-{AE{;No*>g<-^IB&8@63I0;NZ~mib%`2J_ufe3jzl!zk%5?GV_B>cbRs~+oEdrOJ kpj@cuBAVk}DY|hlDQ{a3u;#0j2=zJW$pAVe^-mrA0%+?`sQ>@~ diff --git a/docs/user-guide/get-started/install.md b/docs/user-guide/get-started/install.md index 4b224715..4077628a 100644 --- a/docs/user-guide/get-started/install.md +++ b/docs/user-guide/get-started/install.md @@ -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 diff --git a/docs/validation/aerofoil-series/figures/karman-trefftz.fig b/docs/validation/aerofoil-series/figures/karman-trefftz.fig index ae81df8fbc415abf78419cdbec76e0e22fd2e396..bbec6c9024555a658818d17ce55dd2dac6d1b0a8 100644 GIT binary patch delta 2492 zcmds2ZA@EL7{2HBmL3i;Zd*P|apO{koL*?x4`8LVYsVO{WItS^gyAwbSOpgB2$_*h zvr0mYzZiHDvn}ys!qxd;^&YM;NxWZuR-FhE{MmE?~P z9OT}7h+K-og`eIQ2raeKx6B&?xmjf;r_UrvL%EN(6rB>t+7|2FC#7Zr-F5U>^k;!o zdF^zpV?zK%6?X=0uI9A4y=@Y><0oZ6H#5gyS zcKTG5MY?@d*QsKhF!^g9lf~&c`6Vimn$wWH@pOp%IVRG{o(&Ul(iiu#wFjoF>BW8P z0(pkm>HED*zlNT_AewC9_vv{hTRU0c=0I5NFA&$b(;Hx}4@WOh=fr600^C|$qI24d85g}vg`@hN+CsG`c= zFGU@6?%JyY*hRxjY-YD6+{J{8&2r*v3j{U@IgxV!2{?Jgx$LNPaAbUNG${=xQ^&`X z(uBy%FOYYZ>-S?W(CO?z@pEUD=d(qvAUIC{j%n|&VnOx!UjM!u%M^|_{#z`};>5w8 z%<{kyvb0>EX6j8&8DU*z@&BUasf0I+>INi^39$fq({a!**mzhua3IkS1=}WsCI+Rb zkBfu5~%b{*uh*CB=A2H=#e#2e;aa5!6{)b+~X{zU>WJQhIUJ z<3|QfdU})=0ndQ!oL?HU@YV478yFpV!|?J$I`>Y6`$gRzg54A&14)hkDWD zkh2vacJGS3v&YtjAQBJ{D@33pLF9B~F%U9PiGf4|*+ouvpfWPET$UDjAAj;C0KIBl z^}hPgxE>>JwV}7gK(z+iYM=%K$t*5w3H3g|_3|rh-%V-O46J4}qgf_10QRsEe#EGWF_CR?1~!+Yd-2I7TTEP{4|BfwCmXIX&BO<@7#`4b@9n=i*Oz;H zzQ6DH{C=PQy7Z&-{qLO;kz�yhL5sdbeKdP0c4lLEShz+x^Mx#z5L(eqKwb@BQAF ze*8@Ws~5wtzO)D4N#F~2&WH%H1xc3XPpI(J9A&?d>mq=pvah|HA`)TZv2M10ke{6* zzQyrA3BK_9Ae+jtC$^sxQ4QN@ktMj&(8402KScP!ud-;A8wodt*|Uj)h}`VqJ+g#6 z@ld(%JI9yCU+jQ-eHRsidoR`anu$xAU7KWcyUMafG`Xc}<+PjsGm7CPrT3E{&g@ zWXsd9ipbB}XZVR;$qCpwC-()|)Z2GOSR7Q@3E(-~*z51{_CG)9Tm1M#2`(Sw6fZ-_+vNIukav!`ek7tFNdZgaW2QDlq7&~N#h76<&#IZ9>T8Bp`hmYos;r!UK$-Hsgpi{#} z%7{f{UG2uVbNsb4cImT_EQEtFu@V&GL0Df2CcCk0B^Zu0>PH!#H6$tmM!7+906Ud> zLXMj;5uOv51%97{A{bxmaajc-pc|@FpicPn*PcM`8L}TsN*$>!c{(kNNF{jkR!_5K zaaCt_c3A~3S_5Zq6$5Ij1ME97t%Z)8#Q`@4D*_5p@>Z#X6W6xPRT&RHt3$55zhg3@ybNCtZq55Yy<=GZ{DR4~a^_9Dhc+GJv~?Y=6zO;Mx% z3M9pXq|<;2sU!233pa2`Le)woNXN0s!=pz>$3`Og=ST9!;jt0EUwoA)oGaYVD*gag zsx{3PvmI*<`4(EVn(0=PmfdY*S?(&z+vs+nqYCUe;G5?gkI?tswkqOMi+l7A;{ zfj3IJzpTeg*YBc1SSjgxSx=PoR=OL~zxAlrQU}U)(ccZ7Y4i=?S@a*2O?Z18{mqLeoSi_w6V4n%KMd8A=tr-aaN{)kqpK#|c^CaI zxP1owZn%FA#e%hV#BSYffr-_iM*88!B^-{z%Q^}NZ3$%nI#y5)NtO&kb`{^^aQr&T zVJHcdB+Pw=l7i)1D8ul{*C-?KF@z+03O^fx%{VUjgFcM?j z>9YP`v+U?H)}_n;Vky`NC)i;ZNhx5cwD&@3@;w?bS{a?HTH{tCp3qg?29=X0vGFKx zog=ekfk-Okp^}o)cu@5H?1PvGN=bT;HH+Y+f{^Fa? z^gtsL+RY8I?1wr$P)0)Se2>`jLwh~YsDwIsSnv(sbo)I}RzeZJPh9mw2Ru+tLS4LD zENtbuvzhNqo~AuUJJXYyp;R(u@#*r+`ODS%fP^jnrr6jT%G>a)`XOHFD`fHyO8aC= ziJQ_+X3n3#QklOzZ`EfjQ**WQ;-XdmKgF{sgLkm8p>;c*_WAX565Sckn1`~ro$~=v zed0U7p=~H>$MX;$6c7DS!UNf%iu1!Fyy@$d_CR(Nk~}49ekijIrR*y+%tyqJekkXG z?4XbGtcYy+rps?bY5R(f@gqA!RruFXjq{_8P(}TrDg`MqRMLm4HuOS{1rzaPMwn0k zTKS65UJM+uKa1f|&qst^Wy@4Cf(*YWx*Az9YK*b~Q)!P*oGPFzP8D)1=2pAhN_Q*M ztvcLludb{(ZT7o^2XrOo2BC3!33cgXLW^>d@>bv?tL!41^{nx%QF0m;+%+C_TgKdK b$gL7~HH?2N?G9$#D(6;tdp*u~Q?BP94jiqG delta 1222 zcma*nO-vI(6bJC`w%ZO%p-UI~B_B&mVM=0C3J7gzQaJblVq#;6fs24e8=)Mii6H?F za3L`$nTsBb#EXz}oq5wed1>C? zHcPS;3HNMDZMMQ-Q(R3XB_!?FZ52-M+j@2&s zHjnOx;~a(utcw_4aIawa_*1ftu~Ka#3~%6Y6HG9SW|)11(E>|PaOJ(U3R}-`!B%+s z9Ebhz>IFss_VO5QaP%4@2w&e|wClU?z}?%WWa@wr(MgC&F?j?{@QJd?ACiMN(+inf zS#35mlhdZ>(=#)f=}bC{-)A+N(-K-F+!YNeVrP(@e-{*^LH6@77>yH!FQA!{!bKs4 zOO0EcA`zyp$thxX+GT>%i*qB2h(Bhc0?Ql>_&8e(+{Ia>X7;wsl7XX~EmUHy?@P0- z2JYr;r8c%yW}AU~I1^gMzLc35xRq%OW1O8-W!}Pob!t=|AvJ8W;5FCN2DVf<8%sU55eILU@V8lhxp{8# zUS{s@oR+(tnZA=vFDz&|jh2r3zq?e++n^L5M{jUM->ZwdD_yEHu>Q0j>Sez^l{Vw7 zaF^aCA3eiHk4mf>SU=}R+QfFsTyJ3gq?&09lRlSbH&i&P-w=(qvdJ>L46OIqPXlbX z%({T11}o?6;pf~=JBsJ*8MhL_DT)qEv6xda*;>5u?6B~=&xb~cp#NVyl4j~8 Ta`Bqun`qHTYPs?D{A+&zVulV9 diff --git a/docs/validation/aerofoil-series/figures/naca-4-digit-modified.fig b/docs/validation/aerofoil-series/figures/naca-4-digit-modified.fig index 6a543d23bc6018a04d6dcd669b813d7d7f37d24f..ea8a7e27639218aaef4c02063b928005e5e5c668 100644 GIT binary patch delta 2461 zcmds&O>7%Q6vubgUVAcj+s&Fbsgu+zCr&5MNBxo5v1`X}k|uEyN0n7*8V)5UZexL+ zkPj(BRSlv@+)y-JsMJa=hy$ladn!nj8$w(vaj4)59C|~dC2eRCvpc(MuY2I$z4*QN z|LuFT^WKb~zp(9nY!{Fa8XDad`kkEily~FUA9SIX7Uut%3Gx#6Qb4(lK5-~c=Per$ zlyhlO01jn&d;=(bt+#B7tM9r)nd_jUi-~R~RsvLfZ<>mqo}ywa6D20@C8+qYM8%&c zsMyBDb|yZ_Q!#y=ifxNj>|){xCMJqh{Nj!ufdGs5Dz0r&Q2KxB6qI<)A%K1sA81&{ z?xP+-`H1{$2Vo{g6mpY($1w9o>L^ICP*QogM}ZXc(#o@Msh441wgDe!p$R4ZfC6Wk zH>F(qm3lMG%PAlJPQ5wi%^NS@u*C|WAqXT0Igx7zGVt<<^ZQW8yQSroa`nn!Y2`{u z-k|pOrd+PBly>Bt$Oo^I2M2+Rn2St$$5Hmi=j!w0ebzvZ)BOzl{UsCCp5Ju;QO0OA zi~p5L(wbS2GhUCZ5czSSMzarig9z(c?*CtvrYy-WQTrH~qlsvNCOHe1>=F;Vix zL%Y<1gq)m~Ln2Q%fycIFfGAIsU2lO^unsiFX)s}p5wL^&@Ho{-AcKf#1jH@G`@}*V zGKi=~xUid4tb}L~!y0iIA0gjc36DXHXhbWPi05xhm171G(}>sbQF7Tz95;x#MzrB} za?eV18pNnZcyR}D?^~+)3?iWsUHAmKw4ZY8J9fTMfPQj&-%FMcLNhG5%5Tt|(e_6JiM`>UAhMS}fd0y`2)~nRZh2m0S5uTRZ2ySV; z46)5iV31V1DeK)-sj&u%9h($RlP~|VKxgVun9W57XUTyTnrwhrL&xz1IrrR>X}SSo z=lCq1B41gd**X+qSKAEEHMc#lwS5lHt8K^Dwl6s$kZho5lp4Hd6cfl18VbjhJ1?A| z_BDXrbSeOwigu^}1AwlftB{i%h~CR0?_HOw2qHV;pI)9?dqabxca>31jA3 zPnxNNrZs3-H5f8X7^Rgx=!p7H6K$${4KC2SHgl5}(HrP|L){r^O0SzY>z+0fkD1mP a)0$*fFWuH@GdN3EAfdzh3iK^dKm0#{q$8^U delta 1484 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 delta 1806 zcmb`HO>7%Q6vsX5U1vOw?akV8?9JE4shdffIQBY8ld7N;sp^L!B+Q3sK}d1fz^1Vs zHK|nbaS9hiTdB%nIZzNi06l;xPXZx?L@$8E0SfJf6BQ>U#DQ`^2nk+h;&?;}Cw%#h z=l`GQeSg2%uRqW~yQUk=C>5tJ8~Kh~NBVCa`P92yra9F_@i2 zbjm7mStS8yj^Sn!URXewaPc8T7rg!$!jjIEbf)2^XBu0l9*DuuD-nZa;BFI5J)oaM zWTiJJy*3-LSSVj%lTUl={xc0{)nDFlmYUx3 zvcKee4P37}e9gJfDHY4p}lQ7 zD5*Wl9;RU{&Z1aK)-g5w)@y9TNl=41uNW5Ju<*wkdON;fIch*^JuKOE)v*D~cd?$j+e z>SR>@Atl*Ff+t0gw$S5ky)1h$c~`K4K}$WM=<>Y9Q^Cb_OHa0SSDsJv?jWCN>87H~ z^BLZwWYi@y%JQ7ZXrcO@PE(kXri|M(E1&h2SH1I2-CNwAxrR6Xzr|#U7}D*>+=;>F zy-G~oiZ0ve4+h1qRt!CgF3&rBNQogUW61Mi5ySn}FCx?yF|?KhKV ZYR}n}2~GSd_h=Ss;XTgfg!V7z{ssc7SoZ(` diff --git a/docs/validation/aerofoil-series/figures/naca-6.fig b/docs/validation/aerofoil-series/figures/naca-6.fig index 12f58986a2aa4bbcad45a050e82ddd8c2e02803f..b88d46c4c87680413390c8bdd4a72608ba3c79f0 100644 GIT binary patch delta 2995 zcmds2T})I*6uxuWy9^_g%e{chB5*}khApi8F0d{vB7!0!uKYJO3PM@s#{wowP3VI! zjfsifq}STNq&zfjt&N$a#G?2OvTYkBBt(^jnDVbWUfZE#Q~W>g;qofU@PBm8C8v-f%Hzud1w2)pwP%u#mtIhg)i4`l>)P*8_%p)8c8wBB=Lq+u+nk_Q>cD-)@dlCsGYGqLIO zA>+*4LToWMH*3sI8Z)uk*v#1(rC(wMYoEx1j^;+iOpnnR&>!4B;!ienPDxm*ysNuuhfXGGv+-@)3__DjwA6J&vZ(8N^BHoK(+A z3U!;e^HA97C*0q_{g`H$+4&p&S==Aw{w*}q{6XL7SGm8D`(L5i=96tGoN47h8a;w_ zv(<|V(sHO~_7oW~VKwxY$vdvanS_QtF+9h+P<#^)v`Cu8HW ziCMtYFPDyVY&1HIJz6HgjZs zz7-X^UM?})$A+Q1A@Gfl&N#~@1kg|#f%mEPG zNvchfgp$>?#{43M)Q}{J8Lp%CTyAHirX)$I9i)wBf5FCdyOSg#b~6o`p987LI(VYG z*7fRYcy(dgvZ){Th<=FBHr@~6lzzCKj`#9@NDll~qR*T-$DixPso41GS$MLI#h0r& zM);(6CMIf847yOFgQ9(@lYYR#YUcOcMfaOm3pc(8JxNxLXrgX<(EKZfb=biY4RVO~ znjP>+J10HrU#FLl z!Y|+veuPBbZz3psm<*DB#}W2xoz@YC?IU#B0T0-y$3};36t&Sn@(R`!NZk9sT+xuA delta 1802 zcmaKsOH5o<7{}*a?inrzC^PpCFb^0mkAX7?!@Pi&K%~_~tF?yFm`E4O;Gje2(G*CO z*mluw3`!-lxF)6#%R)^|DmjU+G@=U^CZ=G!apQwVcP6G9jS1CnzT+_0v^bmZ&i(zr zbI<+FcjoOc)!k22%4jr_T%+B!HxIYpJUo$E8{#3q`TqH__s;)yeA0oR*yMk|4^KY! zI@>twqJ)Tx6pA9QG8ZkaQbT;vbXO&+_}l}xsrbeR#VKwFq11}syCJmcF@!gKS1oP( z3=xh%8kZ{V1l{XbC@uZg?OvarprlcpI0A7RHpJOSAl?!&#KmI}ca@)9>Pujp=;Ff@ zu(%;MpNHZR+Zia$VrL#QM<6tBJoyrZr#Pl$v8|YMOjcSUnX%3geOHATd zn4@cqjm*vDv$ zl>U(Qx0i&Jc*r{ABcXvY&4p{4j!E(;Nx|yVs;+1xeViTBYIG-4MiMRsp6uYn>hP0s z4-yqg5{bfVtWBdrD3W0$DyLR7P$5nvBS@UQ*7~}<ZtWBGGuA^_Y&KJX>zv6)68|S4 zzK?*q^wTV*)$0vR``2R>x7WZMQG*|OgN?_LAK*kjY=dxVB=DZ&J7eJ zd6SJF#PvPA*`~)O^|bI-)YF>!)T!y9N3jhHXFb;UUJ__5s~Ak)ZwiL?k>E~Gu_rY{ zuVZwkJVLkwFZZDVU-~IFrFryvR*Ntq1^9WuT8251xVehu`?v9S>((A_sbV=XLEeFS zWOr*7OFJg-v{qoCB=%OZl+(q#t)Bq5;=X;AeaCR$Jv?MH=a=Ssc^{gaNc~E+HZ)fq ztX|fYpLw}_yv)Uyva=@(aKzv&;Y3vq*S$w-zljRq#I%=xQF$Jt@|`#j+a@Y#dl0S-m2tY_qq?ekGevOs?VrpEVtn*DpIh zUUR5O4TsE|YO_n?D|a)DTZ5ITqC)Vm>7cAjf4$g!13{TSuhCR8=l0Tj>Q4ozoC7DF zcWWwD+L~3mQuk9K-KJ-D4ymAu2Y@EF-R<{M9AewNv^A!wpoW?z%l7TGmmc2Cb~z@z zbaWd7fl1Ho#T_b1T=C3K?@_^8IJULx8;;1HIg+x^>6@15I)^=etPR+5T5EuQbwF}^+*`Iq!>3&i{H z=_E_v3wHh99z8~u9|tB`_I7^|;bNBMe^n{Vl3b_pa~l#%#0r#!d%=LB%WzfpK&B5W zx(hiHv8|fSZYyI)GJt$!X2Gl<1jFDA;2Ia##Dzx1PV!+PWsf5s?-Fr{E6BU0R8?_* zUuHk7Ci%rWM8O44TRI6>OC|7xL4SDmZMC7xH;#{-_*A|?yXR5b}5?oY= z{p8|OiKxLQGP!6iURUnndhTH(HVPi5$~?RU;RfcRZ2M$tkvBhdE|;Ik8{@;dqbE-t z9~(2q|0hRN+|iao^TvgtqQlJ?1j-$4v#CUjTQMf1PfC1f|09Zs;Rxc5HAoYmR=Xh#DReO`Ul@Z;yaxZluo`>O9<*E3nB+ya=&54D^;Qwx zO1;`bJ`2@bsDTyspz1_?YgyH+0H#=3hjq0q8bn8ISIg-8P*&V+lU26GO5A3lb_;b{ VC~BcD3z_UFZdA#SA{tS+{x25-!S4V7 delta 1272 zcmZ{jO-vI(7>4(!(;bFFx4SKE7h0A=lcABiErlR~q~)SP(O3-!@InzHO1%Jyl1MZp zCK{vSoQ)wI)SGb`bxiPxmtW=J~#F=6z@P%T4w6brm4s zWD6UhN82qWc1xpk8xBre_S5y@C+j~>7A5}Viht^X;`kjzH<1oxncfF%QMe-afJrmi zeV^et4G-q zq-!Twk-yiZo2U1EB$_qp^GUXcA*PyqsvH(7LiaARrZ%p~^cbTntTrZST+~`ZwbSui zthPhcKAJS?8ez3bK|AT)hm6`nby255lSSE?>kq9R~P6~o3SS72kY6NgHAczkG@?m=v`7Tp049^|+c2g%0++~UDQ9P2n# zPo=eCDh4*)R7T40{ZMDAD4r`$qps@Am4)g`)n2}^Fnj6p+|m-eWRa0D`u9~gN+`%9 z2)U;eUx_13gmI*vkm13?L>rEhwb!)>%sS4Ad}BCHJ|Ez)2a9|y++Ghb;=uzvp$?oN z58l*H+qkQqZ>(XyDctSmYfWiTHdF=b>_y=(l_SW{{+2vZ m-+n)1F$$q9!jA!4%0T4)B8sl8T2SMRf3E+qAYNACA 4-digit: default NACA 0012 in black; maximum camber m (p = 0.4, t = 0.12); maximum-camber position p (m = 0.04, t = 0.12); maximum thickness t (m = 0) +NACA 4-digit: default NACA 0012 in black; maximum camber m (p = 0.4, t = 0.12); position of maximum camber p (m = 0.04, t = 0.12); maximum thickness t (m = 0) -*NACA 4-digit: default NACA 0012 in black; maximum camber m (p = 0.4, t = 0.12); maximum-camber position p (m = 0.04, t = 0.12); maximum thickness t (m = 0).* +*NACA 4-digit: default NACA 0012 in black; maximum camber m (p = 0.4, t = 0.12); position of maximum camber p (m = 0.04, t = 0.12); maximum thickness t (m = 0).* **In the validation set:** NACA 0012 is the symmetric baseline (Rule 6's XFOIL-independent invariants: C_L = C_M = 0 at α = 0, mirror symmetry at −α) and the CI reference case; NACA 4412 is the cambered 4-digit member and a real wing section (Lednicer lists the Champion 7EC Traveler). @@ -34,9 +34,9 @@ Parameters: m, p (the 2-digit mean line), t, I (leading-edge radius index), M (p Report 492 (1934) varied the leading-edge radius and the position of maximum thickness of the 4-digit form independently, at speeds up to and beyond the compressibility burble: index I = 6 is the 4-digit radius, 0 a sharp nose; M is in tenths of chord. The aft part is a cubic in (1 − x) with a fixed trailing-edge thickness. -NACA 4-digit modified: default NACA 0012-34 in black; leading-edge radius index I (M = 0.4, t = 0.12); maximum-thickness position M (I = 3, t = 0.12); maximum thickness t (I = 3, M = 0.4) +NACA 4-digit modified: default NACA 0012-34 in black; leading-edge radius index I (M = 0.4, t = 0.12); position of maximum thickness M (I = 3, t = 0.12); maximum thickness t (I = 3, M = 0.4) -*NACA 4-digit modified: default NACA 0012-34 in black; leading-edge radius index I (M = 0.4, t = 0.12); maximum-thickness position M (I = 3, t = 0.12); maximum thickness t (I = 3, M = 0.4).* +*NACA 4-digit modified: default NACA 0012-34 in black; leading-edge radius index I (M = 0.4, t = 0.12); position of maximum thickness M (I = 3, t = 0.12); maximum thickness t (I = 3, M = 0.4).* **In the validation set:** NACA 0012-34 has a sharper nose (I = 3) and its maximum thickness further aft than the 0012, with the same t: the pair separates the effect of leading-edge radius on transition and stagnation-point behaviour from that of thickness. @@ -48,9 +48,9 @@ Parameters: cl (design lift coefficient), p (position of maximum camber), reflex Report 537 (1935) put the maximum camber unusually far forward — a cubic mean line joined to a straight (or, in the reflexed lines, a second cubic) aft part — to raise the maximum lift with little pitching moment. The 230 line (cl = 0.3, p = 0.15) is by far the most used. -NACA 5-digit: default NACA 23018 in black; design lift coefficient cl (p = 0.15, t = 0.18); maximum-camber position p (cl = 0.3, t = 0.18); reflex mean line, position p (cl = 0.3, t = 0.18); maximum thickness t (230 mean line) +NACA 5-digit: default NACA 23018 in black; design lift coefficient cl (p = 0.15, t = 0.18); position of maximum camber p (cl = 0.3, t = 0.18); reflex mean line, position p (cl = 0.3, t = 0.18); maximum thickness t (230 mean line) -*NACA 5-digit: default NACA 23018 in black; design lift coefficient cl (p = 0.15, t = 0.18); maximum-camber position p (cl = 0.3, t = 0.18); reflex mean line, position p (cl = 0.3, t = 0.18); maximum thickness t (230 mean line).* +*NACA 5-digit: default NACA 23018 in black; design lift coefficient cl (p = 0.15, t = 0.18); position of maximum camber p (cl = 0.3, t = 0.18); reflex mean line, position p (cl = 0.3, t = 0.18); maximum thickness t (230 mean line).* **In the validation set:** NACA 23018 is a real aircraft section (Lednicer lists the Beech 90 King Air root; the 23012 is its tip) and, with the camber concentrated at the nose, loads the leading edge heavily: transition and separation are exercised at the nose rather than mid-chord. @@ -76,9 +76,9 @@ Parameters: family (63…67: position of minimum pressure), cl (design lift coef The laminar-flow sections of Report 824 (1945): thickness forms derived by conformal mapping for a prescribed pressure distribution — the family digit is the position of minimum pressure in tenths of chord — on the a mean line, which carries uniform load to x = a and falls linearly to zero at the trailing edge. There is no closed form: the sections are defined by the tabulated ε and ψ mapping functions of TM-4741. -NACA 6-series: default NACA 63-415 in black; family: minimum-pressure position (cl = 0.4, t = 0.15); design lift coefficient cl (63, t = 0.15); extent of uniform loading a (63-415); maximum thickness t (63, cl = 0.4) +NACA 6-series: default NACA 63-415 in black; family: position of minimum pressure (cl = 0.4, t = 0.15); design lift coefficient cl (63, t = 0.15); extent of uniform loading a (63-415); maximum thickness t (63, cl = 0.4) -*NACA 6-series: default NACA 63-415 in black; family: minimum-pressure position (cl = 0.4, t = 0.15); design lift coefficient cl (63, t = 0.15); extent of uniform loading a (63-415); maximum thickness t (63, cl = 0.4).* +*NACA 6-series: default NACA 63-415 in black; family: position of minimum pressure (cl = 0.4, t = 0.15); design lift coefficient cl (63, t = 0.15); extent of uniform loading a (63-415); maximum thickness t (63, cl = 0.4).* **In the validation set:** NACA 63-415 was the standard wind-turbine section before dedicated families appeared; the Risø catalogue (Bertagnolio et al. 2001) compares measurements with both Navier–Stokes and XFOIL results for it. Its trailing edge closes, so it runs XFOIL's sharp-TE branch, and the 6-series is the family whose generator is *not* a formula. diff --git a/docs/validation/aerofoil-series/tables/run.md b/docs/validation/aerofoil-series/tables/run.md index a7443e0d..f5d3f032 100644 --- a/docs/validation/aerofoil-series/tables/run.md +++ b/docs/validation/aerofoil-series/tables/run.md @@ -1 +1 @@ -Results from the run of `cargo run --release -p aerofoil-series` at 2026-10-17T11-24-21Z (git untagged, dirty). +Results from the run of `cargo run --release -p aerofoil-series` at 2026-10-26T15-03-00Z (git untagged, dirty). diff --git a/docs/validation/branch-coverage/figures/branch-map.fig b/docs/validation/branch-coverage/figures/branch-map.fig new file mode 100644 index 0000000000000000000000000000000000000000..a4b2f35ebe24f388decf8dd9f8731956ed9a8f41 GIT binary patch literal 58842 zcmeHQTW=f372YKyfto}%wYCs}vDyU@x2{?XlAPw^#Nt&K#-v4?l&k=K$WmTeR2fnr zSB{mSsL_W4E&9;cq%F`OKOk+<0R9QdTY&=UYx)OLKe{hzcR4fSdPrh7yl*7uDRR7$I&eU!8D0)R+roM3W`o(EUdK8(pnt7|aY&G%OfG;R7JWr-r@{`Dd zPm$jK{bPT?ul)A0pZKC><+SzNm4tVwe#~lZh4$&yWJ1sDWbr^n>cX2bZfb;y3G^g2Q`m4Z@Yf4>+w8p`TXtr z+&}6Tx~G*M6b_s>gR-1oPV+g%@$o^O6Y~s~H^8$uJe_oL*5jyaZ*>K3?!-;h6YAv7 zI-xG!sEepK^!q~qy6}cBhE+%YeNv|^8`eHBFL-Jp;B_8wojr;0Cu+t1v!jiks8_M? zb^LHX$_3!i84eBQ^!5aOb%rze2%LITSbtgI9e8IUFs?jsQqQ>Jf^*|u7{s1YCfNg+RYNpeQ!le&<6z#S%CG2MYm_2NV_eww?Z7f}cPn`7s|wpxw`P+xeYvbE=!uxYPWjI^ONQ)a^OP71wTY z@_Kwb-L~^P;n8cJ)4X0A?^fT4rwjlNlaTL`$7 z2TmGt>%Ors=*rEyYR69}z4beOR$s^UyYF7}+u;m9MReS8=#^JIZ`|?2`Jp2VflhhA zbu%p6={^ugD*zn4jl-~X*6Y0Bo45Hk?)7!j#kk{x^F#j@0)yg#lis==AJo1u4@Dk0 zcnb$nPpGfA>Wg~1tDexCyXpabc)K3Zo44x={kp5ZqTZbPsGqy)hkBqM76RY_cmN(i z2e=1t51i#aK+$isZu|Fu#Qi`o(92osrCuA&!b#iB&pf7jU1$yyjU7YmhG!9;PlNYG#aOx`R2{;c2XW$B4Ed;;=@Blo3 z4sZ|P9`NElApRcAsZUuTNn#xLix#?}hyaIi$6-#s^X0jKPYDF1pfo3|a>z%dh52CJ zs?rr{#q3`<|3sw}SJBF+)2HL|%2)o-KmFt%em+X=>Hqi^rG2i$LXI4tHKC@DW z^_i7!vAiwj--=%U+f?YYDKblDerD(ojl%wi50A96URvD$MNlRe$oab4Fg}cB6R~u{ zaw_B;ci!SYTVOVqN~dlK+1cFPX6MF6i}c%fTRD?0oV)Mbjon-m7Ru$1vEBdncF+U(z$$QYe!7pUK5fn*b5AE+ahLE zJS|LUo+bBAPH<-KBsS?5ZH2I$$!#HWH?_{zwnc7dld*h#lXe~yO0|<3<$-Nav0avb zkpwvn^5#lwH&?8sY>}rMwfXXEZ1Xl>JiQaU)0CS@)vIWW%il?+)|=v*eaToRE238S z)i)RO8Ggq%WKfYDE%8_1e5=<_;W)e(*-*P5I=T0ieBJJ?k zDy0W{6Z{*o?VMQs^ma0l$qFmQV>ff#anYI1ZKdlJwQqmFQe@nlA=4ZjlRe@-6I+>P z?j%m{1skCf=@!~bv=VXN0;ztj*w21!6ibYHwpq=^*LJOQM&;AD)b;t(msH~=))a|-Hc`&geO)Omlu~0XGow> z)yjJZdQF*7{DCh5hjV0dzj*(+sw?NhFAze9ufC@p9bO_6HRJf+iE<%4L(a2-y~+c< zs@>NQFOcVJ$AxOe1gmV$_MGAiT&t1?TB&$XGm4e+2P=U|^NlalH~yR)R>}9srGTsi zh!yKMfjg&|3zEsub7s^;P+Ic+O8&?fkVC)og@5BCzxI)9vwO^#FvRm6D#G?iCQeHM<$|`QssVePb;Z= zT1~H+%l^pB{=>(i$gI`OTMf-NNnNfOYE{?v4zxl^H$yL*q5t}7Co=n|KVDj$H_yh7 YHBF|h(E4IQS(zkey@OKNw2Eo}4^#(e6951J literal 0 HcmV?d00001 diff --git a/docs/validation/branch-coverage/figures/branch-map.svg b/docs/validation/branch-coverage/figures/branch-map.svg deleted file mode 100644 index 70302b22..00000000 --- a/docs/validation/branch-coverage/figures/branch-map.svg +++ /dev/null @@ -1,9041 +0,0 @@ - - - - - - - - 2026-10-23T12:39:03.789334 - image/svg+xml - - - Matplotlib v3.11.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - xpanel.f - - - APCALC - - - NCALC - - - PSILIN - - - PSWLIN - - - GGCALC - - - QWCALC - - - QDCALC - - - XYWAKE - - - STFIND - - - IBLPAN - - - XICALC - - - UICALC - - - QVFUE - - - QISET - - - GAMQV - - - STMOVE - - - UESET - - - xbl.f - - - SETBL - - - IBLSYS - - - MRCHUE - - - MRCHDU - - - XIFSET - - - UPDATE - - - xblsys.f - - - AXSET - - - TRCHEK2 - - - BLSYS - - - TESYS - - - BLVAR - - - BLMID - - - TRDIF - - - BLDIF - - - DAMPL - - - DAMPL2 - - - DIL - - - HSL - - - CFL - - - HST - - - xoper.f - - - SPECAL - - - SPECCL - - - VISCAL - - - xsolve.f - - - GAUSS - - - LUDCMP - - - BAKSUB - - - BLSOLV - - - xfoil.f - - - MRCL - - - COMSET - - - CPCALC - - - CLCALC - - - CDCALC - - - TECALC - - - xutils.f - - - SETEXP - - - - - - - - - - - - - - - - - - - - taken by the finite set - - - taken by the non-finite set - - - taken only in pathological cases - - - not reachable - - - not taken - - - - - - - - - - - - diff --git a/docs/validation/branch-coverage/figures/case-matrix.fig b/docs/validation/branch-coverage/figures/case-matrix.fig new file mode 100644 index 0000000000000000000000000000000000000000..ada9cf033026cdfa2313c0a69c0b55956cc8b868 GIT binary patch literal 8677 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 evfWP-Ov<@HPHDf<<0bzEd#a6xf? literal 0 HcmV?d00001 diff --git a/docs/validation/branch-coverage/figures/case-matrix.svg b/docs/validation/branch-coverage/figures/case-matrix.svg deleted file mode 100644 index 333a47b6..00000000 --- a/docs/validation/branch-coverage/figures/case-matrix.svg +++ /dev/null @@ -1,5464 +0,0 @@ - - - - - - - - 2026-10-23T12:39:04.425386 - image/svg+xml - - - Matplotlib v3.11.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - kt_n60_inviscid_a4_cl05_cl8 - - - - - - naca0012_n60_a2_re1e6_type3 - - - - - - naca0012_n60_a8_re1e4_damp - - - - - - naca23012_n60_a4_re1e6_xtr022_0001 - - - - - - naca4412_n160_polar_down16_re1e6_iter100 - - - - - - naca4412_n60_a18_re3e6_iter40 - - - - - - naca4412_n60_a20_re1e6_iter50 - - - - - - naca4412_n60_cl1_clm05_re1e6 - - - - - - naca64a010_n60_inviscid_type2_m03_am23_a0_a19 - - - - - - naca64a010_n60_type2_a0_re1e6 - - - - - - - - APCALC - - - - - - NCALC - - - - - - PSILIN - - - - - - PSWLIN - - - - - - GGCALC - - - - - - QWCALC - - - - - - QDCALC - - - - - - XYWAKE - - - - - - STFIND - - - - - - IBLPAN - - - - - - XICALC - - - - - - UICALC - - - - - - QVFUE - - - - - - QISET - - - - - - GAMQV - - - - - - STMOVE - - - - - - UESET - - - - - - SETBL - - - - - - IBLSYS - - - - - - MRCHUE - - - - - - MRCHDU - - - - - - XIFSET - - - - - - UPDATE - - - - - - AXSET - - - - - - TRCHEK2 - - - - - - BLSYS - - - - - - TESYS - - - - - - BLVAR - - - - - - BLMID - - - - - - TRDIF - - - - - - BLDIF - - - - - - DAMPL - - - - - - DAMPL2 - - - - - - DIL - - - - - - HSL - - - - - - CFL - - - - - - HST - - - - - - SPECAL - - - - - - SPECCL - - - - - - VISCAL - - - - - - GAUSS - - - - - - LUDCMP - - - - - - BAKSUB - - - - - - BLSOLV - - - - - - MRCL - - - - - - COMSET - - - - - - CPCALC - - - - - - CLCALC - - - - - - CDCALC - - - - - - TECALC - - - - - - SETEXP - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - branches - - - reachable - - - taken, all cases - - - 6 - - - 5 - - - 5 - - - 12 - - - 7 - - - 7 - - - 68 - - - 36 - - - 36 - - - 20 - - - 17 - - - 17 - - - 20 - - - 20 - - - 20 - - - 2 - - - 2 - - - 2 - - - 42 - - - 42 - - - 42 - - - 6 - - - 5 - - - 5 - - - 12 - - - 10 - - - 10 - - - 10 - - - 9 - - - 9 - - - 14 - - - 14 - - - 14 - - - 4 - - - 4 - - - 4 - - - 4 - - - 4 - - - 4 - - - 2 - - - 2 - - - 2 - - - 2 - - - 2 - - - 2 - - - 26 - - - 26 - - - 26 - - - 8 - - - 8 - - - 8 - - - 82 - - - 81 - - - 81 - - - 6 - - - 5 - - - 5 - - - 104 - - - 101 - - - 101 - - - 110 - - - 109 - - - 109 - - - 8 - - - 8 - - - 8 - - - 86 - - - 86 - - - 86 - - - 6 - - - 6 - - - 6 - - - 44 - - - 44 - - - 44 - - - 26 - - - 26 - - - 26 - - - 4 - - - 4 - - - 4 - - - 42 - - - 42 - - - 42 - - - 8 - - - 8 - - - 8 - - - 12 - - - 12 - - - 12 - - - 18 - - - 18 - - - 18 - - - 4 - - - 4 - - - 4 - - - 10 - - - 10 - - - 10 - - - 2 - - - 2 - - - 2 - - - 2 - - - 2 - - - 2 - - - 2 - - - 2 - - - 2 - - - 6 - - - 6 - - - 6 - - - 22 - - - 20 - - - 20 - - - 16 - - - 14 - - - 14 - - - 46 - - - 41 - - - 41 - - - 24 - - - 24 - - - 24 - - - 26 - - - 25 - - - 25 - - - 14 - - - 14 - - - 14 - - - 52 - - - 52 - - - 52 - - - 22 - - - 16 - - - 16 - - - 2 - - - 2 - - - 2 - - - 6 - - - 6 - - - 6 - - - 4 - - - 4 - - - 4 - - - 8 - - - 8 - - - 8 - - - 2 - - - 2 - - - 2 - - - 12 - - - 8 - - - 8 - - - - - - - - - - - - - - - - - - 0 - - - - - - - - - - 0.25 - - - - - - - - - - 0.5 - - - - - - - - - - 0.75 - - - - - - - - - - 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - subroutine not entered by the case - - - takes a branch no other finite candidate takes - - - - fraction of the subroutine's branches the case took - - - - - - - - - - - - - - diff --git a/docs/validation/branch-coverage/index.md b/docs/validation/branch-coverage/index.md index 8112bbc9..56b524d1 100644 --- a/docs/validation/branch-coverage/index.md +++ b/docs/validation/branch-coverage/index.md @@ -17,7 +17,7 @@ CLAUDE.md Rule 6: "numerically exact" is claimable only over branches that have --8<-- "validation/branch-coverage/tables/status.md" -![Branch map](figures/branch-map.svg) +Every branch of the analysis path, one row per subroutine grouped by source file, coloured by status *Every branch of the analysis path, one row per subroutine in XFOIL's file order: taken by the finite set (green), taken by the non-finite set (blue), taken only in pathological cases (orange), not reachable — annotated unreachable or a never-taken DO-line edge (grey), not taken (red).* diff --git a/docs/validation/branch-coverage/tables/subroutine-counts.md b/docs/validation/branch-coverage/tables/subroutine-counts.md new file mode 100644 index 00000000..b321c763 --- /dev/null +++ b/docs/validation/branch-coverage/tables/subroutine-counts.md @@ -0,0 +1,53 @@ +| file | subroutine | branches | reachable | taken, all cases | +|---|---|---:|---:|---:| +| `xpanel.f` | `APCALC` | 6 | 5 | 5 | +| `xpanel.f` | `NCALC` | 12 | 7 | 7 | +| `xpanel.f` | `PSILIN` | 68 | 36 | 36 | +| `xpanel.f` | `PSWLIN` | 20 | 17 | 17 | +| `xpanel.f` | `GGCALC` | 20 | 20 | 20 | +| `xpanel.f` | `QWCALC` | 2 | 2 | 2 | +| `xpanel.f` | `QDCALC` | 42 | 42 | 42 | +| `xpanel.f` | `XYWAKE` | 6 | 5 | 5 | +| `xpanel.f` | `STFIND` | 12 | 10 | 10 | +| `xpanel.f` | `IBLPAN` | 10 | 9 | 9 | +| `xpanel.f` | `XICALC` | 14 | 14 | 14 | +| `xpanel.f` | `UICALC` | 4 | 4 | 4 | +| `xpanel.f` | `QVFUE` | 4 | 4 | 4 | +| `xpanel.f` | `QISET` | 2 | 2 | 2 | +| `xpanel.f` | `GAMQV` | 2 | 2 | 2 | +| `xpanel.f` | `STMOVE` | 26 | 26 | 26 | +| `xpanel.f` | `UESET` | 8 | 8 | 8 | +| `xbl.f` | `SETBL` | 82 | 81 | 81 | +| `xbl.f` | `IBLSYS` | 6 | 5 | 5 | +| `xbl.f` | `MRCHUE` | 104 | 101 | 101 | +| `xbl.f` | `MRCHDU` | 110 | 109 | 109 | +| `xbl.f` | `XIFSET` | 8 | 8 | 8 | +| `xbl.f` | `UPDATE` | 86 | 86 | 86 | +| `xblsys.f` | `AXSET` | 6 | 6 | 6 | +| `xblsys.f` | `TRCHEK2` | 44 | 44 | 44 | +| `xblsys.f` | `BLSYS` | 26 | 26 | 26 | +| `xblsys.f` | `TESYS` | 4 | 4 | 4 | +| `xblsys.f` | `BLVAR` | 42 | 42 | 42 | +| `xblsys.f` | `BLMID` | 8 | 8 | 8 | +| `xblsys.f` | `TRDIF` | 12 | 12 | 12 | +| `xblsys.f` | `BLDIF` | 18 | 18 | 18 | +| `xblsys.f` | `DAMPL` | 4 | 4 | 4 | +| `xblsys.f` | `DAMPL2` | 10 | 10 | 10 | +| `xblsys.f` | `DIL` | 2 | 2 | 2 | +| `xblsys.f` | `HSL` | 2 | 2 | 2 | +| `xblsys.f` | `CFL` | 2 | 2 | 2 | +| `xblsys.f` | `HST` | 6 | 6 | 6 | +| `xoper.f` | `SPECAL` | 22 | 20 | 20 | +| `xoper.f` | `SPECCL` | 16 | 14 | 14 | +| `xoper.f` | `VISCAL` | 46 | 41 | 41 | +| `xsolve.f` | `GAUSS` | 24 | 24 | 24 | +| `xsolve.f` | `LUDCMP` | 26 | 25 | 25 | +| `xsolve.f` | `BAKSUB` | 14 | 14 | 14 | +| `xsolve.f` | `BLSOLV` | 52 | 52 | 52 | +| `xfoil.f` | `MRCL` | 22 | 16 | 16 | +| `xfoil.f` | `COMSET` | 2 | 2 | 2 | +| `xfoil.f` | `CPCALC` | 6 | 6 | 6 | +| `xfoil.f` | `CLCALC` | 4 | 4 | 4 | +| `xfoil.f` | `CDCALC` | 8 | 8 | 8 | +| `xfoil.f` | `TECALC` | 2 | 2 | 2 | +| `xutils.f` | `SETEXP` | 12 | 8 | 8 | diff --git a/docs/validation/branch-coverage/the-minimal-finite-set.md b/docs/validation/branch-coverage/the-minimal-finite-set.md index 46eb6d31..3a337420 100644 --- a/docs/validation/branch-coverage/the-minimal-finite-set.md +++ b/docs/validation/branch-coverage/the-minimal-finite-set.md @@ -10,9 +10,15 @@ Branches taken by exactly one finite candidate make that candidate essential; th Every case: yFoil generates the panels (`--method cosine`), XFOIL `LOAD`s them (bitwise handoff asserted), one OPER script, one instrumented run plus its five 1-ULP twins. -![Case × subroutine](figures/case-matrix.svg) +Fraction of each subroutine's branches taken by each case of the minimal finite set -*Fraction of each subroutine's branches taken by each case of the set; a dot marks a subroutine in which the case takes a branch no other finite candidate takes. The three columns on the right count, per subroutine, every branch, the reachable ones (not annotated unreachable and not a never-taken DO-line edge) and those taken on any measured run, the non-finite set included.* +*Fraction of each subroutine's branches taken by each case of the set; a light grey cell is a subroutine the case never entered, and a dot marks a subroutine in which the case takes a branch no other finite candidate takes.* + +??? note "Branches of each subroutine" + + Per subroutine, every branch, the reachable ones (not annotated unreachable and not a never-taken DO-line edge) and those taken on any measured run, the non-finite set included. + + --8<-- "validation/branch-coverage/tables/subroutine-counts.md" ## Whole-run comparison diff --git a/docs/validation/known-issues/figures/polars-naca0012_n240_polar30_re1e6_iter200.fig b/docs/validation/known-issues/figures/polars-naca0012_n240_polar30_re1e6_iter200.fig index d9281ce012093eea1ada02c6ed3de29256d3e8f3..5d7163442e8c11e835c98e1a231cbaded75f385a 100644 GIT binary patch delta 6779 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 delta 5560 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 RXi_PR7`;qH7WZ{M{0AM&w@?58 diff --git a/docs/validation/known-issues/figures/polars-naca16-212_n160_polar30_re1e6_m07_iter100.fig b/docs/validation/known-issues/figures/polars-naca16-212_n160_polar30_re1e6_m07_iter100.fig index aec345e23d0b8bb8e2f1bd634008e513fec99561..ddf76c3d4621b6462ace96a836ce9ebe4bffad33 100644 GIT binary patch delta 7956 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 ljGo~qVMRr_pLcr}=gj*)zwhrk=e*~c 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 Gfd2q_ydmlU diff --git a/docs/validation/known-issues/figures/polars-naca16-212_n240_polar30_re1e6_m07_iter200.fig b/docs/validation/known-issues/figures/polars-naca16-212_n240_polar30_re1e6_m07_iter200.fig index 1dd964a5eb619bf66254415731050c7805fb5923..7bb5d56494b9dc9cea80442b5795291665026e9c 100644 GIT binary patch delta 8517 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 A;Q#;t delta 7134 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 NXBUhFe%f_`e*+STrfC2G diff --git a/docs/validation/known-issues/figures/polars-naca16-212_n60_polar30_re1e6_m07.fig b/docs/validation/known-issues/figures/polars-naca16-212_n60_polar30_re1e6_m07.fig index ac61d0ccaf6d950fc2dea0e5454bea62aedba6e8..053b6318e09f71ccff746404e71a0cfff810d992 100644 GIT binary patch delta 8200 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 delta 6900 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 delta 5558 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 I!C{2|0nz+>J^%m! diff --git a/docs/validation/known-issues/figures/polars-naca4412_n160_inviscid_polar30_m07.fig b/docs/validation/known-issues/figures/polars-naca4412_n160_inviscid_polar30_m07.fig index 0d1cc3393e04fa9e0254ac8b944fe04c41a5d52f..559e44c9db336771aa96665ea50ae50ebf7fe6c2 100644 GIT binary patch delta 2677 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 OFi8di*f&;`(*FbOjMT#b diff --git a/docs/validation/known-issues/figures/polars-naca4412_n240_inviscid_polar30_m07.fig b/docs/validation/known-issues/figures/polars-naca4412_n240_inviscid_polar30_m07.fig index f401156299d4fe62ba239f0dd8e4d8ffe09bee14..451fac9f8c978d9a61637d5507a2e6382b1349b1 100644 GIT binary patch delta 2677 zcmb_eTTdHD6yCFI!!QtQnFMfLAZtR&K!6xy2sQ?DNgxnzHP;Zg=H?0;2fMb_Ql+TM zPiW>TQq(G{`cg$|rG4v5rAmG6Q>98(+o(@PN>d?KZQ7TrvpWQ>T_ELpqfJI8zG z`@S=?K6ngY9Kr;)dpi3%J*wmy#DSUN(P7Q!>+%M)NG`UQ%jItGu5YdzNF=)J^?4%E z`0m^B>}Gr`5>2O)>zQs2-SS-CSK)(T??@2YRk^JaS5!#m=P*=iAQNfw4+SZ@w)Paj zroV^(R`gHg-vBBEg!TI}&zusGLr*$*q>89oM5je`MnrWYa_SFzez8M?-mE=Rpi$3- z_{A-9P5KY5PeK1ZjAZ?B>jA(8fm0KQiy~^*gDzfm=jIc|_G3Bf5>N7qC-sP$Ub6=J zMC8}o8~N(`MP*P_hD2^i|8?O{fRVe)HRv$pgvnRX=-uUdxM~5$Sb!tfIMQ-dGC}Gv z$V7c6>$5BWJy-3z`Di>v4iyXohE?R&kv+s(f?~NDs zy(#)e^83~~A^f2Y$pj%um7Jh~OU6>$DATq&lT`BZAi%#vFd7&t?5r!{QWR=qpKk>l)?gdbu(2FoM2PTi?~bM7Nfr3= z%4j)zkO(@CDn5hoLqL^mJmyy_C}!@R?eikF4H>@fJ(FFP$gH0PZec&Art^of z)imq^rNs>USwU@_pIoA0e)i4Ixp6+nC$PghOxG^b_CgLGMlyM>b#vFcsJC?2I4hsY zsx;U9*oAAG_oBCRnf*(f&Fxw;^-e6KW#e116Lj!0sbSp-r_w`v3-?_hf8X^{zp?ME zgFC<_=%>NbC3s{*gIofoL}R1u)3;jLM=AGw(ZMctHyQ`za6Xw39kP-c2=}8aoXiLv zW$APWS(D9^)qux2e3gzB!jl}nMklPWv6ZJee4S3clp-^xA_YV7oU_SLWb*&3@mjtb zvvjUdjRj65Oy{lq3^kTHyhxV{;Z+W=&>L3RP~&wD-=vY}tKrK(*jB3$5F}UCYHhy3 znbk7>!j;>6;yO69;m2j3&aPhl%dA6QdICl*vYDeKJMB&11O5*1w}2#qu=&d5@71q? zdjF*%m^Ip3A(s GNB$Suzwp)o literal 30184 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 Ap#T5? diff --git a/docs/validation/known-issues/figures/polars-naca4412_n240_polar30_re1e6_iter200.fig b/docs/validation/known-issues/figures/polars-naca4412_n240_polar30_re1e6_iter200.fig index dec3e56f81c3985a9cf7005b532233879ec8a9e0..9931840fd394354c4572f7f2d040a92809138e26 100644 GIT binary patch delta 7649 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 a?OD*&&Du|~D>R{~nbm5zk+FIef&K?wKb{r< delta 6171 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 S;5Um9O00%BBJ!6fGyD(PE@f)~ diff --git a/docs/validation/known-issues/figures/polars-naca4412_n240_polar30_re1e6_m06_iter200.fig b/docs/validation/known-issues/figures/polars-naca4412_n240_polar30_re1e6_m06_iter200.fig index f30dd9a421143278ac588664084fa6bc2a30153c..5a64e4dd36f10792267931647f580fd54812cc5a 100644 GIT binary patch delta 7116 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 G75xHY5W+VA delta 5997 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 diff --git a/docs/validation/known-issues/figures/polars-naca4412_n60_inviscid_polar30_m07.fig b/docs/validation/known-issues/figures/polars-naca4412_n60_inviscid_polar30_m07.fig index f85e7fbe7b20b5408340cad5a0804f56455c4f58..7955c842b8e1743275480cc65a38124a9182decf 100644 GIT binary patch delta 2627 zcmb`JO-vg{6vy}N+GadB#NGsOTuf|8lQ9HpJBDD4G2n#o4K<$uSH=)mSQt!ft4h^Y z&Alfyy;QAIMLkuGl*1l!>!DJQy;Z8zDvENbiW;RtmD=>y&d#7{?V8F_v1EDQe;)Jp z_x^8o`S}?PZf0*KQ+xSbx^RU_JN~O5*kM1?J2HUGcDbV-TdflR9L8!5WL28@LqUqRzx)Eg zqt5%G$AFo(z(|+=?TE8cdto`ac0JtP{!gc5r(Pb^-Vp(_Bo-3xubtoWq3W}Y2 zL{2Zyfj$w1wa!*{KM|2RATncOZAg2z@F&0s{d=hq9mbq6@fwP=parfQrBNDQZhEuJf$8g7^3QdqP-tGfI7Ah>*1`ZF(+yyjDJ1PYb+Y2C0=92DBa{W)3Ai zP%S646UV;^8#&<_a`rgFzZBHO2%*x$=BmyF=jiFJ$Vk<-S8?qeXKE%+oylf!t%csr zO{+)3Mq(YEXN`B0R_fUy4j;k}F-`U%m+X?-XsUQlw@luXURw5r33)hBg=HUBz3E^V zZooKO$`sP2Y<{HQ9#Y@^Mi7(Q}`RVep^l{TJ5ptYq zPZiX`808vG3)Anm&);AZeFEpLi#yjzE?08+siRQl+!f|rfCOuIj!}g;RfTiMjAEGH z-#IC&tDLHb^j4m+{_-={N5c9UGY)8!VMfS6?U+wYXpmtlHJX>AU*7i7dwK7C)d8;c z)aj4J;c}Q588X5|6A^Tc!HkeN&F^)o>oR|q>hcXHUnirL@&uE|$hc9~kL)y)C&|<+ zb!3Kjq&DOAj!gXjZoFCEjaf2R*^LDTk|6U&KD`^uOkN~QmGUZ+SIAAHtasxrCa;m? z@!bfOKfqRP2vj87nmiq$!I@PHd&SBfA@Msnvk}G?e|Ml)`?_e8m!5;3i%fi!WT(09 zN5DS-{vME|BFtZs>`nSDsNVo)F_ziCWzx|VdU;jPuI>Z=4Af5m5926(y4PeXWjD8J Yp!m93UBC%Uy*n3Gwd(Vi75B^k0Qm3vivR!s literal 30181 zcmeI5Yj_h?8pm_#WiqrSo!-*)Hl!^b3JvLvN`cM*Qsh>ou&70-X*;DUX;PDv7Vn_k zilVL{%6h@Y8}1{*!lLVnlLa0hbt_O^gq0NvRe^R3g$fiw+{v7`gBh(r;EU+ zrL}ge*^zB3tuf_kRR%(%5Vi^?ywbCk*cnQhiH;eV{K!y+;t@K^vrF*oQa!sdMl4XP z(o^WHPP#WuKXX^!bB1H|#4a__sm7I~h~sK>Zh;%=DA19hBS7;(hlAb%ng==zG#4}n zG#fMvG!xVWY6Kkunhr{V4g^gBHGq(2C4w<>N?%?8Z@%>^9>ng@Cd=y1?{&=H^`K}UfWFgoSL zvuaE!`W|LF^7WyZi`*BVpDKLeqYiGEqVnU=W9au9osgGDW}_>`20t;JL1;lOU$G*+z&Ci z133FGnrwG0ds!Xn{_BHpK6BF2#m%gY_;2@~f6?2XYg|-xd#Zc+!S) zjef*2q}f&2N$0n(yyL}{U36L9D=)op`5e6ypWNQP;RpKPhncdOZ(pWY%^DxM`UNFZ zR`tlT=k5$(T8lbgniY>RcTN8>qWqs)=KPdDpLTwzVYa@qY{#5I!{Ee=-H=JKh-h0b>*FR=ft~jA04#dNK!xMfx&H;8&{Fco;8Qm zU#(Aa-;=v*`Lep7=dA~Yy-6bxz`Ao}cQxKzj|K5$Q znt0~emfCZ-9cGxJV@ElLf1Kp5Ze6lAde<55ivuMYV@`L_KQ5Ux13#n(-&LaT3SpCZ zM{)3X-mlE_1n}MjyubSsEASrMhq@ctC={x9IY%ZPY?3V1gF z@3#T(UuY5U^}_tn6A9q^vG7V+K)yzd9zw*c>N0q+-p_uasIBk-OI zye|aa^RAJ1Gw^-{c%K8jp8(#cyo`8n2i}{3_gvt81@L~h9`Qa7cz*_XPk0vbzNHu5 zn}GKVdlBzZ65g|ccRTR@H1OW(#e0Cvy8w7!Ti*jk-(}vfZGHC--h)Kn<@H^N0E6P- z@9TT8==-F1!ZNsh7wNMPHB`5(IY%$k)XY1z;S4>#-}A4X*!Mk6k9mDp5t-;-yQeFo zst{ue+GEz%Py2yhoIbnd-nY8wfpHu26wjQb|2k!D#vj(T(xWR}vq#?F%~icoez1P0 zih0NM#0QTRD%?)n@aa4E9;Po1erkDGa|@lfu(+F^8SloYBo3Op`UGwJW-UacpE59oAWnw;U}xG z!#h9Vb_MKo@BHKw?xQX7i$8z+E3UZYz$U}kZZ55DYxBC7JLux`pC0_%V`=UWpWHEc zOvzEYXn$elgk-gQ(vHZbou6Hzzj*Gzy{c^;bkbkyLk5;$O!8F4iFapQpo#X`x9*QP zM^B4+|DB;_7wOQ0PggHJa)y31qW|VO4xHKe!|Wx?E~hbnH1~fuPIr;+%DFV}zi+6R zXPHMAJjQ95v(Bi-yJjRa%%)GaRlRqJ8@l!4p)b#!rrREzxgp}cPI~FwZHg&jQIOAL zqVF>AeTjFC=z9UV zE=Rlv0`E@XJq>tYcNFpNl<>arIO2U4@a_cOCj##WfOj46z6N+V0`J3t_hZ021H5ks z-v17~-wnKn0q<_$ode#jz^>`vKs6DeyiOc#nS`@qP|?9}K*Q0PnfL`}wba zo%d&e_YUBFwS@PX!24j}eFyMf@5MVV`Y!X{mw4|d`Y!X1*7v@_dx+?}%zIzrJyha* z_ru74S^YH*=yRDciSP3J^T=<1zkly9@m=QJUpepT7tj0G!I+jf5R5DDBlZTP<1+95 z&b#ch5CO*DeMWqTi+sO5bLoAz79OW%mE$0fgWo>~C|@Ted7Hd`_knqx|NFTJiSMY_ z^4@3D5*KftFVPebz5kmX1Z6Qq~>6+!k%w^>-5y*N9hpBDD9~%f7>mJ zu)luWZHDN(ZN2gX{=4yLS^YZ>h+&p`sW|mi>q-M5Pp(Gp0@hX(m%=IdiW+THSqqL{8oQ)uN{52cN6MdJn z5dY3Xdi>QXi@#Q5Dgwh4T7JSIm3kGfNH^jH9+q;q*;-p=P9cYq73^HCQmfQQ5Agz*rO$thxpNi{|CclA zs25*|FGzT09a$heS|t3lCeQXiTBjNlfnnimpZ9DUMz7K<4E#mPrr}eu0D^yo3kvNP ztI(v1Qj1Mq=ROT3oEd?6TNxq{vpDjfZ) zX7nfszw(L=y_R_V4HEqI#PY`cz4eN}Zz&oc8oPS1JA6NJR5!tyel>rCd-pep3KoOm ztG}4HzZj(voBWk%!5F!kzZ#$Y#i@S3dHtK|@0V%}=lxaQ0KX&fBs{Ko%JealNwbS| zRk5Vi>~vij$2`?40sj2QO-ENQ7*_y9bkbL$x?k_Ehftwnh}njL+Sl4$Ad%5Q-+XrwYB^xby_M|iQx*xwSpVbyWu)2R*a6XhDUlE zuBYO}CVep+fg5$Rz>TL8#Fyg>xV)#)f~UTN`wLI4_WB$3G^Te?6R9Mz$G&>1^Y)aa z28d1i?rH4J^3*^ji!a9)Pd5i(dfrp@4RAUUH{$6ohuP+|2+3is9e%Lbgmr_@AXx{$ z-~hlPyZ1+K9Dn4#Ra|&6|8E2B(|&W&_2jy7-bSQQsbbH4F(MDo#p3zYaUhkJAB}5> zU_9{W>lgpohx*r(WKYEez9K~py8eoZe8qHX@by(h^FY$g8Z$$v3^8&(ju}!IGrrFQ z*FI*13Bu_A(PmG)oW2Pj8e*hw;3c|yPXgygH#NV2$h-jpGvTQgWWHM%E8BGLkHM`h`3`d>Q zr4OAxW5S)}jCwmcgKhZvuT#vfDnnqFG1Hh?os37AYig~QN=vzLZ(3SouVAf2hylkj z);6ED+H2YB3_PIR>|}{lLoz<#s?P$gj>Us2EpzJ}EYaT(iuV&96Pn?yVp&&pG#*gN z-)$3Nh7cSVTFUo4SF}0;52!JhR}ua68jbMu(8}-V>Li@-94sxjmR7K}tgXUx^G|3D z{8UqEX<#d=`{ThiW>-D7})}3dbWoC)!GFe9xT1fsxgbc%ZG^ zVQ~o;L>ae%sSd+~ggbV0)~MvpZhHpf;kRtgC}|!<@TCd uv%})F+Z9mwt+57aK0fPky>yk=`k&1K-7v5IUD*q4mxC?Ip diff --git a/docs/validation/known-issues/figures/polars-naca63-415_n160_polar30_re1e6_iter100.fig b/docs/validation/known-issues/figures/polars-naca63-415_n160_polar30_re1e6_iter100.fig index 39e515ee2321028edf64325bde80c1da329fc12c..3f718c4d4ac22f0b0643cafdbb0e6bacfa2ec034 100644 GIT binary patch literal 24382 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 ZX`06^lZ8m)Bc$aBpQ%jf{1t15{{f>y%=!QT diff --git a/docs/validation/known-issues/figures/polars-naca63-415_n160_polar30_re1e6_m05_iter100.fig b/docs/validation/known-issues/figures/polars-naca63-415_n160_polar30_re1e6_m05_iter100.fig index 536814102b7c89fea76966d82e398625da789d1d..c947d5c57e551b3c39c370f8dd2423b25d978739 100644 GIT binary patch literal 25126 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 bhJp*UE?TLVN3aJ|cjOgW)_jGthOFX$2wzTu delta 4322 zcmeHIT}&KR6rMA)yId~2Fgrk523Q7I+RFk9-SSglVHc>4!Jq!bG!$ZLw-TX0Y`ajc z#b$^;SQBH@+=n*t!6r?bHcd_HSRd3VMjw2v4;qb$#i!Q9G{nCqrcv+QOS?a^TVLGz z;Jn;9XU@In`@VDTnIHay?|ugzJ3B*Np-xrO2XOC`!=uASPuFng#?F3&!ik`)%BP>| z35SqVm31QVXC<0z4gR#d_iW?M=-AJSb zwZf{i?shQC&nF44*G?)JI<-Z`YG_*o7uz_8411EG1}%ZXqfOI>wrSyS2Kf00NO z5JxwQakJ0Aq5$EO23o9(BR3QXihx!g0Byo*x2{JRl8}h(;E|RRLbBB{eiQs*tqOyz zsbcI7Ev>FGma+zIqB|xknFh+Uzb9WvFTFTX2b(!6vrk)LiVSMEl-cv} zp;KFl7IZZWoiHn(S#ZXg0L;Qm&H#uu?q{X9&U=0V7_f}--@qTq*ENIbEjW}fGRj*p zmM>(Ox8R6q!O{O)u&|fzztMs_KXM^iL&zhkY_1ug%UBAA=;7JWBjILTjGckeJ&y-e zP(iQIwBo7*+mo++xpB$+$&Z(=PVU|mGo*gRzOh|~6oW@$CwrT4XflHM(|8go^!9VQ zA`zh55|OI-4!jLHGk`x`BNykk*HXSFYLoZK9lu}Z*78ke8MNx@&_2Yd$ z9?nN2<$}*w3MyrMAiF$rcz|A;9q{Lszl`IpAeA;=7nfh9-ye9w9e*A0Yk<7kD2~$Y z<~rA*y~zVpNxI)`X1`s-N2=+L7f4GvD)Ne7pc>O{J#< zNm}K&@*s}YoMV7Ar%I#KGl~19smW0qLRxZ{hEb^H6tYSqhkNLT=j*%eD=dof&wsUkXR=3AW15F%Z z;`QQg)X4fWOg7Q+g?8a+Vva7-opJ=2qlfeghqwyOIZ$sVeR)x@=cxatw~czNBj@F` z5pT#;v7ZbG6>SQH0gRBr6yg@<7$VV>Bgh;vGMrAKH3x+eGHO%kU+XqcxDjYbW;4cD tYpkEJe+IIzR;8uF>sKkIOCMmRnf(VKz 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 literal 23829 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 diff --git a/docs/validation/known-issues/figures/polars-naca64a010_n160_polar30_re1e6_m03_type2_iter100.fig b/docs/validation/known-issues/figures/polars-naca64a010_n160_polar30_re1e6_m03_type2_iter100.fig index 724605f92953658867013647ba3532db06328538..9fd77517d62b6b0965743bd6eacc3b54d092ceb9 100644 GIT binary patch literal 28319 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 delta 5437 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 KLgOR28U76qb;_#% diff --git a/docs/validation/known-issues/figures/polars-naca64a010_n240_inviscid_polar30_type2_m03.fig b/docs/validation/known-issues/figures/polars-naca64a010_n240_inviscid_polar30_type2_m03.fig index 976a14f5d8493da4364b46ef836580ab83079ff6..8244519188a95a3b6344eaa7fafb9ec4b898cc88 100644 GIT binary patch delta 3079 zcmb_eO-vg{6!zP-VHp0_OA^4r-~g!x3a(>_6NCA2Od$Ld2Ld%D#{480jG^}S(KEE&YVuQ_k*=mz?rdE8l%k6Pn9Mx{y1zU|}C^Ehri9}Yzfzg1YI>3BG z{;<88U4!Ap;QVNCY{)-56AH{#F;`?|cHBPXpSD%KEy06khueh=l2D$G)6#VOx7(2H zK#+B9&qcKU%}W5i@}=+sAZ7hAyQV6iq_A6>@)24Bz)A{cw+#ADdK5BgRz?)Exh+v^ zW)yPxlvBz^_6ty+6$*m#BxeU8U!81H&g8Pm1$=^;7UoBxh}#xgZ;C<*pIoXeI{z?0 znew=JQ-pG*-m@uEL#MRki?H+=@>yPN5PvQ)9fNBZXis^Y8R zYI#?{P$_Q(7OLj zJHct#Q0nn_lu97+bc-%wW{vsSa3lEivVdS@)EhtsnjJBTBGErwbeV@?0%l>P-`j_f z%obZ|o(YB|VAZ9NR4UzbrSD}t0q+{^4!OiMf|=h;zYeu$?Zuvg_gg?&^qGJ)QHSm4~$VfM)N5yO+(SmSoi8^}%<{0W_&W+5uiLIVIH%%wW&1ud}4mriR z$vDJK9_^j!+{Y550?=7%3_8VpVoJpj&#an7KpJ7VWZlzQlm*Q>58dw zAwQqv2mceyS2kY=Em<( zb0kBoBvuVzJNX>{SFP_}ZE zE##U8Wk)>9Hqx#|+09UPlCI=YZt77FLn$VyrJsJfQbs>mI(H-CW=M7e#}4ru@nEhK z?_n@?-dc2%f$1fE^ufJKd0615f6VMePr;kJGTA(%zca8ekDeRIU~vG z9%H2K|Fba~&qjcZXxX^KfQ*sx!}wxs1eyCTnb5jJ%sok_4!dJ)%rN&f2_MZyb^KxI z%RnHr4}szeS98y>FNK+ct69DG439W*ip_e#p{%X83zPTRYnu_L=dC2<8H#rR{|R^- zkWNOJzt!2R|5uQI0nEl&V4u#qJqsOJ=?7NUfn{XY7l0pu{25?3uA@IMpV3Exqf@kK X^}Ipu#a>La7haR)gy%ae$R_*?pPftQ delta 4341 zcmeH|TWB0r7{}*3vzL?8O)@)4%x02oCfA;9lWnri{kFSJERrTI29cUn(rgWM+cc?Z znrbhsiYS7*GzY;41))$7Dj3JUi49nMX`vN@2t``3&^(ANR&7BGo;j0bleF0c!L1MO z!=9OQ{r|r2f4=$nbu{xAXvALUZ}8VClGaX|_k_E{dQ(HVuD-5KXP3_Ukb=%V*HrJv zb_Hov62C-nz1H{Z3(X&v-3|8Mo4xt~0E{c>J^-4%#{XsGO9yXk#zlM*z;09!-Z
GUGtRk}bx3HeccrFPX-b9R8AJt9mZ#X}D{_RseyB30JZDZ*^aLswK zv=gru5KDi^R|$wcft;@t5PK4)TOlBpR=2M>c=6mr|N3?FK=1D|*32S63e|*6pi^Ct zjdL}3fPGS9b6Vp?V!U{rafH31)Q}Q2fe6&93y5!U2+YOr0oFyeDP=!5#-U6+mory$ z94bWRspOUCt{teTH&AU{thyzGPYi7nMG0!eORX`l^P=Xr=4}`AjMILMjGy-11#dvj zCxBGNs<&Npi*1NYD8xHha43&G9CUgcQcY`QpC2spHl>=<%=QlzLyM^>*18;y@$>MN zT}=ej+Q!m-;F|MdX(wJSAeR1+a{<;B(;>Ai3N4fhRv?Z8OII8#z259pvFBKiER=oU^eH98Q zpyjG+MrQw;?;jw2bMC;OZ+JUz1Vg&ihWQv@rAr}r1_t=kpiM;>A}^95EVJ-QO_nII z%ZEDhBhQk(*rxJbU)?nk9aDgxh^Ra>Ud$3FN^s;;AQu4JSz@eJwo&`cIAz~PLpX8? z$QOVetnT;_%ZutPez*}wE&w?X*vSrzACxi8U@b@5^Gq7dX(X}rV_|xdFmFA)Ieg|} zWX|1l@4@x0!{Lo!G6W2vTTGa20YjEy*x|#KCWT#4u;FNjoJF(OAh=8fS)uNk@qs0R zOhKU19E;$48E0_EK!M&;nLEO{;UT(`|kFl#qrrsf?$0 z;2Q^4g+w!dD}vk`@R2M+qEn-jBU90d@#D+3v9`E?U@9s-)4hyro@|7PTif z(q166SJ2924m7s=*m4dEH=6@WtJlu~S6(EPugL)lG_jl0|4ezYQ0}AKOy$;P^)5$U z+^8B_yLefb996Pe8b95>KFzER>zqb@G!MP3^mH{_c%^iG_a-Hd~sKCM5~9T+)wA zKoJxb5ky2p`M3qzQsfXpF69rx2ZEp+e)0$86!}0wzP%>N);1*Brv3ezO=k9apLgDQ z-r3pNg9=Ve=$eqIQAPEmdJjx{CXMNmoR*lBn8J*3uq6(MV}iwyYv__};L1j1TRJ6k zpG8))!Pc4kVq=roE+euXRY}_$JH&F8N&soxL0zYat)BO) zuzmgA&>q6hyC(`!;rl@V(e5B`nIhalrPBn9s*Jhf_J&rpJ9}ihyn0Lh6ijTd9!vET|00eDYGyaT2J(ltXdpx5IGi zweMCeITMD*jmW&cWMCL>_x_C5b327$(hmYtdw-ZZNT6Xo`-Qg+p>fQG+gBcs48v2# z96g@iU5k$#9eE^aA&q5iO7MrNqwh=+FsZsYcd9PVPs)5Ef_}tU)p{(9KZHShHlH?89~c0e1+GqVCz3z*<2jdyZXb_O#+QN{K4L>Fjlwm zWLb!+tc~vQ**D{^9aigF-D$PsSxtXk(1Y(YPlP-W_58FyOx=BFih!xr#TiS5#AiWe zX)>R@)N{H7T0Uv!-0yZZ)P>TAR~?wwT=&iM`zAF0iq?I`%)hd%OH&=`2Z5=lKTN$O z(D`@Yc)i}UEp&qq{B$(u$;WgbPhOk6ULB-cw`0KZX)ipXleMXjKTOa1&J+RrRTt;} z)x~+B%qK7PJV*vI=>*u)nzurn2g^WSGU;WYni+@(0#l|84ueaUL!! zG>O@qB{T1(o?mbWd5EvrM2YD|caXQUfS24s-ZH)H4)T`C;12SRDFWuoLMqKSjIu(h z^&{QQldgZ%d?Q~1#qTfuY3NtwxR-5gM*Ey{yymaA1J7?bjKBCjbXD2Lm3ZZuuV;1p z?Xoy(7Wl(7N&>ChyWL=4QI5|pxm9v@_GWy!bDZtdOY8B@{4FQjW}LxC-ZCybM9&wQ z3jJZS_|6moi)0~{;yhX^Bt8o{X7|S@hwnahhGS(+US>|Q1lluxVacLVTk!pic@NjF ztH2RYw!HRPQU$)adQri;qve?NxX`9?{xFS~K%ZB9Zae+S7OZ`$cjo!xEjZq^wrx^q z1%A9txA?a&%CRhZ6a8VDn&H%C&k;L;jFG_~564XO|7&UrfIkwvG%o{kq0^j?2#t*YUzJRA^7)MT;cp2Y5 znYQcOg}?IYkPK%L6k!p!kKme3Q0La|pPs+G9QT}hps3e^N*w3N8g(&$8#cCF^~*f= zA}(H5d1@(jN#uH+pnT$*Lr^|&Y2jSbD};+{trpHBrNovHgSQ9++1INW%qJ*f%c~f? zLr@|I(S^rz*x2qS&m8H3+;N3 zpnT$5NKmKZD!XN_*o==Q?CAZ=b7lDA*wbUTmX_lWPuYGPw4fZ5b_-mK2+Aj}#RTO8 zmliG|y(n@CIi-akl2SLnqTxp*)8u|hdAN+!5`H&qGW>*iM|i~TBXVN}L0w)LZf|hx zIzAd+m^;AwE1o~Gx$$2wAHjuyUfF-~EUewTx9he|SH;};jG%ntT1ikoaB1Nx5-GwZ zgHE8EGH@<`-qjHrHgruQk)z?beJzjmSggqgzt)ktN^4coA zr{uLnDUx>2kf;!pxFvhlpr;=RYQ%#5Z`0GX5{6$>h#QBt6F6*Ri~ex({J~x zC+esFX&653%7ox_K_h1g$|tUK1my#l7G5AdM7RX){!B`VEy1r}2!ptG$_u8hokg$+W3M4()v9;_a|oYJJYE-&Z5=3lLf9Ra+H@`Qwi$+i{JG9 zU`}gY^v!jRo4>IKXY5uzwQ2QN7=?fN;O$vkv8-Lw3CgE-%^)ZrxU_Jly1MhrS)|nE zmwp-r)ii`E4R^7K0a0p7)$SRJrW(hNFqn(-4Y5o|#>kG;01b%J(&Rd9>dtVn5ve*x zg;bq5R0{X61AWC}m8uh>>Qlk7&tzxB3RteThCNeSa;9sp)`6@bS`9%?uHFEH=}0Pk zc<(`dGZ{e$z|g-fOEYb3QB21HKUtQ+D0di55tikERIfX`O#7)Rk#`){_QvX zA9t#%PLF^v{QZUeUwvJaI!YD8vA+!RK@rG#PCpbvn)7^IZ>z~H7!E<9PQ&-^-t7^& zdDEuUmOO=MShU2SmvV-I6cB3JFiJ0&W{RvPiz9_8GT0n;CeLOqWW;tA zn;iKj3uAJ0V5~OAkjGsFXAI)rj3eLRV2masgG`n@tF6%BFj*}IvlxK-i1O%XXW{Px zZWf##2i4)?&w8StV%Vx_ofMYUK)({e#`H^6mgsPCJ{Nn*W0RdJX3ggKT&u-s;yq${ z2f0=`icJAn3Cwc zlS=sYKO*RGOjYilvedY+Uk}0Dc&3Aq<23SQ3C?uxD)!gOAm$?P%1} z*|o~F-CC~gR)}#LR;umT0BSo1J?_y@<=TFt4z%3{wN~r z(jIki8dfTm!~jYq0d@4~r*f%ut^=tgp-zgW;v5E7Q!tqu20?Al2LC~|qFU$&3`x&m zxG9VypJmJjyMvqMur^^5!_5)Z!u&Gj^~CdUjO&#eu7Blxk9BxRP4i1%W{pqPoC8my zE=~z3%YhVm4s=D`oQ9R=K#u_Cz|*L^M?aP4fUXX5AQfT7IUo#|&(tiHG?ea)lrpLG zmP@54>g6=7R4RP~D3w0wS&x1ymr6z*NTna@uUIP1;c^xQ2MEJuEzcU`sm2uL931Vn zcn1BCngu=(4RZFcGJy|~3w$urI}Ixp_^<#9JQEG|=%;dlKVJs|e+~^-EO22g98t4W zvd|08NGX#_wp=PNqL-Y8l}aTifKquG89e%_TqBzNU>vu0o5w9$+nF5JQOvRy;Y29cYGk=1Jh$v*M-Hcl{1Z&> zYoAdtL|R&o5|?bnMNfV=w?)sAnpHd&6+1gonTjXKRXh%jcN$iz;#UHw;)!UIM?aOQ zcrDPc0`Q-p4rDwTl>(T=R&DLW4-mm9+A+*4!RF{PI0 z*MB>vn61UNdrZmG8{te)b2`od=vCmt^)Iq7p==J29k;6c>y0Q9b_ zH;Ws(u+b-e;goDe+1Anmy|t7O$D0Il#WRqt+Z5Dyjq3S?G&2GB~+t}4%}HFc3y zs{mT<60F}@#dqiWD&4sjpmm;OrWZ#zn?eZ*XU$XD(KxW@=fcAKHReAR9uZ!&43(!87QWXuR-&E<& z0{|WL93x-4^Zx?bo!i{3D7BjEI0?#jrHMaelQKH?U`u?6;H(#TW#E(82oZ2%NT4n!?+YCcH)Sh ztl43h$lv|zu$sA>G`Rb0xv^C3;YM9=@5tciEk9LBwKD+y=vlwMq}usFrrKG6&UtoK zd8+-VE>i6RKtH==sc7W<|0>=23qTh=$Hj2Z$XohE3mCI(fx(+VS z%~YdVYQ@**$RL`_YB>w3%)gLs5{mjAhN%BDUZuDk#@rXX%bH@l<=)8$5*GHn_5T2-e``4a literal 20762 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 diff --git a/docs/validation/known-issues/figures/polars-naca64a010_n60_inviscid_polar30_type2_m03.fig b/docs/validation/known-issues/figures/polars-naca64a010_n60_inviscid_polar30_type2_m03.fig index bf40ea1c2d9d732a32dd9933c5bdd608ea745a48..dfb6a0f6bddb0f2f643f8b04608787146b5d98ee 100644 GIT binary patch delta 2767 zcmb_eUuauZ7{A}WNqchI#hdHaKUtGz-P}&PwWMhpoBm7Ev|ZExrhnIEq-l*btWCN$ z%7&DI;ERYN=SdJj#0POI<>H%9qTs6_3`7ZnvOcKPhpzLfo_lV$B{2;dHjw0gzwgUE zzu))!&bfK}56JGpG&Z_gJ6m0{U>(K-6Twi>;q?SvZLTgyG?TcU$z)zn#aH59Pn==V zSjx*jlBw5{TPw+zqp^+jT70veL76)n33oKM?rPtb;k`j`um>4svAGJHOag!H`O6JN zMHtza(9XR_00#9<{Ewu5A^rtm+WGtd0IQ#f2S5j`^3K=00A}@7Gec#bv+#ia=G1ei z_3+y^J*-o|>UdxTo7&>YN?=#F{aJ|)gn1G-DSHxzUS(HO|91Wk6635sjF?6Bz?lWi zqB_t>UDDRUB)|A+`ZA|+MIE#vcVPou8)y>i*0_u=gvq7(_+Z8~XTj&(ElxMImaMZ?F%RDjA|b#2W0wrAoqdd)@}4 z+NyZ$smgc!yHKfjV3ISM(iufc-D{dNnk`i_#~Ce@D!IlPUFVFV`HX7oAo8IBi3&o3 z%nWjXRm4Jbh)$+k*NEU|K^J=mApE#jZh}qNcp(%UyC8$i%N;_|$}U^UbO06&iilu& zB@#zQdbn8&KOI1#q!(mVInM4lby`vb#k|yIZgs<76hW8P6ossjU;7 z%=5|_Y*`qZ439a;X5tlkDpf6=BTa>hFVY9;@%*uLa?>sma-36w5TtNAKTk zyvio}1om4CckVK|QmDg4&u->9*Up@4BkuB@W2!t{Re^KIjG~WzwtiBoUgWAeh_`T$ zb>;7|PU6${Snh!KGMU|^r+k@N1L|Wkr83Qn(Yxu9>7v71?vB%r!(hG;KMCXt>5X)w z0VbrMglJ%+MOhO0IjX@|89Yda3gHn350h{%tZmnE29J`lXX?fT??!p1Yu#|tpKqP} z{~dWLzax`ms<0z7OiYAK=W5hCGSA>yGFJ#MGI)Vp%Z0U$++gr^5`}!M_1^{RTaDX2=E7>ybm~tduiiVjUkgY}0)J%8EaF7aP_Tmhv3 literal 30220 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 ROBRk15)B5pXD$lq{{cS8_{snP diff --git a/docs/validation/known-issues/figures/polars-naca64a010_n60_polar30_re1e6_type2.fig b/docs/validation/known-issues/figures/polars-naca64a010_n60_polar30_re1e6_type2.fig index 7d6774948f35e01aeaa743bd2d01e78115f6f27d..b410fa9e60311fdd3dee1c4a9e2d2373739db0de 100644 GIT binary patch literal 25175 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 delta 4975 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 diff --git a/docs/validation/series-cases/figures/polars-kt_n160_inviscid_polar30.fig b/docs/validation/series-cases/figures/polars-kt_n160_inviscid_polar30.fig index 1943c3317fdc1e5cdec62783af880ea2219daa2a..962da86e3f24af23bb9fe056f7ecd92a6cc9fe4a 100644 GIT binary patch delta 2626 zcmb_eO-vg{6yCRM!!S6+-UM)5Ol(M#F$8Km;9_jB1BnU$)cgiqnV%40VX$LcRr;rL z?+HyWQL9v_y;O;m!`^#n)MHPrRH;=IrI(^csZgahRWI%A42ssSsT>tc*3SFh^St@q z_uj0(eF~o+zz{ZjJ9|34R>={@J);lza$67gwtw28Gr9GfHw*9dx)O!;+l4~m-OWNG zx$O&h6Uo%}TdDk7>P8}&&7@aym%Q&=VK)>Q2qUvqZmY#LH4^_EL@G68RT}wSL5j9J z{~W-ieJno*EoYHs?W3w+0cr$}wGXS9`-HISw9PtZS%qcOmQ-e)64q&9H44kF-Ru6@ z3{I`p^;m&sEgxYIxA3)SKYCcM-{MHte(~%BTo5?nIJ67vq84(nsGX%}is?}`@`;`N zVyA8q6W}q>Bdnm--ptMu5|LpM84#dZ1wFQ}-3^y~sXPvwc_%Cq<4akt1&W^)$~hYlP-_js+uho#$8* zITFP=8to9jYeKS$kYttY-~xw?rM5wOYpXL&BtJ8H**6Nqq5~w!PjC%3pBqe$owI_K zhr6V*k-g6UbVp&)q{;|Z*WxS4OxU?|_A(dr&JC;eMt-y{~ zMXKp;I$U=c@c`oY0oBsjdQhn$n7N$v)Ai9-;og88JNs?U(83w&TU5^Q%5gG0$r+v? zb)^i|Vun^?`!~aS&hQjDeH6n#71Y2OLaBz0WtH(y(UTjYfwI1rQSA)p)JW_)CyPO~ zCc2XzR}X>J_%b@n3hyM%)U`!y9)xY8o2*fX?2uY$GJi&wOwLL#B>T*ST%1seWFM40 zw7&z_VVo+g=T-`-%y#}5JF``-$F_yo92JK@sLnK@|&BrFx&uA(YikGBc-15-78TWMA z2Cj7K^egdlF_{R78p-s_-YBldB$+C$#tb78C(}lLdNt-5JWJ+E;Y9{7kn2WRug04U zULuL3tI<__fh}4Ps7SUnxZ1kK(wBaUXq>Z7>y5 aYn#-Uf7Ptc;5er4t@El{_Wotbz4AXO-v6%v literal 30163 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 SRw0F!pk&De5Z*-5$o~f_Z_lp) diff --git a/docs/validation/series-cases/figures/polars-naca0012_n160_polar30_re1e4_damp_iter100.fig b/docs/validation/series-cases/figures/polars-naca0012_n160_polar30_re1e4_damp_iter100.fig index b93d2c75ca9f1fc91cbdbaaad75953cf74cd8cbc..9f566f42e3b25d2591a9d5f1abce88792b49048d 100644 GIT binary patch delta 5299 zcmeHKYfw~G9N+(4mOWe*m#d&|s5eA9QsM$ifhHjNLIhkz3CVSVB~XMF*3x{S7HU!| z9?M6TkF3l`K2E7oR@&4w)l7Y`$2isawVFmhG>ua;*g5y?>)u^V)9F*r;GX~M_rL$& z`Q3Bx{qeB$<0+|Jwm32#%5d0B_Q`T#X<<>J>TqOc<*C&^&vKv7x3bw?>*k(nS94~L zquS+dX>zw_vqh^X(=)c(Rp(yft#&o~Jgu1y$7&mNP0A^oj$&<6dLKD1&ZPajmjAB| zNih|7NXSgD%(eo=&~9KqvBI*_&&|w=6P7HjzVuoJbM~XZSlLg1VcF=PaaN#(0~8=; zxd)O&$slTvXGueZWvA&jmee(rC1u-sV7RDNY4JejzA%EhYm<6lq;RLvsw7rBO4O#) zr|isnKv<*c=457#5tc((S)wkR9#L6roUn3ecM1bvn#aIDPU)dt69)tNIqf>+i`oKu zIgMpc5>}D0CX3oBR36P@(}Xo$STlrGBCIk|S5EV?S*BB1vuW`-W>pAlu4d7z73>Py za(iID0H~&ma#_*>VY!4=BT8z8Kj6ja$QXC$-{ zsCU*_XlKh!pq!Ls7^xhVI)0sXZdk|F{QvB?{l9K#=vVM>CuL-BkWeQu5yXInEuRCn z!(zDw*q)2!R-o7CvgNmcx?p?dCBE1We!2STbi2=v>>gcJ^iysw44Z-lgR80a5v zk$WWMJu19RF2`@Wmv4!^WJ)|0YIGWCdA)#qJLG)_=)wl!Jsa|#3wgUk?cW2swn^lk z4|zWTx~*AwKMHv-0NwA6fq`4BNKz0o*-V2$1-m4h(u>Gfof%Eol*NoZ_E!MY16s_& za-3{QEpnBl+Q6oztEQ-#c|&g+E`X{SMM6+h>vSW__EmPX8OIR!)(JxwLIZfAW}dSG zA%!(tF{P!k$p-A|VzHGZY@879&o}|jV}SaQr7LpHaad-dq2$Dd8GZd>4jj*b$RvqNrR1S$pIzvPODVCYzJ+ zAR}q_=;s@P?UC~hWAhyfC+c3l1H-Xu1WVO}rQi{fVE1Wcjl^lak#)w3Fe3|$5*rkq zta4JfeEg)SvD*>V{d}ZRIGv9a7?q1EgPdPIQ?H>+ZGth_5tty5&owlb<7DA%BQ+1| zsTqfJBB{yKfFHuS0brw!h!u_3s4?iMJ?i*bqvd#6=C8nRUt>#i+g-c`Cn||@LgkdQ z=_M*|_0*I2Rej01^|~9UP(N@!E{GiX5pCc}xF|4iIFnO3lauijUcXtd7rBgKLd*J+7b2 zgX<&bTEyofZO^O5qkRbnU5uNIpk6&_Gj53lZPUnFid%al>-SjXV`Q0cQC%+b<)%^O zV#mF+qu#c+dTR00=}GkC)nRlIP3|6w@MIRUNZJHH0E zlb+3^6BkO_Q?|f45k1ZQ>j19@V`Kx5JOh}0iG7y3d2!f7vlj_+R*UD}5u;VkO7uMFndt=l9B^(_ zKQ}ySn;~fn;H_Fxe}B?xaGn4}fKzYj0?fV({O=oy{tQm#i+r*jfZ69j7-2lJn@4s6 z-W3tq%OiULv-kV38cy;)9(ft?E4M-N*;urnlN@u4(k&r3I=RDOr>ktKYc3DmVJ`Mcz3ZMOqVfMEHzY}Hlcg5^y0iO%b-p{W? z-r;?%LpR{}B079%=x`qJ2T?j)6dgVSd?DB&Pjm=ySe1Z4=_e)P@uhCxX4nrWp(++h zid5>Z;vW@e=B&w;V;vcpIW%RvDz*OxtO6m6rcG(ECFL^6-+_DykV!$Z_C3Hp8a@H# zBFM~^C7$YEq2Q_yTO9y-FDSb~E|eekHnsSCo;vjjchfSD+SsO+x=U5;Yph+Yx_zpz i!K3m)-s5 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 bIV7sCr@=HucE}{Pu9wNAxiir9!-oC?d2B2Z delta 5537 zcmchaYgAO%6~|}a8Q^ev4a3uckx@~$XmCJ5L0~|9APC5ysDL7apeO-E)EJGZ@j*;{ zz=^NaXIxP+ny5EMjYi4SWHm{fd}!LWvKrfdNc$ntHWrg6B|YcdVdjn+lCG?k`7)e+ z=kEXi|2z9W`0%c@?H^LK>|;wBmt-^Q%;|D!ac*9&)n-dh$*|Iuml7qT)agj`OC!EU z$!w!F*%*-cuX*G55BQS2 zIBhn^!U)$YfvwCOFSKo zgwqg5tdcj>4-`kF320S(AEfnL98b$e^uh#}sdRcQ!Awic3G`G#BxJgc zWGOe18Oe4R?8>K!tdJv|<UEJ*nz&Io!1}<&*KO_Fo{g zg!S3Oac#sM_)lr%Oen20`Mq)EhXeyBu? zJKu6;)pP?Wc3T9{&cg3_(6;{VT1mGT-DO!;xw5RLM~ltWJ}!ZtEDmFt9{1|)%Cy#1 z%-r`z*OrUzu1xFP{-026GgbfVh5vS%;pl@6)Se#!8)->d1Z?7eM?VR!Mo||6RzLUE zbG*@3iELKZ7Z89g%5P@*f&W%8mB`?4%J0f9)XyT@=(n?H$8~@yN(RYfbd7M2W|U*e zQ?#T!a)eID+QpLX1OD`B+Ffq3J~QBNb^zbtpzA<(g6T4wi%8QsdTjZ5dVP*>`!070 znvTOYAJZNCulb$#O9WNiyDv;;v^$ku^BC=}e%hV4@_pIyS%lIrE6U+nbx-yvMOEy_ zd+F{O-@ravvfv)A&kdowibLQ4Jyva@n{yLrcXdSE!2yXL(r(b<0e^Z#Y|zmGe{)Q1 zP%zt|=RLbYt-(q=D;@Wge$9;$`;zsGCPKNF7Yo$Cwof_%)ZYAqbP}js*9WKg#UphC zwbqAIQ+`}X_o0Nt_VQt_%jb;PD(NgJjf>fx`6a$r(#t@b8hYUz-B@gq&I9dP8VeW1 zLJ1*ZVJU-G(pC zYj1y?%_b_GTo)JrjfWg#7k`iH{tZx`Z)F;9iX|Oh5<=fvl}dZt-hsD8pm%`2x~do6 z6&Y><{a|%0yeAg#^TnVD$AF*yi zOFE=O4rE0+OXU(`FtMg zfcVn$7b>KARvg$bd-80QfR z_ZG6^5Uo&zz%>+yX>mmgv3MM=6^r6xBQQb5^|t^L&2Yi}116v{nKd` zz0+{Kw)YUxdjh6wdk^KkGjO6x;FAC{Oc1zlUj8iRGHqXmSBT6RhCG#6qjjJWs6je_^39Oou_KTW)H#~A=ZM+xtMyT z=F&gzbYH2HMDI3SsqLL7dauIO+TN3S@5gYBme>?Q>~UP{A$CzFQyDS+{i7(Q6wnDT zjHDkO88YTaZ)E(rH_e;HI($OQB47AjkL{}8OCY}jWIFe2*Z{bZoWEk-aRsZcU=9ne3ZYX<0a9oi|slfTb0CJoKwjV{w z<2t!eaf?dd$gy_7N_h5+{aHLPL+ST;J+X6fR_Ip#-CR0 zOINAQWl;|T9`aC|$0~;bk9aB-ta23an3iMZ&*k_$;PD^l*e8=J#?kQLSWc(EJ)I&h z*jE5AaM_q}KqiY9lQbk*)#0u8p*eN4gGpgGcgpa=}I}Xy^fagTFGXijR6Lz6to2R`vip fOG-{rLN3{)7&H4ZAwFW%q-bK*B@$V@=^NpH=w{XA diff --git a/docs/validation/series-cases/figures/polars-naca0012_n160_polar30_re1e8_iter100.fig b/docs/validation/series-cases/figures/polars-naca0012_n160_polar30_re1e8_iter100.fig index 0a727eda89fac0298466ffc60136e3c0ffb78ef9..5b88dd023a1052944473aa0fccec3894e3130a43 100644 GIT binary patch delta 7068 zcmb_g30M?I7UmU$6b-`Affx~WybBx<&^6v*P~&}!M+9{d6%!OZ5RFHS zhVeu_odlaRns_3cl^vozYnKgz3%t^`u}=WUG?@g zvgtbMtpz0|wo6QM2E?Rl9s74p@9IiQN>1tE8d;b#v#_vm&ZO+I*`jA;)}-XNNh7nS zOwP+LXpN16oaCH#BeQY~a|)7^lIA+0taIA|y=W~bi4WC6LIUK!tEvB&LG*y$^9c>y zzdIPfVH^bfsU?kIAn)8YeL(%SshT5gE&T28d$9T-P9 z1_k3wcNrNJf1;Ijw~?y_1CP?3yeRZE)a4mrRZx#Zz6cb{^J}}If!At?mJHNbVrXJq z563voq|w}{9f<}r0mbp_dKI+vLU8lQC^sZ}tyXBsILQh^ict|A493+aI`AWPZos{K zYUm|s$1CbqK?iT{jy$5CK-Af5cR@P=y2{-58Qo(scQFlNEp|MsgCqTcE6I zM%NUyrb{?8cygO6nCS&D3twy+Qk0f<5A<$H=XiS`5xtt~MfK)KAC);HNrV2z1khykuGec^dfTj`3(NmgW*^CPmQ_&?7Ve zovMl6NkE9AOU>RB!e4U)_*G964F0a{eb2X7ynaf;g!he|!!XKeZalCL&hT-=_m21g^g0?4 z-Kn@Y|Cb|wWhlJ}IMq!}GU%=3~AyqkhN*(6clM)ra#zg~R7z z=yCL-*){*uL5Ca6zmj^Ve^m*6a&z*VBQHDtm0;|$v3>*v?NiXVx8i4 zqCJPhDA6emaANtoAcklG@#%bYd14+5NI|0m{-r@Lvk+347NP|;P0#Av)Co@cxGTW+ z$qw~z=}v=8hfXMr8SBZWLEN<>CNPjWIPgxRz!D{~=f#L<8?6gZlwbPquRvOW`s5JnqGZA3yJZ6??e!1ike^!6@jhGMZz+*uwdImt^98*3+JezLJUFtdEt60g{P+tiQhy9)XA7C{Oh*k)buQ3}!>D zScc0I*|4mowOmI?92qRrisO$G$4EBHPLX3_G)pStS0?jpSx-sO!Zl4CCbJ83Cr_Go z2iLJNdbAcfyvKlEeO;^|XFN|_5f(U_W&2B(QW;?EpONp7subZ>Ew9E(#P;*vLePYj}?I+tuaP#D(%!_$}r<0gf1Bd`jIv8mstwK;-T;$amv^()6;Ss>f< zHLLH0rh=obilfcIwn&Z|`iQ#Yrl=CxHelP4mH6G`1k)qrn=d?1 z5cVYD*<0A{>GydkT_DbEj@zufzP!P+DI<}uOL{X5UeH)PyQAJ>lK2vgyB5^A~27oLgxx0+6Kb#@Q-pfRYF&n*Uw>e zlalB5%BvsWu&?zU_^1&Z;1(i35-JJXjK_T5$(Oh7e!OiZY?}>l&noG55QY~Nw$klU z#O)#sFD)#@?N`L@B@8bykT~-@MZkMb!8<^hVS)F&AH0Kv9kPLUM8P{u7~XE!!aJtm z9VHBJJS^}|DtN~U!y6EU*G0`NQ@zZ)C|CWA2^Nq|Br}*2JrFxG@v}yE zi0(1%wOUDu$!(3rhvLY*C3us81sNm@lH3#2_y^Fw1??-K0Xo&>MF-x7;Faci(9qV1 zc+}gX)veI#R(N$Q5?go&q6Ol&AvP;OTMPPX(7I}!r{qm8EX*0_nvw0yo1Wv!o#yJF i-QUFubH`5PQ+75B(uZgsjj!L?T-R;41zbLo$o~K~x9yPt delta 5465 zcmchbYfx0@8OM1J3%(rWwt(Da6_A(9BCd)eC?H+{xd#pjYwj&O%y$vn~7o*lQfz-&6hOO#!03rWG0=7>EwemNi%)l=j`RINl81?DPMM< z$34&g|NoumoU`kRe=0Y>RU%cN%#2wXnZaJsHZ`NXps>J_nK{EY&w_uvl%NDFhf1<0 zWHH}hB{~xui@AU5KWgV3ojR6R@{ppzToah~C=9euU;==<;jN8%i-rlmF^z!_e&RC< zzPLUt9VXz{zUjc|y`OlWDi2q+gQfZ)f32U1<0iTq0op!QGy-w3{7ci{fCXXy#8C*w z%L`Mz(^v=|3`hsFqY4!j%?ooG3)kX<#Nm@%jR?S* zIqGClTY;-JiKGjjfky1H_v~^XH&KjQLL>E-yKPVZ_2AVbAUe+=PFp zWp8%WEwUDdH+UGHKSG8#dNgl447a+-v}rFd(rImS_1d+|OLSTtM5_~rizKZs;Nj)PXON7E_Z=h}j;+TLZl_&u)PHtlGcE`G1`{NIOKTKFuyVy1JdAm{R=+)9PFBHW zwqQYZf_FOGOWj9|UDIBLJ*pij6>}cbzOP~6|2UXWiBlD3J`Q-hbjWlD*gm|t`fU6Y zV2)5hF$e3rvtY#WDt9QB?Z?5|Nz=T%s5mh0M-Sqa+PL_EaX)hizpssh!x&Z9(u!bi zP{H54x@!pD@-g4e`(M8b&jQ1*h~|^pH}wL>Q+Q|1FdV_tYlq<|@$r8e+$(<5Zo&Nx zx8NB(Uj7w4iz^$);23UMZiVNt-EPGtg+aJuc|1HXsk_lyl9l?l>x3Te{S?QxOj9n?HKbetywfuZBhsgGRXFHNhu=~0jx188+jn)h z*FZb5RZM(O-;wuSBLU<4E%@2CTkrt|`90vt`$pkIa(*4~t^3pA1{EL4A|<@)BY&ot z8S@Gj*N_FG71b*>0}t)WSZqsD{ZyaC!iLhsUwb zlyC8x$Hc#hqby$ePy|)tU7g7+zO~rbs`bnpuO5q5yf{`KpC4hbR4bUtEQa2PcWeIJ`aE+r)kF+>TaezX8J-)4A1XGnQ;- z@aaaIabz=-&u~MTOlmehQ>yvW&c@@)J!Q@nwb1Y^KF2s*P!dZsmuDM?Po?4Wc#h8C zV}F)F4DMQ*5?iVosH$y!Ej^8WZJWD#U02&5Zpc5ck&q4YZ*ToCU2TbEJ(tgSgG%xQ zwSeatK~1A9`MkiGC7H4;Q+njR4d}eMzu6jE8)wGYU!j_ z%FB#uR#GeH6*{>`16hV3r`)|gHMT4@TAh@5Z$p1y&jZt&64NbPySrO@EWK^bEiQ^* z9jO}obJ?jYA?<6DJIZg|{-x`0y3B>0`||W#NTz3|Dqd~OBo;^9uvYMu#!NQKRKr&p zGtHz-t9h-=#O#MbJqTy38{t)gc@8nZYg-jt7S7 z+_C0+#@fW&jjo;jIjzRRuAuW#?t97BpZEvtJ_AQ zr;U5~HlvM2(gv=_G^}_i-+7Z=Og8T4{YD#0$i{a5fYHWMvhg6_;j|H6^@hrd1YPgD zH(IfkAiPjA}hr<@LizW{Vk}cfR&KlfhVVm=cT`H`87~409z(4n;r(f z=kUpDW*-1`Kd@41*|Zn!*_O^6q$M*XNhZ`96;W-v{!% z1lqIJIjllF37-JIA8^+NtKM2JS_gn1G_)#3YXJBmYH`P-yvkgt=5=>$?(1u5w%pU$ zv9-n0)@vzmEVuB^#-8?;9!$8L#Ke(@fg@nY8J-f#CWPp}1A_CTm1wW2;%kS5T1`-Y zva%9t3ch`M%D;cm$jB@3yQ3A(lq)IaQ^1c<$|!ll@L$>WY{dH29 z?|9Ge26ZiKlQ$;Qo4_y0@5}mSxXBd_t%DwyVLOGo3|#zfpl{21s16D>0{p5WRHqDu zS5IX7+NDqz3B3#aniSF*ZgvULYQG2keGi7+B=-Su@sq+Jw}s?B1b*Eh*Fy|%0RPCz zkk`*dt^w*+laTD>)%$Q-*+ApOjG120~jQP+8Dv`|M MmC985RS^vT0`Pm#Pyhe` diff --git a/docs/validation/series-cases/figures/polars-naca16-212_n160_polar30_re1e6_m05_iter100.fig b/docs/validation/series-cases/figures/polars-naca16-212_n160_polar30_re1e6_m05_iter100.fig index e90a6749ec24c03fcdd0e01f45b51fb7257866c2..459c7cd4b0b48e50496aac19e4b241018629bebd 100644 GIT binary patch delta 7527 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 Y(AC5i6Jp?{EQAuXR}2yPwa^Iv1s6KE^#A|> diff --git a/docs/validation/series-cases/figures/polars-naca23012_n160_polar30_re1e6_xtr022_022_iter100.fig b/docs/validation/series-cases/figures/polars-naca23012_n160_polar30_re1e6_xtr022_022_iter100.fig index 9e5cc73ac6c9ec8d374e9e67739856146f28f471..71b59d7fd246e8b23bc67ca3ebc899db9d3cde5c 100644 GIT binary patch delta 8097 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 diff --git a/docs/validation/series-cases/figures/polars-naca4412_n160_clsweep_re1e6_iter100.fig b/docs/validation/series-cases/figures/polars-naca4412_n160_clsweep_re1e6_iter100.fig index 7fbbd44856d1410f93e9482e7254736520e56346..172a88ab9614c08817cfcac3b665f5db0f88e5d2 100644 GIT binary patch delta 3258 zcmb`J?{8C87{~8(+m5rdAC^lyN}=qsUmV6-+PW3GU)>lCHueKL24hsZZrr&_S!>tH z3_;L@m=L2OkG?Pg6Jkt!;f9IgH8JtUjQ#~06HSc1@RcF*#`s3TbM7YT?h2W1P1^fC z-*fKm^EuCR&bc?P!TjscOKpDN0iWM4xg)e?ptGw}ZEOq#o7GrOo6F^Lm!}gG3E{-z z(}9pb7N1DXT+*~mES}W@S|}Dz=Co|U@4sS)n{A=N9#UkN>q}^Hv1EK6ME+MpR;1p0 zKrA=sb+DoaI{sx{#Gm8^C>9pQ?_?dyge=EPsn}fbm$NeGHA#q zAw&2_g$@UV45Q}Kp+(46Lq>$`z{A^h?5S@gK_F;H+S9|AtC9K+Vieu(0Y$`lOYpZs??|F%=c!$Btwn_(0$VvRQ z`ZJiyk0)usrbO>We5+Q6w2&EmRHs8$$QewtPaW@BiL4MJ*(DdK;FhUW--Y*mz7&&! zywS|R9niB>eUcPpw3ymzy5fB`cCZ`6RjJ^}X3tZ*J7CnR$OOhGq6uQdYhJg-!mN1P z+v*vEDagS1>F6*a3jep4yVJ>(9e8*}Y!g24ww5oCxC_ybfRx~!rm&@$QOx+Js+YQ^ zAj{*PgD9sfHR#0cYo|N3k#x@(bY-mEOsA}*v$M^s>6`}L7Un2K_l=cQa5^yOZfn7e z1droxZ_B}geg7p}+YFwq%q4hA^Rl%A?*;l-Ca2N}-@|qm3jcj8QH}6T$+=oE#Gsd! zQ!2K{L@t@0p4q_qt5%$J^GNq#PoK)N+9bZ|Ew$`oRc5|A>}xXd?J@XvGggT2u9eht zKG~d}C-8--n^x4!#V_ZwSN2WRG^n$gjFwerlIOJzWc4aee9h~!>}3sRK0n^xZ{mB_ z;0v(e6ZjepzP06V6<;cSY2*3!FY`6Aka@i|uUu~jSa|h%YcqgbSZg8Rc^hfxz!q~t zpRnHI*tCBSmNo4fDp=5KU2|e1>|7aogmtVA{g4s*LDnTgSC-dhTeO87;d|d<){Q4Z zUS&)+HdkTya{DOjG28vzKF0clUA;8=@osvvhzuC~ri4F*zjyuYCswi#vg51SPa5n| zHe_Z$&1r_&DKq3Kw@26+v;7>m&$3v)Jsuq+W86N+;`nx$_3t5c^U`T(?{Ld*smi5P zP)C_0U5hv$J$%^i`Q_)awp!8T8IUv5pj>{eY768HG9y}svKEAns{}X zHV&f{yJ%kQFRo-xv&?GdoWY!BGlDs^v||3v*JwEbh03KBYJFq-K;pt^5s?+SzH#Z= zJ}@4pMSfo(^vREH4*BYR;G+q#8OfDhwyGb1eh>6pAd*6;@utIHhrR}75h!m{SqxoP zJHFbpu7vTleT42>0C}bufCK>&1!0LMjc2Ds4SP9xS%F-YHmtXbLotlo>Xxz XTwzmA(kR7u!gc$UwHK*f^2`4KMRc17 delta 2330 zcmbuATWBLy7{}+#VL z^Us<2e)B)yH~HW+diigX z7qWuON@yb{a&TD%XUa`@)s};|%h!>{t!g;wFp-PP8X;@BbPMU>vJNh3CVWS7kan#j zFSi>wqo8>+3AGr3?JN1~^rn1H*U}9`R40Fud>2qHd@&vc5>M zTYGjOqQ$nw1Vp^pmR<&-EBE0(h&JB;K)*IcP?>h<_&exRtG1o`zbi}ZBZLQdw1aRa zV4?(era}PVe$C#&e`SX&z5!ykxr&doL!Y5)M#~qP~A)DqBXk-or18I zy7PqB6|6Br`CvS8n2Pvgyq~UT-%1?0ckQC{akuQy>MvyC*_(+$8+T_?Co&1praaSS3)UHv zaxjw&Y{WDF-ZFf0>fz_`#GOI$iU+sSHvZs>xn2^(4QxgzZ3mR>${Rjr1ZhXX@H3;6 zc5#EvmK7U<0ec&xt!OC#t#J1hag5~ke+mL^syLVF6Oo0$=%v4YXUj5zJ(2Az8a zk(Ur|)ka9_M0RmyW&Wt~^lbL2d1GPOn4Fz7=##TcPs}etXr#r)?(3ud@Yx`3ug3O# zH%n?-mvt$?zCP36D7cNim*rr_cf-j8$IBNuRGs#b$v=Pxl`3 z03GCqe7oz?-3cW@hYBwrWQa+c%E$bo1$PLXeKFf284@Bvcs9`hW>OR5aPGC#&0Dc6 z3FmJ^<2BEXA5E4R(N58hX*$f?u}X|X*A6LM?rnkXkpE7{gv2S0(lHK7XB-%5M79x! xR;wFr9WyreHy~@Z`BgXO`iU(Z>W*xjOS|NHoo&Y0Dk7CV*pPylh_Aeg{ss(N_r?GK diff --git a/docs/validation/series-cases/figures/polars-naca4412_n160_polar30_re1e6_iter100.fig b/docs/validation/series-cases/figures/polars-naca4412_n160_polar30_re1e6_iter100.fig index abe3bf7c80a1607898c38b207b91964f04bcb237..d7d0bef39bc0e980a458cee20d6d6942451e04c3 100644 GIT binary patch delta 6835 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 delta 5697 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 SkIK{#$uu7eQ|Mr1g8u+D&Mi&= diff --git a/docs/validation/series-cases/figures/polars-naca4412_n160_polar30_re1e6_m05_iter100.fig b/docs/validation/series-cases/figures/polars-naca4412_n160_polar30_re1e6_m05_iter100.fig index 55cf653a67124026cf3e3c829b2605f6b78c4414..061322a9f21214ab670f0ffaf18b7eec36402ad1 100644 GIT binary patch delta 7761 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 delta 6315 zcmc(jd0bT26~{R<16-ag!(&)v9Z>W#EW)rTiVlh^qHM090Y-@u0TK78bvGKL0#}VD zMvcbQEs8$VsL^6HTNBe7mn(@!v;CT%{Sg!I1iW_dHDr0pN+{4>nCXYTKO zf9JeA?{Rm%_Qv;`Fu^l5B_kzO?-8CQn8y#z8*0qRNHY&KvhUjZYV?}s!mNQ=#7nOU zPxZi8hvk>vP2WuIzbozwOa5Aolvu0--FgiH=|k`aa0ax}S6Uv>vB9N3=zav^$^P!y z30~~!SVM@n7$5+MWa2xocZ4p#X9r%29NBNJWHi>+1;kNtFT* z__LJsFCc&w`L=_eS!fajvU`3eZ4ePzv!5X($hL2Oa^LA+-oDkwB@>AO6N7nTGK4r# zLZ#>czGIjp&{IkYV!x{iL&jhIljwq`O|9(N;IOxbY0>oMn~nbQcE!3 zHVy-Re5seXN+Upw_)lPI5n=2~Xgl;qta#SavmN?iwSAc>LdK$qm0;7@VY;2T=;UQt zK9QI(F_9-GLz06cS*ncWaw(2LKdB*_2U6`=X>5tH6U>N}Zrism?Nsx{r3M8n1FHeC)U?}!+>(ofmycLu zyDo980tdx7Cq;8aFw02&62?223#C40u33aw#VjP_Gi@R%!Fo$sQl< z5O9u7BBG`LBS$?tl!2_p#5zpuMe5m~i{iAs$Xr&L7bhz|rstu9K2)$C+ZqSipxuv5c=L)U_B=bkre`<;p3A&VxFb(p!H zXC}i2_EAy1&m$nl2%r_iSihn$Z=SS4LpDmLVgj&9N-yDWj~>h^1=!36PxvYCG5fB{ zd}hnzVIW)V(b>ja&pQ)t$yx@o)gE=p-Nsf<41p%8GKX*7EbW`*4L&U(RtV5ToWkyV zmuqJd#{8yK$7}~NQ~-^rSH3^wlI>mAj3hf)^OUHVo!#a<&felVySmNU&3vXt!xM~7 zZC&{!h=)M{gSa2+55fgcvBUCfzNh~;f=Z|MD4lvrV&(km-pf4+VcY7wVIMm;EgJSK zEnXVnkL3X3!3wTDJ1kyEX9`Hsg4=InQA;PIQ}dk~bs5|2rV9Aa?LhM2Yu+&5QW!N*+4p4E>r-!Je%A zKTLxxtTg#F@T10?-?@NPRL$FbM!q05&jTx))fx7?pS{Xn0M;jMbfy57J)zJbhd0{gJB zv+Sx})a7J(%O$$%^})Op1Noy%)WddS{mCV&DHgm9(vXE*^t$r#2KRk2cjP8I z;?Etq1>z@sJR`*S_;@aEWEYptfp?JkU0|O)oS@^v@1Z~MJB9A$W$oUk_;VX`Kag`x zhP%E*BNF1F_XqC!{OqUq+XDaA zGB2RT_`SvGF_7>z{w1cYbhyWkF%t<_Oac4t3jTbpx<^?49Z1My<8u3YOyz4<~3 zv2F!nGZ1fP+*qO|)Q8>Qo#BOw1Qi9^LskqxMIVPETeTvd=~u?s)dW=YrvdJ2Jy11tMQKjd14^kuQ=)soe}hjA$C;bkl%p zMjGqvU$8?B`xi%hsrwg#ioI#PyJ9FR_Mv^%j)tM5lqR@4+7nAL(L}}3?|n%)cNE-b zRAU~*0yhnc?sf(vkRgdCI~gKf8B%CJHA56Kq|!8ZhG=9k({#0KF<5E_&2+kEM8z!H z->DeusyKjVs}6kvc7gzes#13rpvFj<+jBwt%n=_+xD0p-I|$I+q6{Sk&I0 z!6nW9#Dea+jjhI<#rEy}-`veb<_0>?$vo7RxslFSGY>=N1$3c1a~?7;qKnlo4@a~m zbg9$j5vceuUFK9A>8iM#u23tE;)>4@>kA;90Ww-v(X9fynpN#eV67`m#JU5779e9} zdv$Aou4PZ}C?(b=5VioxmkZFX1G=8U-bmK8u_v)^2ca3rSlM9R2B44dmCWFha{?*g z?rOWvZa%wxEP*vV8=LxXkI_=0n2d9!*a&nJuQ6KrIXwo#VIbpW^K_2_-ON{N>`aBo z^cc`BP9~-LBCLKZ&~5hW!>qDYv8z-Q&}Mh35|nBI%48{jYqrpzl=7e3?Lc<`3pkam zgeLINPN0vgLKAst7tq}pqP81}Nn)O0X{epMU_tdvIA3$&)wJVIeLB>l{$-$7?D|Uo>Ud{f2KowiCb(p;AE`$p-6qz{oG+vZCZ<0f#V(u` z@#n4N`rW1PE}WDTc;)kT9&f}Ox-+ywn9l!)#UB7XUj_OaJ_0D?+Th@4#}2xT>pa$a zVIT*5Q%&87(|Q%?TTX1n;Q2Bj^FEaAWtGJipx8B_Z=;xE_Ckjkw)8sC8+;p=b88Wr zeG}*{wd`V)eFx~fPFdwFUV?7F2lRcl+e^{y+dx0CyG_gd35bHmKfI6GQZRW&eVG@3 r*3y_Q%6q|Ner$QArlgsrrnW(vh;W`vh$qgoCX5(8Ohmv(DLwoPNkO2| diff --git a/docs/validation/series-cases/figures/polars-naca4412_n160_polar30_re1e6_m06_iter100.fig b/docs/validation/series-cases/figures/polars-naca4412_n160_polar30_re1e6_m06_iter100.fig index 36eae08fd8bd5b634a9564c957b4a99268cc46c7..ecad491e9def26725c89b4a9bac8f92054142e68 100644 GIT binary patch delta 7162 zcmcgw30zfG8s|IjJvm%J;3|sBCW0Oi5D+b#P*5{BTpnP7C?aBFhKA787@{beyYP{M zE2QFDs4q=xrm~u5YHDnm8k;t4Z!~P3%Ep|ty!&`Op8afc#@|mp=R5a(_rKilyXT&x zXUN6($wbYP5PxHQg1<-4ky_e>^bzUd$;pXHL&EdQitZ^ZD_c}jP*@-$dHE%YgA?-d z7tEhmP&$wgN{bSU2IuA9nZW-kE-NZcOh~Bkhnn=k6UNX^{v^g*^YZeLf4&&@UrmVa zF?Jy}VbiQq(?V*-C@6&j?$Zp0BJzL=xk%W%6C&48>dYLlmSP6F=h6HCrhJ-r-dxbQ^ zBnfvieixDqnG$Xm=7h$<6b0~B+lDX>H%HBKZ99AOS)Q6rv$gc$63ws!DZu_wUMYAYWj1nCf&@n+S30gSfs-D*^Uh0C@MLMGO`3PF0s_y3bYDOP5pu_1H_~Y=15u% z97_ZrtN`3QFv6Tg?*|UU$_J|edna4@lS9`4N3&au)&hnN+HFpz>w$wx;)4x(Y7I4~NF{m>l56FmR1)-aPgENB|)`RWy8`N-1TXZYfE~lvI_O|E_6*ZO7 zCTyHlP96kYl`84%mUQ+2UK}du?3Hx9Bk4XcY;H^q^5pVn(E|ptgMbxjlGsBIVh;l@ zN*BZqOJd&1^a$t!G_dFl8&jFc5KCdqED%)u;>}d=~=*nu@|@x&l$M?1h{8hHhErxHgh*#z}@5bk{2anQ!;%C zt1lF%@@Ji0v${#^L!zJsfzx;nzM~PGDu6bTfrC%CXs{SNY z^}Yi7r<`Z7eimw{k4E#f?PMvjx0fn{KTOLa%Za^hR1>V29vifh=zbc2PSp!DB8}(k zO7AClYI;|)+PHX)xHv|X+dAUtDC>!B;!IUo8{48aBH9TE66`%WkU!U?j^MeRo<7Tg zZX$Z627dZISdhD@k_R>$fnUn{I$ZA~-)Wp~8Qnr;*WOC7dr>*r&Sy~N_Ik%gE zpUE3+SK7MOhZ3Dq4}Xs!2#20T^N1ONiFNVwm`4(y4B>y%V2pYaJv@?_=A~I8N92!> z^ap==dANt$D~E*q>~b39nsq{9W}&r!T5woZPm_t6@m%dqfiqwZEP$D}TC*wD`Q7dv zHNSWsZY+($jrWIjwy!pTbrVo;JhD2)ImXiC zU_Aje04o+xHTf}rzQnL{b&RpBl6I9zb=Ji>X&qQ=fOf~m8mlRgb;HE9z0kk5hmReO zV9`^u2x2`1ix4LkAr2P)z(wxGg87X@oY^SI;SzEv3lot0s04GMlsj+_SU>Qly`@hg zg_BQn-Ky{KY2=gmYwphr_vNd}tbX4qPj&yL;y*Vf=l+8SZxNm(rJ)q;Ifn(XYjs{MY24=7c?hUlcq?6cW=cKBpvZaBU zEX$pNw@DUL*sZPxPIa)5HI>71-5WSvLe68;1Z3BmYV@J`(ixYUs&uPRV)5OPZ zL0R$ql7-i?0nXF|wSe13O&l{moRt>M#*8W-tX@0Jxrr3DCtS#8xe`|0YmQWUHoM(f zX=UFXQj20XS7_0`{z`;>_}+&89oykuGU0r-z&W9^u8do^%DDypR@zn0?iO|#3!;(# z4*Ot+aZ81!LZVs3?sMnNC%$wscZOVJ&7@$<%COPAQU1!8QIWNfE(JltT*>$cBWDq2 zFZ^8VZFD)bH=zpHGF;k}>Sw=Z8(15Ft^}c&xf<9CJhds)SUd{W7NDy^(8kpIJB|1g zV0|3uI{rbnc&d2~u(g<8A1!P!E>KxFs;ujQZE()I8LV|c5kwJLYJl-CPy4IM`1w|_ z*7I#1kJnDI)B)pPqKRvheeIBETW8S*$=I?PSbYb|+gvDb0k+kdvSPJUvNCT6wu7TD zj#SAV9q=<>+Q_%F+)kH5yGjd8LAw5W*eS^EQskO|@h@fYWb_3hvOin4k zUn##A*gj|F74t)i`2k=D1#?4mWonBu^&wynyEpZyN_`mE5$Dv()MLt28!-N@?bg&2 z%GBe)__w!u3RIl}wCd%)RZLp`gqkExzjv zBKN>z;T^H?7O=Nn7tV@>cY(dB`lw*{BsQeVablO~A zr9Sg9am#h^^Ai`%J_7b}N6r4JH2W0TXF@Y)(LZ-da30u&jtRa{3HY(>MOV?k7JR=1 z_LZ}QLiB%$g>Qg;>$>oRSojXu_s$DK^nZ(mOTd1-R?+Ld>183h`5KF8JcjQ`%Ki24 zI8jMUNc+b36R;~CgvWc+G0-W2Oylb62=Sv&J$Flhv-(wkAv1{OU z*p`U**ft`zC6+CO@goub9&?szr;AkE3FF5kE>bB^9#oz*64unt6D8yxCFCx`_~}Up z$M-46n+fA*DQ+D_EHh%JGMlG`?4B04-fh#f1Ic%w@%eci`y< Xqbz!sX4UXWLth`=fo&@qAU^bT? delta 6085 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 UCd8LNA3_u{_!x=EcTzg|FE<9aj{pDw diff --git a/docs/validation/series-cases/figures/polars-naca4412_n160_polar30_re1e6_ncrit11_iter100.fig b/docs/validation/series-cases/figures/polars-naca4412_n160_polar30_re1e6_ncrit11_iter100.fig index b1074f41a01cfbb6ab31adf2fffac208ec1525a7..1a97b9d42ac7f437c9cb1407edfdcdc7332a8cfc 100644 GIT binary patch delta 7055 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 BUl;%Y delta 5439 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 diff --git a/docs/validation/series-cases/figures/polars-naca4412_n160_polar30_re1e7_iter100.fig b/docs/validation/series-cases/figures/polars-naca4412_n160_polar30_re1e7_iter100.fig index b1794b3414aa7715f8b5b93e45d75038acf7ec58..4aeafd3068af0afcd2b454537a945d1de17457a2 100644 GIT binary patch delta 7110 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 Z6EB%fY%(FfqBEs%V)02Jvbf+F;J>+%Y+e8W diff --git a/docs/validation/series-cases/figures/polars-naca4412_n160_polar30_re3e6_iter100.fig b/docs/validation/series-cases/figures/polars-naca4412_n160_polar30_re3e6_iter100.fig index 4f205d7d6fa96c4cecdcb2e584a8b3fd9cc6a627..e5136343f6c3c23e0625d3346c0adaa983e277af 100644 GIT binary patch delta 7756 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 delta 6334 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 Au>b%7 diff --git a/docs/validation/series-cases/index.md b/docs/validation/series-cases/index.md index 95fdb3ec..5f97dce4 100644 --- a/docs/validation/series-cases/index.md +++ b/docs/validation/series-cases/index.md @@ -87,8 +87,8 @@ iteration of the point agrees with XFOIL's. It is hollow where the iterations pa where an earlier point in the same leg parted (that point started from a state XFOIL never computed, so its comparison is informational). -- An **orange square** marks a point where that code went through a non-finite value. An orange plus - at the top of a panel marks a value that is itself NaN and cannot be plotted. +- An **orange square** marks a point where that code went through a non-finite value. An orange tick + at the top edge of a panel marks a value that is itself NaN and cannot be plotted. - A **black diamond** marks where yFoil parts from XFOIL and XFOIL is ill-conditioned there (one of its own twins parts too). - A **grey diamond** marks where yFoil parts from XFOIL and XFOIL is well-conditioned (a mismatch). diff --git a/scripts/docs.sh b/scripts/docs.sh index 86531971..d12e8a4a 100755 --- a/scripts/docs.sh +++ b/scripts/docs.sh @@ -21,8 +21,8 @@ zensical_version="0.0.71" # The ironlab-web bundle that draws the embeds (docs/development/conventions/figures.md). # Pinned by version and checksum, fetched from the ironlab release into a versioned directory # (gitignored), so an upgrade changes every URL and no browser pairs a page with a stale engine. -ironlab_version="0.12.0" -ironlab_sha256="7f5d2a1225cbe4e699697347e2657ca4249784b0d4cc71514641f72d0dd0821b" +ironlab_version="0.16.0" +ironlab_sha256="ad1160b9052f555eb060390b3fbd04116448c10888f53eac69ecd8dbd2e67380" repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "$repo_root" diff --git a/src/output/figures.rs b/src/output/figures.rs index b033592b..15997cad 100644 --- a/src/output/figures.rs +++ b/src/output/figures.rs @@ -1,5 +1,5 @@ //! Shared conventions for yFoil's ironlab figures: the colours, the ordered-family colours that -//! replace a colourmap, the parameters and labels the ironlab viewer sorts, filters and groups +//! replace a colourmap, the parameters and tags the ironlab viewer sorts, filters and groups //! by, and saving. The rules are in `docs/development/conventions/figures.md`. //! //! Figure size and fonts are ironlab's defaults (set up for AMS journals). A figure sets @@ -106,24 +106,39 @@ pub enum FigureError { Ironlab { path: String, source: ironlab::Error }, } +/// Refuse a figure with validation errors, or whose layout leaves something off the page or +/// out of its legend: entries that do not fit, two entries of one legend with the same name and +/// different samples, or text beyond the page edge. The layout warnings are scene warnings, not +/// validation errors, so a figure with one would otherwise be written silently. fn check(fig: &Figure, path: &Path) -> Result<(), FigureError> { let report = fig.validate(); - if report.errors.is_empty() { + let mut errors: Vec = report.errors.iter().map(|e| format!("{e:?}")).collect(); + if errors.is_empty() { + errors.extend( + fig.layout_warnings() + .into_iter() + .filter(|w| { + matches!( + w.kind, + ironlab::SceneWarningKind::LegendOverflow { .. } + | ironlab::SceneWarningKind::LegendNameClash { .. } + | ironlab::SceneWarningKind::TextOutsidePage { .. } + ) + }) + .map(|w| w.message), + ); + } + if errors.is_empty() { return Ok(()); } Err(FigureError::Invalid { path: path.display().to_string(), - errors: report - .errors - .iter() - .map(|e| format!("{e:?}")) - .collect::>() - .join("; "), + errors: errors.join("; "), }) } /// Validate and save a figure; the encoding follows the extension (`.fig` or `.fig.json`). -/// A figure with validation errors is never written. +/// A figure that does not validate or does not lay out ([`check`]) is never written. pub fn save(fig: &Figure, path: &Path) -> Result<(), FigureError> { check(fig, path)?; fig.save(path).map_err(|source| FigureError::Ironlab { @@ -133,38 +148,14 @@ pub fn save(fig: &Figure, path: &Path) -> Result<(), FigureError> { } /// Validate and save `.fig` and, for the run folders' LaTeX reports, `.pdf`. -/// -/// Drawing the PDF is also the only check ironlab offers on layout: a legend whose entries do -/// not fit, or two entries of one legend with the same name and different samples, are scene -/// warnings, not validation errors. Either would be written silently (an entry clipped from -/// its legend), so either refuses the figure and both files are removed. pub fn save_with_pdf(fig: &Figure, stem: &Path) -> Result<(), FigureError> { let fig_path = stem.with_extension("fig"); check(fig, &fig_path)?; let pdf = stem.with_extension("pdf"); - let report = fig.export_pdf(&pdf).map_err(|source| FigureError::Ironlab { + fig.export_pdf(&pdf).map_err(|source| FigureError::Ironlab { path: pdf.display().to_string(), source, })?; - let layout: Vec = report - .scene - .iter() - .filter(|w| { - matches!( - w.kind, - ironlab::SceneWarningKind::LegendOverflow { .. } | ironlab::SceneWarningKind::LegendNameClash { .. } - ) - }) - .map(|w| w.message.clone()) - .collect(); - if !layout.is_empty() { - let _ = std::fs::remove_file(&pdf); - let _ = std::fs::remove_file(&fig_path); - return Err(FigureError::Invalid { - path: fig_path.display().to_string(), - errors: layout.join("; "), - }); - } save(fig, &fig_path) } diff --git a/src/output/foil_plot.rs b/src/output/foil_plot.rs index 943968c0..d573d374 100644 --- a/src/output/foil_plot.rs +++ b/src/output/foil_plot.rs @@ -12,19 +12,21 @@ //! XFOIL has no scale factor on δ* and marks no transition or separation on the aerofoil plot; the //! scaling and the markers are yFoil's. //! -//! The figure is an ironlab figure (`docs/development/conventions/figures.md`). ironlab has no equal-aspect -//! axes, so the limits are widened in Rust to the aspect of the plot area the layout gives -//! ([`PLOT_AREA_MM`]), which keeps the aerofoil undistorted at the figure's own size. +//! The figure is an ironlab figure (`docs/development/conventions/figures.md`), drawn at equal +//! aspect so the aerofoil and the offsets normal to it are undistorted at any size or zoom. Its key +//! is two legends: the curves, and the markers, whose shape is the kind of location and whose +//! colour is the side, each explained once. use std::collections::BTreeMap; use std::path::Path; use ironlab::prelude::*; +use ironlab::{LineStyle, MarkerStyle}; use crate::geometry::PanelledFoil; use crate::output::plot::{ - condition_parameters, describe, point_parameters, regime_labels, rgb, save_figure, shared_labels, - shared_parameters, PlotError, STUDY, + condition_parameters, describe, point_parameters, regime_tags, rgb, save_figure, shared_parameters, shared_tags, + PlotError, STUDY, }; use crate::output::{ AnalysisOutput, BlQuantity, BoundaryLayerOutput, FoilNodes, PointRecord, PolarOutput, SeparationKind, SideStations, @@ -90,8 +92,15 @@ impl Default for MarkerSet { } } -/// The dash patterns cycled over design points: ironlab's four stroked patterns -pub const DASH_CYCLE: [Dash; 4] = [Dash::Solid, Dash::Dotted, Dash::Dashed, Dash::DashDot]; +/// The dash patterns cycled over design points: ironlab's six stroked patterns +pub const DASH_CYCLE: [Dash; 6] = [ + Dash::Solid, + Dash::Dotted, + Dash::Dashed, + Dash::DashDot, + Dash::LongDash, + Dash::DashDotDot, +]; /// Configuration of the foil figure #[derive(Debug, Clone)] @@ -396,6 +405,8 @@ const MARK_LOWER: (u8, u8, u8) = (0, 165, 0); const MARK_STAGNATION: (u8, u8, u8) = (40, 40, 40); /// Marker size (pt) const MARK_SIZE_PT: f64 = 5.0; +/// The marker key's shapes, which stand for the kind of location whatever its side +const MARK_KEY: (u8, u8, u8) = (128, 128, 128); /// The kinds of significant location, each with its own open shape #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] @@ -447,17 +458,6 @@ struct Mark { rgb: (u8, u8, u8), } -impl Mark { - /// The key entry of a (kind, side): its shape in its side's colour - fn key(&self) -> String { - match self.side { - 1 => format!("{} (upper surface)", self.kind.label()), - 2 => format!("{} (lower surface)", self.kind.label()), - _ => self.kind.label().to_string(), - } - } -} - /// Everything laid out in data coordinates before the figure exists #[derive(Debug)] struct Layout { @@ -702,30 +702,6 @@ fn layout(points: &[DesignPoint], config: &FoilPlotConfig) -> Result ((f64, f64), (f64, f64)) { - let (x0, x1, y0, y1) = bounds; - let (dx, dy) = (x1 - x0, y1 - y0); - let aspect = area.1 / area.0; - if dy / dx < aspect { - let half = 0.5 * dx * aspect; - let c = 0.5 * (y0 + y1); - ((x0, x1), (c - half, c + half)) - } else { - let half = 0.5 * dy / aspect; - let c = 0.5 * (x0 + x1); - ((c - half, c + half), (y0, y1)) - } -} - /// The parameters of the foil figure: provenance, and for one design point its inputs and /// outputs; for several, the conditions they share fn foil_parameters(points: &[DesignPoint]) -> (BTreeMap, Vec<&'static str>) { @@ -753,11 +729,44 @@ fn foil_parameters(points: &[DesignPoint]) -> (BTreeMap, Vec< parameters.extend(point_parameters(record, p.boundary_layer.as_ref())); } } - let labels: Vec> = points + let tags: Vec> = points .iter() - .map(|p| p.conditions.as_ref().map(regime_labels).unwrap_or_default()) + .map(|p| p.conditions.as_ref().map(regime_tags).unwrap_or_default()) .collect(); - (parameters, shared_labels(&labels)) + (parameters, shared_tags(&tags)) +} + +/// The marker key: one open grey shape per kind of location drawn, then one swatch per side +/// that has a marker +fn marker_key(markers: &[Mark]) -> Vec<(LegendSample, String)> { + let mut kinds: Vec = markers.iter().map(|m| m.kind).collect(); + kinds.sort(); + kinds.dedup(); + let mut key: Vec<(LegendSample, String)> = kinds + .into_iter() + .map(|kind| { + let sample = LegendSample::Line { + line: LineStyle { + color: ColorSpec::Rgba { color: rgb(MARK_KEY) }, + dash: Dash::None, + ..LineStyle::default() + }, + marker: MarkerStyle { + shape: kind.shape(), + size_pt: MARK_SIZE_PT, + face: ColorSpec::None, + edge: ColorSpec::Auto, + }, + }; + (sample, kind.label().to_string()) + }) + .collect(); + for (side, colour, name) in [(1, MARK_UPPER, "upper surface"), (2, MARK_LOWER, "lower surface")] { + if markers.iter().any(|m| m.side == side) { + key.push((LegendSample::swatch(rgb(colour)), name.to_string())); + } + } + key } /// Build the foil figure @@ -765,15 +774,13 @@ pub fn foil_figure(points: &[DesignPoint], config: &FoilPlotConfig) -> Result Result fig, + 1 => fig.tiles(2, 1).fit_row(1), + _ => fig.tiles(2, 2).fit_row(1), + }; + let (parameters, tags) = foil_parameters(points); + fig = describe(fig, parameters, &tags); + let curve_key = curves_named.then(|| fig.panel(1, 0).legend().id()); + if markers_keyed { + let mut panel = fig.panel(1, if curves_named { 1 } else { 0 }); + let mut legend = panel.legend(); + for (sample, name) in marker_key(&lay.markers) { + legend.custom_item(sample, name); + } + legend.columns(LegendEntryColumns::Auto); } - let (parameters, labels) = foil_parameters(points); - fig = describe(fig, parameters, &labels); - let key = named.then(|| fig.panel_legend(1, 0).id()); - let mut ax = fig.axes(0, 0); + let mut ax = if keys == 2 { + fig.axes_span(0, 0, 1, 2) + } else { + fig.axes(0, 0) + }; let draw = |ax: &mut AxesMut, c: &Curve, width: f64| { let (x, y): (Vec, Vec) = c.pts.iter().copied().unzip(); let mut line = ax.plot(x, y); line.color(rgb(c.rgb)).dash(c.dash).line_width(width); if let Some(name) = &c.name { - line.display_name(name.as_str()); + line.label(name.as_str()); } }; for c in &lay.surfaces { @@ -826,26 +851,23 @@ pub fn foil_figure(points: &[DesignPoint], config: &FoilPlotConfig) -> Result = ir.axes[0] + assert_eq!(ir.axes[0].aspect, Aspect::Equal); + assert_eq!(ir.panels.len(), 2, "a curve key and a marker key"); + let series: Vec<_> = ir.axes[0] .artists .iter() .filter_map(|a| match a { - Artist::Line(l) => Some(l), + Artist::Series(s) => Some(s), _ => None, }) .collect(); // surface + dots + (upper, lower) + stagnation, transition, forced transition - assert_eq!(lines.len(), 1 + 1 + 2 + 3); - let names: Vec<&str> = lines + assert_eq!(series.len(), 1 + 1 + 2 + 3); + let names: Vec<&str> = series .iter() - .filter_map(|l| l.display_name.as_ref().map(|t| t.content.as_str())) + .filter_map(|l| l.label.as_ref().map(|t| t.content.as_str())) .collect(); - assert_eq!(names.len(), 4); + assert_eq!(names.len(), 1, "only the curve is named; the markers are keyed once"); assert!(names[0].starts_with("$\\delta^*$, scaled ×")); + let shapes: Vec = series.iter().map(|l| l.marker.shape).collect(); + assert_eq!(shapes[1], Marker::Point); + assert_eq!(&shapes[4..], [Marker::Circle, Marker::Square, Marker::Diamond]); + let key: Vec = marker_key(&layout(&pts, &config).unwrap().markers) + .into_iter() + .map(|(_, name)| name) + .collect(); assert_eq!( - &names[1..], + key, [ "stagnation", - "transition (upper surface)", - "forced transition (lower surface)" + "transition", + "forced transition", + "upper surface", + "lower surface" ] ); - let shapes: Vec = lines.iter().map(|l| l.marker.shape).collect(); - assert_eq!(shapes[1], Marker::Point); - assert_eq!(&shapes[4..], [Marker::Circle, Marker::Square, Marker::Diamond]); assert_eq!(fig.parameters().get("figure"), Some(&Parameter::from("foil"))); assert_eq!(fmt_alpha(3.0000000000000004), "3"); assert_eq!(fmt_alpha(-2.5), "-2.5"); diff --git a/src/output/mod.rs b/src/output/mod.rs index 2cead47d..b93e379c 100644 --- a/src/output/mod.rs +++ b/src/output/mod.rs @@ -29,9 +29,8 @@ pub use results::{ #[cfg(feature = "plotting")] pub use foil_plot::{ - check_same_geometry, equal_aspect_limits, foil_figure, plot_foil, point_style, quantity_symbol, scale_factors, - surface_offset, wake_band, CurveStyle, DesignPoint, FoilPlotConfig, MarkerSet, OffsetScale, PanelStyle, Pt, - DASH_CYCLE, PLOT_AREA_MM, + check_same_geometry, foil_figure, plot_foil, point_style, quantity_symbol, scale_factors, surface_offset, + wake_band, CurveStyle, DesignPoint, FoilPlotConfig, MarkerSet, OffsetScale, PanelStyle, Pt, DASH_CYCLE, }; #[cfg(feature = "plotting")] pub use plot::{ diff --git a/src/output/plot.rs b/src/output/plot.rs index 91c9e77e..735e87b0 100644 --- a/src/output/plot.rs +++ b/src/output/plot.rs @@ -1,5 +1,5 @@ //! The `yfoil plot analysis` and `yfoil plot polar` figures, and what the three `yfoil plot` -//! figures share: their error type and the parameters and labels the ironlab viewer sorts, +//! figures share: their error type and the parameters and tags the ironlab viewer sorts, //! filters and groups by (`docs/development/conventions/figures.md`). The foil figure is in `foil_plot.rs`. //! //! Every figure is an ironlab figure, written as `.fig` (or `.fig.json`) through @@ -43,7 +43,7 @@ pub fn save_figure(fig: &Figure, path: &Path) -> Result<(), PlotError> { } // ============================================================================ -// Parameters and labels +// Parameters and tags // ============================================================================ /// The flow conditions as figure parameters (`figures::flow_condition_parameters`), as a map @@ -53,21 +53,21 @@ pub(crate) fn condition_parameters(conditions: &FlowConditions) -> BTreeMap Vec<&'static str> { - let mut labels = Vec::new(); +/// The regime tags of a set of flow conditions (`docs/development/conventions/figures.md`, "Tags") +pub(crate) fn regime_tags(conditions: &FlowConditions) -> Vec<&'static str> { + let mut tags = Vec::new(); match conditions.re { - None => labels.push("inviscid"), - Some(re) if re < 1e5 => labels.push("low-re"), + None => tags.push("inviscid"), + Some(re) if re < 1e5 => tags.push("low-re"), Some(_) => {} } if conditions.mach > 0.0 { - labels.push("compressible"); + tags.push("compressible"); } if conditions.re.is_some() && conditions.x_trip.iter().any(|&x| x < 1.0) { - labels.push("forced-transition"); + tags.push("forced-transition"); } - labels + tags } /// The parameters of one operating point: its inputs (`alpha_deg`) and outputs (forces, @@ -125,8 +125,8 @@ pub(crate) fn shared_parameters(sets: &[BTreeMap]) -> BTreeMa .collect() } -/// The labels every list holds -pub(crate) fn shared_labels(sets: &[Vec<&'static str>]) -> Vec<&'static str> { +/// The tags every list holds +pub(crate) fn shared_tags(sets: &[Vec<&'static str>]) -> Vec<&'static str> { let Some((first, rest)) = sets.split_first() else { return Vec::new(); }; @@ -137,13 +137,13 @@ pub(crate) fn shared_labels(sets: &[Vec<&'static str>]) -> Vec<&'static str> { .collect() } -/// Set a figure's parameters and labels -pub(crate) fn describe(mut fig: Figure, parameters: BTreeMap, labels: &[&str]) -> Figure { +/// Set a figure's parameters and tags +pub(crate) fn describe(mut fig: Figure, parameters: BTreeMap, tags: &[&str]) -> Figure { for (name, value) in parameters { fig = fig.parameter(name, value); } - for label in labels { - fig = fig.label(*label); + for tag in tags { + fig = fig.tag(*tag); } fig } @@ -198,11 +198,12 @@ impl Default for AnalysisPlotConfig { /// Height of the analysis figure (mm): two distribution panels over a strip with the aerofoil const ANALYSIS_HEIGHT_MM: f64 = 120.0; -/// The analysis figure: −Cp (top) and Ue/U∞ (middle) over x/c, upper surface blue and lower -/// red, over a strip with the aerofoil outline; x is linked across the three. +/// The analysis figure: Cp (top, on a reversed axis so suction is up, the aerodynamic +/// convention) and Ue/U∞ (middle) over x/c, upper surface blue and lower red, over a strip with +/// the aerofoil outline; x is linked across the three. /// -/// Cp is drawn as −Cp because ironlab has no reversed axis (the aerodynamic convention puts -/// suction up). +/// The outline is a tile of its own rather than an inset under the Cp curve: ironlab has no inset +/// axes or second y axis, by decision (thclark/ironlab#80). pub fn analysis_figure(analysis: &AnalysisOutput, config: &AnalysisPlotConfig) -> Result { let (Some((x_upper, cp_upper, q_upper)), Some((x_lower, cp_lower, q_lower))) = (analysis.upper_surface(), analysis.lower_surface()) @@ -212,14 +213,11 @@ pub fn analysis_figure(analysis: &AnalysisOutput, config: &AnalysisPlotConfig) - analysis.foil ))); }; - let minus = |v: &[f64]| v.iter().map(|c| -c).collect::>(); - let (minus_cp_upper, minus_cp_lower) = (minus(&cp_upper), minus(&cp_lower)); - let g = &analysis.geometry; let chord = g.chord; let (x_lo, x_hi) = padded_range(g.x.iter(), 0.0, 0.0); let xlim = (x_lo - 0.05 * chord, x_hi + 0.1 * chord); - let cp_lim = padded_range(minus_cp_upper.iter().chain(&minus_cp_lower), 0.1, 1e-3); + let cp_lim = padded_range(cp_upper.iter().chain(&cp_lower), 0.1, 1e-3); let q_lim = padded_range(q_upper.iter().chain(&q_lower), 0.1, 1e-3); let y_lim = padded_range(g.y.iter(), 0.1, 1e-3); @@ -240,21 +238,23 @@ pub fn analysis_figure(analysis: &AnalysisOutput, config: &AnalysisPlotConfig) - .tiles(3, 1) .row_weights([3.0, 3.0, 1.0]) .title(Text::plain(title)); - fig = describe(fig, parameters, ®ime_labels(&analysis.conditions)); + fig = describe(fig, parameters, ®ime_tags(&analysis.conditions)); let (upper, lower) = (rgb(config.upper_color), rgb(config.lower_color)); let mut ax = fig.axes(0, 0); - ax.plot(&x_upper, &minus_cp_upper) - .display_name("Upper surface") + ax.plot(&x_upper, &cp_upper) + .label("Upper surface") .color(upper) .line_width(1.5); - ax.plot(&x_lower, &minus_cp_lower) - .display_name("Lower surface") + ax.plot(&x_lower, &cp_lower) + .label("Lower surface") .color(lower) .line_width(1.5); - ax.ylabel("$-C_p$") + ax.ylabel("$C_p$") .xlim(xlim.0, xlim.1) .ylim(cp_lim.0, cp_lim.1) + .yreverse(true) + .xtick_labels_hidden() .grid(true) .legend(LegendLocation::NorthEast); let cp_axes = ax.id(); @@ -265,6 +265,7 @@ pub fn analysis_figure(analysis: &AnalysisOutput, config: &AnalysisPlotConfig) - ax.ylabel("$U_e/U_\\infty$") .xlim(xlim.0, xlim.1) .ylim(q_lim.0, q_lim.1) + .xtick_labels_hidden() .grid(true); let ue_axes = ax.id(); @@ -453,19 +454,19 @@ pub fn polars_figure(series: &[PolarSeries], config: &PolarsPlotConfig) -> Resul } } } - let labels = shared_labels(&series.iter().map(|s| regime_labels(&s.conditions)).collect::>()); + let tags = shared_tags(&series.iter().map(|s| regime_tags(&s.conditions)).collect::>()); - // the key: up to four series to a row, its row sized to the rows of entries + // the key: up to four series to a row, in a row sized to its entries let key_columns = series.len().min(4); - let key_rows = series.len().div_ceil(4); let mut fig = Figure::new() .size_mm(figures::WIDTH_MM, POLAR_HEIGHT_MM) .tiles(3, 2) - .row_weights([3.0, 3.0, 0.4 + 0.5 * key_rows as f64]) + .fit_row(2) .title(Text::plain(title)); - fig = describe(fig, parameters, &labels); + fig = describe(fig, parameters, &tags); let key = fig - .panel_legend_span(2, 0, 1, 2) + .panel_span(2, 0, 1, 2) + .legend() .columns(LegendEntryColumns::Count(key_columns as u32)) .order(LegendEntryOrder::RowMajor) .id(); @@ -487,7 +488,7 @@ pub fn polars_figure(series: &[PolarSeries], config: &PolarsPlotConfig) -> Resul let marker = config.markers[i % config.markers.len()]; let (x, y) = extract(s); let mut line = ax.plot(x, y); - line.display_name(Text::plain(s.label.clone())) + line.label(Text::plain(s.label.clone())) .color(color) .line_width(1.0) .marker(marker.shape) @@ -545,7 +546,7 @@ mod tests { assert!(shared.contains_key("mach")); assert_eq!(shared_parameters(std::slice::from_ref(&a)), a); assert_eq!( - shared_labels(&[vec!["inviscid", "compressible"], vec!["compressible"]]), + shared_tags(&[vec!["inviscid", "compressible"], vec!["compressible"]]), vec!["compressible"] ); } diff --git a/studies/README.md b/studies/README.md index 15bb8418..b8a3d5fd 100644 --- a/studies/README.md +++ b/studies/README.md @@ -23,5 +23,4 @@ Each study writes a dated run folder under its own `runs/` (gitignored), and nev | `xfoil-instrumentation-check/` | The instrumentation is inert on a polar through stall: pristine and instrumented builds compared byte for byte on XFOIL's own `PACC` and `DUMP` output | `cargo run --release -p xfoil-instrumentation-check` | its run folder only | Shared support, not studies: `study-support/records.rs` is the studies' whole-run floor comparison -(used by `branch-coverage` and `series-cases`), `study-support/publish.rs` writes a run's `publish/`, and `figures/` (`render.sh`, `style.py`) draws the -branch-coverage study's two matplotlib figures (needs [`uv`](https://docs.astral.sh/uv/)). +(used by `branch-coverage` and `series-cases`) and `study-support/publish.rs` writes a run's `publish/`. diff --git a/studies/aerofoil-series/Cargo.toml b/studies/aerofoil-series/Cargo.toml index 3e610775..7ce7707a 100644 --- a/studies/aerofoil-series/Cargo.toml +++ b/studies/aerofoil-series/Cargo.toml @@ -11,6 +11,6 @@ workspace = true [dependencies] yfoil = { path = "../..", default-features = false, features = ["plotting"] } -ironlab = "=0.12.0" +ironlab = "=0.16.0" cr-libm = { path = "../../cr-libm" } serde_json = { version = "1.0", features = ["float_roundtrip"] } diff --git a/studies/aerofoil-series/src/sheet.rs b/studies/aerofoil-series/src/sheet.rs index 04d43498..bcc2030d 100644 --- a/studies/aerofoil-series/src/sheet.rs +++ b/studies/aerofoil-series/src/sheet.rs @@ -3,22 +3,18 @@ //! rerunning the study (`--render `). //! //! Layout: two columns of panels, one per parameter, each panel followed in the row below by its -//! own legend panel (row-major; the default's entry is last, because ironlab lists entries in -//! drawing order and the default is drawn on top). Every panel has the same linked axes -//! (`X_RANGE` × `Y_RANGE`); the members are drawn 0.75 pt in `figures::ylgnbu(i / (n − 1))` -//! (already ordered by parameter value), the family default black 1.5 pt on top. ironlab has no -//! equal-aspect axes, so the figure height and the row weights are chosen here so that the plot -//! areas come out close to equal scale at the default width. -//! -//! A legend has three columns of entries where they fit the panel's width and fewer where they do -//! not (a long default label, such as the Kármán–Trefftz one): the sheet is laid out, compiled by -//! ironlab, and any legend ironlab reports as overflowing is given one column fewer and a taller -//! row, until none overflows. +//! own legend panel. Every panel has the same linked axes (`X_RANGE` × `Y_RANGE`) at equal aspect +//! with the same ticks (`X_TICKS`, `Y_TICKS`), titled by the parameter it varies, aligned with its +//! plot area's left edge. The parameters it holds fixed title its legend: a panel title in full +//! ("position of maximum camber p (m = 0.04, t = 0.12)") is wider than a column at ironlab's +//! default font, and ironlab sets text on one line. +//! The members are drawn 0.75 pt in `figures::ylgnbu(i / (n − 1))` (already ordered by parameter +//! value), the family default black 1.5 pt on top. Each legend lists the default first, on a row of +//! its own, then the members in as many columns as fit the panel's width, in a row fitted to them. use std::path::{Path, PathBuf}; use ironlab::prelude::*; -use ironlab::SceneWarningKind; use serde_json::Value; use yfoil::output::figures; @@ -26,19 +22,14 @@ use crate::{X_RANGE, Y_RANGE}; /// Columns of panels const COLUMNS: usize = 2; -/// Columns of entries in a legend, where they fit -const LEGEND_COLUMNS: usize = 3; - -// Heights (mm) from which the figure height and the row weights are set. The plot area of a panel -// is the column width less the space ironlab reserves for the tick labels and the y label -// (PLOT_AREA_WIDTH_MM); its height follows from the equal-scale aspect, and the plot row adds the -// title above and the x tick labels and label below. Measured on an export at ironlab's defaults -// (160 mm wide, 9 pt). -const PLOT_AREA_WIDTH_MM: f64 = 63.5; -const PLOT_ROW_EXTRA_MM: f64 = 16.6; -const LEGEND_ENTRY_ROW_MM: f64 = 4.0; -const LEGEND_PAD_MM: f64 = 2.5; -const TITLE_MM: f64 = 8.0; +/// The ticks of every panel +const X_TICKS: [f64; 8] = [-0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2]; +const Y_TICKS: [f64; 5] = [-0.4, -0.2, 0.0, 0.2, 0.4]; +/// The nominal height (mm) of a row of panels with its legend. The legend row is fitted to its +/// entries and the plot takes the rest; equal aspect then sizes each plot area within it. +const ROW_MM: f64 = 95.0; +/// The figure title (mm) +const TITLE_MM: f64 = 10.0; /// A geometry file's coordinates fn coordinates(path: &Path) -> (Vec, Vec) { @@ -58,38 +49,31 @@ fn f64_of(v: &Value, key: &str) -> f64 { .unwrap_or_else(|| panic!("summary.json: missing number {key}")) } +/// A panel title split into the parameter it varies and the ones it holds fixed: +/// "maximum camber m (p = 0.4, t = 0.12)" → ("maximum camber m", "p = 0.4, t = 0.12") +fn split_title(title: &str) -> (&str, Option<&str>) { + match title.rsplit_once(" (") { + Some((varied, held)) if held.ends_with(')') => (varied, Some(&held[..held.len() - 1])), + _ => (title, None), + } +} + fn members(panel: &Value) -> &Vec { panel["members"].as_array().expect("summary.json: members") } -/// The family sheet of one `summary.json` series entry, with `legend_columns[k]` columns of -/// entries in panel k's legend; returns the figure and the legend panels' ids -pub fn family_sheet(run_dir: &Path, series: &Value, n_nodes: i64, legend_columns: &[usize]) -> (Figure, Vec) { +/// The family sheet of one `summary.json` series entry +pub fn family_sheet(run_dir: &Path, series: &Value, n_nodes: i64) -> Figure { let slug = str_of(series, "slug"); let panels = series["panels"].as_array().expect("summary.json: panels"); let default = &series["default"]; let rows = panels.len().div_ceil(COLUMNS); - // row weights: a plot row, then the legend row its panels' legends need - let plot_area_h = PLOT_AREA_WIDTH_MM * (Y_RANGE.1 - Y_RANGE.0) / (X_RANGE.1 - X_RANGE.0); - let plot_row = plot_area_h + PLOT_ROW_EXTRA_MM; - let mut weights = Vec::with_capacity(2 * rows); - for r in 0..rows { - let entry_rows = (r * COLUMNS..((r + 1) * COLUMNS).min(panels.len())) - .map(|k| (members(&panels[k]).len() + 1).div_ceil(legend_columns[k])) - .max() - .unwrap_or(1); - weights.push(plot_row); - weights.push(entry_rows as f64 * LEGEND_ENTRY_ROW_MM + 2.0 * LEGEND_PAD_MM); - } - let height = weights.iter().sum::() + TITLE_MM; - let mut fig = Figure::new() - .size_mm(figures::WIDTH_MM, height) + .size_mm(figures::WIDTH_MM, rows as f64 * ROW_MM + TITLE_MM) .tiles((2 * rows) as u32, COLUMNS as u32) - .row_weights(weights) .title(Text::plain(str_of(series, "title"))) - .label("validation") + .tag("validation") .parameter("study", "aerofoil-series") .parameter("figure", "family-sheet") .parameter("series", slug) @@ -97,90 +81,72 @@ pub fn family_sheet(run_dir: &Path, series: &Value, n_nodes: i64, legend_columns .parameter("designation", str_of(default, "designation")) .parameter("max_thickness", f64_of(default, "max_thickness")) .parameter("te_gap", f64_of(default, "te_gap")); + for r in 0..rows { + fig = fig.fit_row((2 * r + 1) as u32); + } let (default_x, default_y) = coordinates(&run_dir.join(slug).join(str_of(default, "file"))); let default_label = format!("{} (default)", str_of(default, "label")); let mut axes = Vec::with_capacity(panels.len()); - let mut legends = Vec::with_capacity(panels.len()); for (k, panel) in panels.iter().enumerate() { let (r, c) = ((k / COLUMNS) as u32, (k % COLUMNS) as u32); - let key = { - let mut legend = fig.panel_legend(2 * r + 1, c); - legend - .columns(LegendEntryColumns::Count(legend_columns[k] as u32)) - .order(LegendEntryOrder::RowMajor) - .align(LegendLocation::North) - .boxed(false); - legend.id() - }; let members = members(panel); let span = (members.len().max(2) - 1) as f64; let mut ax = fig.axes(2 * r, c); + let mut ids = Vec::with_capacity(members.len()); for (i, m) in members.iter().enumerate() { let (x, y) = coordinates(&run_dir.join(slug).join(str_of(panel, "slug")).join(str_of(m, "file"))); - ax.plot(&x, &y) - .display_name(Text::plain(str_of(m, "label"))) + let id = ax + .plot(&x, &y) + .label(Text::plain(str_of(m, "label"))) .color(figures::ylgnbu(i as f64 / span)) - .line_width(0.75); + .line_width(0.75) + .id(); + ids.push(id); } // the default last, so that it is drawn on top - ax.plot(&default_x, &default_y) - .display_name(Text::plain(default_label.clone())) + let default_id = ax + .plot(&default_x, &default_y) + .label(Text::plain(default_label.clone())) .color(Color::rgb(0.0, 0.0, 0.0)) - .line_width(1.5); - ax.title(Text::plain(str_of(panel, "title"))) + .line_width(1.5) + .id(); + let (varied, held) = split_title(str_of(panel, "title")); + ax.title(Text::plain(varied)) + .title_align(TitleAlign::Left) .xlabel("$x/c$") .ylabel("$y/c$") .xlim(X_RANGE.0, X_RANGE.1) .ylim(Y_RANGE.0, Y_RANGE.1) - .legend_in(key); + .xticks(X_TICKS) + .yticks(Y_TICKS) + .aspect(Aspect::Equal) + .aspect_adjust(AspectAdjust::Box); axes.push(ax.id()); - legends.push(key); + // listed first, on a row of its own, then the members + let key = { + let mut panel = fig.panel(2 * r + 1, c); + panel.align(PanelLocation::NorthWest); + if let Some(held) = held { + panel.title(Text::plain(held)).title_align(TitleAlign::Left); + } + let mut legend = panel.legend(); + legend.full_row(default_id); + for id in ids { + legend.item(id); + } + legend + .columns(LegendEntryColumns::Auto) + .order(LegendEntryOrder::RowMajor) + .boxed(false); + legend.id() + }; + fig.axes(2 * r, c).legend_in(key); } // the same x/c and y/c on every panel fig.link(Dim::X, &axes).expect("the panels belong to this figure"); fig.link(Dim::Y, &axes).expect("the panels belong to this figure"); - (fig, legends) -} - -/// The legends of a figure that ironlab reports as overflowing, compiling it through a PDF export -/// to `probe` -fn overflowing(fig: &Figure, probe: &Path) -> Vec { - let report = fig - .export_pdf(probe) - .unwrap_or_else(|e| panic!("{}: {e}", probe.display())); - report - .scene - .iter() - .filter(|w| matches!(w.kind, SceneWarningKind::LegendOverflow { .. })) - .filter_map(|w| w.node) - .collect() -} - -/// Draw one series' sheet to `.fig` and `.pdf`, with as many legend columns (up to -/// three) as fit -fn draw(run_dir: &Path, series: &Value, n_nodes: i64, stem: &Path) { - let n_panels = series["panels"].as_array().expect("summary.json: panels").len(); - let mut columns = vec![LEGEND_COLUMNS; n_panels]; - let probe = stem.with_extension("pdf"); - loop { - let (fig, legends) = family_sheet(run_dir, series, n_nodes, &columns); - let over = overflowing(&fig, &probe); - if over.is_empty() { - figures::save_with_pdf(&fig, stem).unwrap_or_else(|e| panic!("{e}")); - return; - } - for (k, id) in legends.iter().enumerate() { - if over.contains(id) { - assert!( - columns[k] > 1, - "{}: panel {k}'s legend overflows in one column", - stem.display() - ); - columns[k] -= 1; - } - } - } + fig } /// Draw every family sheet of a run folder (`.fig` and `.pdf`); returns the stems @@ -197,7 +163,7 @@ pub fn render(run_dir: &Path) -> Vec { .iter() .map(|s| { let stem = run_dir.join(str_of(s, "slug")); - draw(run_dir, s, n_nodes, &stem); + figures::save_with_pdf(&family_sheet(run_dir, s, n_nodes), &stem).unwrap_or_else(|e| panic!("{e}")); stem }) .collect() @@ -205,6 +171,15 @@ pub fn render(run_dir: &Path) -> Vec { #[cfg(test)] mod tests { + #[test] + fn titles_split_into_the_varied_and_the_held_parameters() { + assert_eq!( + super::split_title("position of maximum camber p (m = 0.04, t = 0.12)"), + ("position of maximum camber p", Some("m = 0.04, t = 0.12")) + ); + assert_eq!(super::split_title("family"), ("family", None)); + } + use ironlab::ir::Parameter; use ironlab::prelude::*; @@ -235,7 +210,7 @@ mod tests { Some(&Parameter::String(s.default.designation.clone())), "{file}" ); - assert!(fig.labels().iter().any(|l| l == "validation"), "{file}"); + assert!(fig.tags().iter().any(|l| l == "validation"), "{file}"); assert!(fig.validate().errors.is_empty(), "{file}"); assert_eq!(fig.ir().axes.len(), s.panels.len(), "{file}: one panel per parameter"); assert!( diff --git a/studies/aerofoil-series/src/study.rs b/studies/aerofoil-series/src/study.rs index 1f9e371f..2829fb20 100644 --- a/studies/aerofoil-series/src/study.rs +++ b/studies/aerofoil-series/src/study.rs @@ -120,7 +120,7 @@ pub fn series() -> Vec { ), panel( "camber-position", - "maximum-camber position p (m = 0.04, t = 0.12)", + "position of maximum camber p (m = 0.04, t = 0.12)", (1..=9) .map(|p| naca(&format!("4{p}12"), p as f64 / 10.0, format!("p = 0.{p}"))) .collect(), @@ -155,7 +155,7 @@ pub fn series() -> Vec { ), panel( "max-thickness-position", - "maximum-thickness position M (I = 3, t = 0.12)", + "position of maximum thickness M (I = 3, t = 0.12)", (2..=6) .map(|m| naca(&format!("0012-3{m}"), m as f64 / 10.0, format!("M = 0.{m}"))) .collect(), @@ -189,7 +189,7 @@ pub fn series() -> Vec { ), panel( "camber-position", - "maximum-camber position p (cl = 0.3, t = 0.18)", + "position of maximum camber p (cl = 0.3, t = 0.18)", (1..=5) .map(|p| naca(&format!("2{p}018"), p as f64 / 20.0, format!("p = {:.2}", p as f64 / 20.0))) .collect(), @@ -253,7 +253,7 @@ pub fn series() -> Vec { panels: vec![ panel( "family", - "family: minimum-pressure position (cl = 0.4, t = 0.15)", + "family: position of minimum pressure (cl = 0.4, t = 0.15)", (3..=7) .map(|f| naca(&format!("6{f}-415"), f as f64 / 10.0, format!("6{f}"))) .collect(), diff --git a/studies/branch-coverage/Cargo.toml b/studies/branch-coverage/Cargo.toml index 02bb69ca..750bbad6 100644 --- a/studies/branch-coverage/Cargo.toml +++ b/studies/branch-coverage/Cargo.toml @@ -10,7 +10,8 @@ description = "Branch-coverage study: the minimal case set covering every reacha workspace = true [dependencies] -yfoil = { path = "../..", default-features = false } +yfoil = { path = "../..", default-features = false, features = ["plotting"] } +ironlab = "=0.16.0" serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["float_roundtrip"] } toml = "0.8" diff --git a/studies/branch-coverage/plot.py b/studies/branch-coverage/plot.py deleted file mode 100644 index 8833a898..00000000 --- a/studies/branch-coverage/plot.py +++ /dev/null @@ -1,195 +0,0 @@ -#!/usr/bin/env python3 -"""Draw the branch-coverage study's figures from a run folder's summary.json. - -Presentation only: `branch_map` lists every analysis-path subroutine with the status of each of -its branches, `matrix` the fraction of each subroutine's branches every case of the minimal set -takes. Run via studies/figures/render.sh branch-coverage . -""" -import json -import sys -from pathlib import Path - -sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "figures")) -import style # noqa: E402 -from matplotlib import colormaps # noqa: E402 -from matplotlib.patches import Rectangle # noqa: E402 - -STATUS_COLOUR = { - "cover": "#2ca02c", # taken by the finite set - "nonfinite": "#1f77b4", # taken by the non-finite set - "pathological": "#ff7f0e", # taken only in pathological cases - "unreachable": "#9a9a9a", # annotated unreachable - "loop_entry": "#9a9a9a", # never-taken DO-line edge: not reachable either - "open": "#d62728", # not taken -} -STATUS_LABEL = { - "cover": "taken by the finite set", - "nonfinite": "taken by the non-finite set", - "pathological": "taken only in pathological cases", - "unreachable": "not reachable", - "open": "not taken", -} -CELL_PT = 3.3 -ROW_PT = 5.4 -LABEL_PT = 78.0 -LEGEND_ROW_PT = 11.0 -LEGEND_ROWS = 2 -MARGIN_PT = 4.0 -FILE_GAP_PT = 3.0 - - -def branch_map(run_dir: Path, s: dict) -> None: - rows = [r for r in s["branch_map"] if len(r["statuses"]) > 0] - files = [] - for r in rows: - if r["file"] not in files: - files.append(r["file"]) - legend = LEGEND_ROW_PT * LEGEND_ROWS + 4 - height = legend + 2 * MARGIN_PT + ROW_PT * (len(rows) + len(files)) + FILE_GAP_PT * len(files) - fig = style.figure(style.TEXT_WIDTH_PT, height) - ax = style.axes(fig, MARGIN_PT, MARGIN_PT + legend, style.TEXT_WIDTH_PT - 2 * MARGIN_PT, - height - legend - 2 * MARGIN_PT) - ax.set_axis_off() - ax.set_xlim(0, style.TEXT_WIDTH_PT - 2 * MARGIN_PT) - ax.set_ylim(0, height - legend - 2 * MARGIN_PT) - ax.grid(False) - y = height - legend - 2 * MARGIN_PT - current_file = None - for r in rows: - if r["file"] != current_file: - current_file = r["file"] - y -= FILE_GAP_PT - ax.text(0, y - ROW_PT * 0.15, current_file, fontsize=style.TICK_PT, fontweight="bold", - ha="left", va="top", family="monospace") - y -= ROW_PT - y -= ROW_PT - ax.text(LABEL_PT - 4, y + ROW_PT / 2, r["name"], fontsize=style.TICK_PT - 1.5, ha="right", - va="center", family="monospace") - for i, st in enumerate(r["statuses"]): - ax.add_patch(Rectangle((LABEL_PT + i * CELL_PT, y + 0.9), CELL_PT - 0.6, ROW_PT - 1.8, - facecolor=STATUS_COLOUR[st], edgecolor="none")) - # legend, LEGEND_ROWS rows; DO-line edges share the "not reachable" entry - lax = style.axes(fig, MARGIN_PT, MARGIN_PT, style.TEXT_WIDTH_PT - 2 * MARGIN_PT, legend) - lax.set_axis_off() - lax.grid(False) - lax.set_xlim(0, style.TEXT_WIDTH_PT - 2 * MARGIN_PT) - lax.set_ylim(0, legend) - keys = ["cover", "nonfinite", "pathological", "unreachable", "open"] - per_row = -(-len(keys) // LEGEND_ROWS) - col_w = (style.TEXT_WIDTH_PT - 2 * MARGIN_PT) / per_row - for n, key in enumerate(keys): - x = (n % per_row) * col_w - yy = legend - 4 - (n // per_row) * LEGEND_ROW_PT - LEGEND_ROW_PT / 2 - lax.add_patch(Rectangle((x, yy - 4), 8, 8, facecolor=STATUS_COLOUR[key], edgecolor="none")) - lax.text(x + 11, yy, STATUS_LABEL[key], fontsize=style.LEGEND_PT, va="center") - for ext in ("svg", "pdf"): - fig.savefig(run_dir / f"branch-map.{ext}") - - -def case_matrix(run_dir: Path, s: dict) -> None: - """Subroutines as rows, the cases of the set as columns.""" - m = s["matrix"] - cases = m["cases"] - keep = [i for i, row in enumerate(s["branch_map"]) if len(row["statuses"]) > 0] - subs = [m["subroutines"][i] for i in keep] - frac = [[m["fraction"][j][i] for i in keep] for j in range(len(cases))] - unique = {c["name"]: set(c["unique_by_subroutine"].keys()) for c in s["cases"]} - # three numeric columns to the right of the grid, per subroutine: every branch, the reachable - # ones (not annotated unreachable, not a never-taken DO-line edge), and those taken on any - # measured run (the finite set, the non-finite set and any measured pathological case) - rows = {r["key"]: r for r in s["subroutines"]} - counts = [] - for key in subs: - r = rows[key] - total = r["branches"] - reachable = total - r["unreachable"] - r["loop_entry"] - taken_all = r["cover"] + r["nonfinite"] + r["pathological"] - counts.append((total, reachable, taken_all)) - col_names = ["branches", "reachable", "taken, all cases"] - col_x = [16.0, 46.0, 76.0] - cols_w = 84.0 - cell_h = 7.2 - left, top, bottom = 62.0, 168.0, 50.0 - width = style.TEXT_WIDTH_PT - cell_w = (width - left - 4 - cols_w) / max(1, len(cases)) - height = top + bottom + cell_h * len(subs) - fig = style.figure(width, height) - ax = style.axes(fig, left, bottom, cell_w * len(cases), cell_h * len(subs)) - ax.grid(False) - ax.set_xlim(0, len(cases)) - ax.set_ylim(0, len(subs)) - cmap = colormaps["YlGnBu"] - for i, key in enumerate(subs): - yy = len(subs) - 1 - i - for j, name in enumerate(cases): - f = frac[j][i] - colour = cmap(0.15 + 0.85 * f) if f > 0 else "#f4f4f4" - ax.add_patch(Rectangle((j, yy), 1, 1, facecolor=colour, edgecolor="white", linewidth=0.4)) - if key.split(":")[1] in unique.get(name, set()): - ax.plot(j + 0.5, yy + 0.5, marker="o", markersize=2.2, color="black") - ax.set_yticks([len(subs) - 1 - i + 0.5 for i in range(len(subs))]) - ax.set_yticklabels([k.split(":")[1] for k in subs], fontsize=style.TICK_PT - 2, family="monospace") - ax.set_xticks([j + 0.5 for j in range(len(cases))]) - ax.set_xticklabels(cases, rotation=90, fontsize=style.TICK_PT - 1.5, family="monospace") - ax.xaxis.tick_top() - ax.tick_params(length=0) - for sp in ax.spines.values(): - sp.set_visible(False) - # the numeric columns - nax = style.axes(fig, left + cell_w * len(cases) + 2, bottom, cols_w, cell_h * len(subs) + top) - nax.set_axis_off() - nax.grid(False) - nax.set_xlim(0, cols_w) - nax.set_ylim(0, cell_h * len(subs) + top) - for x, name in zip(col_x, col_names): - nax.text(x, cell_h * len(subs) + 4, name, rotation=90, fontsize=style.TICK_PT - 1.5, ha="center", - va="bottom", family="monospace") - for i, (total, reachable, taken_all) in enumerate(counts): - yy = (len(subs) - 1 - i + 0.5) * cell_h - for x, v in zip(col_x, (total, reachable, taken_all)): - nax.text(x + 8, yy, str(v), fontsize=style.TICK_PT - 2, ha="right", va="center", family="monospace") - # key: the colour scale, the "not entered" cell and the unique-branch dot - key_y = 16.0 - bar_w = 150.0 - cax = style.axes(fig, left, key_y, bar_w, 7.0) - cax.grid(False) - for sp in cax.spines.values(): - sp.set_visible(False) - steps = 40 - for k in range(steps): - f = (k + 0.5) / steps - cax.add_patch(Rectangle((k / steps, 0), 1 / steps, 1, facecolor=cmap(0.15 + 0.85 * f), edgecolor="none")) - cax.set_xlim(0, 1) - cax.set_ylim(0, 1) - cax.set_yticks([]) - cax.set_xticks([0, 0.25, 0.5, 0.75, 1.0]) - cax.set_xticklabels(["0", "0.25", "0.5", "0.75", "1"], fontsize=style.TICK_PT - 1) - cax.tick_params(length=2, pad=1) - fig.text((left) * style.PT / fig.get_size_inches()[0], (key_y + 20) * style.PT / fig.get_size_inches()[1], - "fraction of the subroutine's branches the case took", fontsize=style.LEGEND_PT, va="center") - kax = style.axes(fig, left + bar_w + 24, key_y - 2, width - left - bar_w - 30, 24) - kax.set_axis_off() - kax.grid(False) - kax.set_xlim(0, width - left - bar_w - 30) - kax.set_ylim(0, 24) - kax.add_patch(Rectangle((0, 12), 9, 9, facecolor="#f4f4f4", edgecolor="#cccccc", linewidth=0.4)) - kax.text(13, 16.5, "subroutine not entered by the case", fontsize=style.LEGEND_PT, va="center") - kax.add_patch(Rectangle((0, 0), 9, 9, facecolor=cmap(0.6), edgecolor="none")) - kax.plot(4.5, 4.5, marker="o", markersize=2.2, color="black") - kax.text(13, 4.5, "takes a branch no other finite candidate takes", - fontsize=style.LEGEND_PT, va="center") - for ext in ("svg", "pdf"): - fig.savefig(run_dir / f"case-matrix.{ext}") - - -def main() -> None: - run_dir = Path(sys.argv[1]) - style.apply() - s = json.loads((run_dir / "summary.json").read_text()) - branch_map(run_dir, s) - case_matrix(run_dir, s) - print(f"figures written to {run_dir}") - - -if __name__ == "__main__": - main() diff --git a/studies/branch-coverage/src/figures.rs b/studies/branch-coverage/src/figures.rs new file mode 100644 index 00000000..313fbc68 --- /dev/null +++ b/studies/branch-coverage/src/figures.rs @@ -0,0 +1,258 @@ +//! The study's two occupancy figures, built from `summary.json` as ironlab figures +//! (`docs/development/conventions/figures.md`): the branch map (every branch of the analysis path, +//! one row per subroutine grouped by source file, coloured by status) and the case × subroutine +//! matrix (the fraction of each subroutine's branches each case of the minimal set takes). +//! +//! The matplotlib case matrix carried three columns of counts beside its rows (branches, +//! reachable, taken over all cases); ironlab has no margin table yet (thclark/ironlab#121), so the +//! counts are the `subroutine-counts` table the pages include under the figure. + +use std::collections::BTreeSet; +use std::path::Path; + +use ironlab::prelude::*; +use serde_json::Value; +use yfoil::output::figures::{self, WIDTH_MM}; + +/// The branch statuses as the figures key them, in the categorical scale's order. The DO-line +/// edges that are never taken (`loop_entry`) are not reachable either, so they share that entry. +const STATUSES: [(&str, (u8, u8, u8), &str); 5] = [ + ("cover", (0x2c, 0xa0, 0x2c), "taken by the finite set"), + ("nonfinite", (0x1f, 0x77, 0xb4), "taken by the non-finite set"), + ("pathological", (0xff, 0x7f, 0x0e), "taken only in pathological cases"), + ("unreachable", (0x9a, 0x9a, 0x9a), "not reachable"), + ("open", (0xd6, 0x27, 0x28), "not taken"), +]; + +/// The colour of a cell whose subroutine the case never entered. +const NOT_ENTERED: Color = Color::rgb(0xf4 as f32 / 255.0, 0xf4 as f32 / 255.0, 0xf4 as f32 / 255.0); + +/// The height of a row of either figure (mm), set by its tick label. +const ROW_MM: f64 = 3.4; + +/// The number of colours of the listed YlGnBu colormap of the case matrix. +const RAMP: usize = 64; + +fn rgb((r, g, b): (u8, u8, u8)) -> Color { + Color::rgb(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0) +} + +/// A name of code in monospace: LaTeX's reserved `_` escaped. +fn tt(name: &str) -> String { + format!(r"\texttt{{{}}}", name.replace('_', r"\_")) +} + +/// The index of a status in [`STATUSES`]. +fn status_index(status: &str) -> f64 { + let status = if status == "loop_entry" { "unreachable" } else { status }; + STATUSES + .iter() + .position(|(key, _, _)| *key == status) + .unwrap_or_else(|| panic!("unknown branch status {status}")) as f64 +} + +/// The rows of `branch_map` that have branches, in the study's (coverage.toml) order. +fn rows(s: &Value) -> Vec<&Value> { + s["branch_map"] + .as_array() + .expect("branch_map") + .iter() + .filter(|r| r["statuses"].as_array().is_some_and(|a| !a.is_empty())) + .collect() +} + +fn str_of<'a>(v: &'a Value, key: &str) -> &'a str { + v[key].as_str().unwrap_or_else(|| panic!("{key} in summary.json")) +} + +/// The branch map: one row of cells per subroutine of the analysis path, under a bold header per +/// source file, each cell one branch coloured by its status; the key is the status scale. +pub fn branch_map(s: &Value) -> Figure { + let rows = rows(s); + let mut files: Vec<(&str, Vec)> = Vec::new(); + for r in &rows { + let (file, name) = (str_of(r, "file"), str_of(r, "name")); + match files.last_mut() { + Some((f, members)) if *f == file => members.push(tt(name)), + _ => files.push((file, vec![tt(name)])), + } + } + let statuses: Vec> = rows + .iter() + .map(|r| { + r["statuses"] + .as_array() + .unwrap() + .iter() + .map(|st| status_index(st.as_str().unwrap())) + .collect() + }) + .collect(); + let branches: usize = statuses.iter().map(Vec::len).sum(); + let lines = rows.len() + 2 * files.len(); + // a row is as tall as its label needs and the widest row spans the width the labels leave, so + // the cells are not square: 110 square cells to a row would make the rows too thin to label + let height = 40.0 + ROW_MM * lines as f64; + + let mut fig = Figure::new() + .size_mm(WIDTH_MM, height) + .tiles(2, 1) + .fit_row(1) + .title("Branches of the analysis path by status") + .parameter("study", "branch-coverage") + .parameter("figure", "branch-map") + .parameter("subroutines", rows.len() as i64) + .parameter("branches", branches as i64) + .tag("validation"); + let status = fig + .categorical_scale(STATUSES.map(|(_, c, label)| (rgb(c), label))) + .id(); + let mut ax = fig.axes(0, 0); + let placed = ax.ycategory_groups(files.iter().map(|(f, m)| (*f, m.iter()))); + ax.occupancy(placed.place(&statuses)).color_scale(status); + ax.box_on(false); + fig.panel(1, 0) + .legend() + .item(status) + .columns(LegendEntryColumns::Count(3)); + fig +} + +/// The case × subroutine matrix: the fraction of each subroutine's branches each case of the +/// minimal set takes; a dot marks a subroutine in which the case takes a branch no other finite +/// candidate takes, and a subroutine the case never entered is keyed as such. +pub fn case_matrix(s: &Value) -> Figure { + let m = &s["matrix"]; + let cases: Vec<&str> = m["cases"] + .as_array() + .unwrap() + .iter() + .map(|c| c.as_str().unwrap()) + .collect(); + let all_subs: Vec<&str> = m["subroutines"] + .as_array() + .unwrap() + .iter() + .map(|c| c.as_str().unwrap()) + .collect(); + let keep: Vec = (0..all_subs.len()) + .filter(|&i| s["branch_map"][i]["statuses"].as_array().is_some_and(|a| !a.is_empty())) + .collect(); + let subs: Vec<&str> = keep.iter().map(|&i| all_subs[i]).collect(); + let name = |key: &str| key.split_once(':').map_or(key, |(_, n)| n).to_string(); + // a case that never entered a subroutine took none of its branches: every subroutine with a + // conditional takes at least one edge once entered, so a fraction of zero is "not entered" + let fraction = Matrix::from_fn(subs.len(), cases.len(), |row, col| { + let f = m["fraction"][col][keep[row]].as_f64().unwrap(); + if f > 0.0 { + f + } else { + f64::NAN + } + }); + let mut unique = (Vec::new(), Vec::new()); + for (col, case) in cases.iter().enumerate() { + let c = s["cases"] + .as_array() + .unwrap() + .iter() + .find(|c| c["name"].as_str() == Some(case)) + .unwrap_or_else(|| panic!("case {case} in summary.json")); + let subs_unique: BTreeSet<&str> = c["unique_by_subroutine"] + .as_object() + .map(|o| o.keys().map(String::as_str).collect()) + .unwrap_or_default(); + for (row, key) in subs.iter().enumerate() { + if subs_unique.contains(name(key).as_str()) { + unique.0.push(col as f64); + unique.1.push(row as f64); + } + } + } + + let height = 85.0 + ROW_MM * subs.len() as f64; + let mut fig = Figure::new() + .size_mm(WIDTH_MM, height) + .tiles(3, 1) + .fit_row(1) + .fit_row(2) + .title("Branches each case of the minimal finite set takes") + .parameter("study", "branch-coverage") + .parameter("figure", "case-matrix") + .parameter("cases", cases.len() as i64) + .parameter("subroutines", subs.len() as i64) + .tag("validation"); + let ramp: Vec = (0..RAMP) + .map(|k| figures::ylgnbu(k as f64 / (RAMP - 1) as f64)) + .collect(); + let taken = fig + .color_scale() + .colormap(ironlab::ir::Colormap::Listed { colors: ramp }) + .limits(0.0, 1.0) + .tick_step(0.25) + .non_finite(NOT_ENTERED) + .key_label(ColorScaleKey::NonFinite, "subroutine not entered by the case") + .show_key(ColorScaleKey::NonFinite) + .title("Fraction of the subroutine's branches the case took") + .id(); + let key = fig.panel(2, 0).legend().boxed(false).id(); + let mut ax = fig.axes(0, 0); + ax.cells(&fraction) + .color_scale(taken) + .edge_color(Color::WHITE) + .edge_width(0.4); + ax.scatter(&unique.0, &unique.1) + .label("takes a branch no other finite candidate takes") + .marker(Marker::Circle) + .marker_size(4.0) + .marker_face(Color::WHITE) + .marker_edge(Color::BLACK); + ax.xcategories(cases.iter().map(|c| tt(c))) + .xtick_label_angle(90.0) + .ycategories(subs.iter().map(|k| tt(&name(k)))) + .box_on(false) + .legend_in(key); + fig.panel(1, 0) + .colorbar(taken) + .orientation(ColorbarOrientation::Horizontal) + .title_placement(ColorbarTitlePlacement::Above) + .length(0.8); + fig +} + +/// The per-subroutine counts the matplotlib case matrix carried beside its rows: every branch, +/// the reachable ones (not annotated unreachable, not a never-taken DO-line edge) and those taken +/// on any measured run (the finite set, the non-finite set and the measured pathological cases). +pub fn subroutine_counts_md(s: &Value) -> String { + let mut t = + String::from("| file | subroutine | branches | reachable | taken, all cases |\n|---|---|---:|---:|---:|\n"); + for r in s["subroutines"].as_array().unwrap() { + let n = |k: &str| r[k].as_u64().unwrap_or(0); + if n("branches") == 0 { + continue; + } + t += &format!( + "| `{}` | `{}` | {} | {} | {} |\n", + str_of(r, "file"), + str_of(r, "name"), + n("branches"), + n("branches") - n("unreachable") - n("loop_entry"), + n("cover") + n("nonfinite") + n("pathological"), + ); + } + t +} + +/// Draw both figures into the run folder (`.fig` and `.pdf`). A figure that does not validate or +/// lay out is reported and not written, and the publish check then fails on the page that embeds +/// it. +pub fn render(run_dir: &Path, s: &Value) -> bool { + let mut ok = true; + for (name, fig) in [("branch-map", branch_map(s)), ("case-matrix", case_matrix(s))] { + if let Err(e) = figures::save_with_pdf(&fig, &run_dir.join(name)) { + eprintln!("figure not drawn: {e}"); + ok = false; + } + } + ok +} diff --git a/studies/branch-coverage/src/main.rs b/studies/branch-coverage/src/main.rs index 6f66bd31..0736fd02 100644 --- a/studies/branch-coverage/src/main.rs +++ b/studies/branch-coverage/src/main.rs @@ -35,9 +35,8 @@ //! (`studies/known-issues`). //! //! Every invocation creates `runs//` next to this crate with `metadata.json`, -//! `summary.json` (every number of the tables and figures), `README.md` / `README.tex`; the -//! figures are drawn from `summary.json` by `plot.py` through `studies/figures/render.sh` -//! (matplotlib, presentation only). Every run also writes `publish/`, its tables and figures as +//! `summary.json` (every number of the tables and figures), `README.md` / `README.tex`, and the +//! two ironlab figures drawn from `summary.json` (`figures.rs`; `.fig` and `.pdf`). Every run also writes `publish/`, its tables and figures as //! the docs hold them (`studies/study-support/publish.rs`); `cargo xtask publish branch-coverage` //! copies it into `docs/validation/branch-coverage/`, whose pages are written by hand. //! `--render ` redraws an existing run folder's figures and rewrites its index and @@ -53,6 +52,7 @@ mod cover; mod drive; +mod figures; mod report; mod utilities; @@ -273,26 +273,6 @@ fn git(root: &Path, args: &[&str]) -> Option { .then(|| String::from_utf8_lossy(&out.stdout).trim().to_string()) } -/// Draw the run folder's figures with `studies/figures/render.sh branch-coverage `. -pub fn render(run_dir: &Path) -> bool { - let root = repo_root(); - let status = std::process::Command::new(root.join("studies/figures/render.sh")) - .arg("branch-coverage") - .arg(run_dir) - .status(); - match status { - Ok(st) if st.success() => true, - other => { - eprintln!( - "figures not drawn ({}): run `studies/figures/render.sh branch-coverage {}`", - other.map(|s| s.to_string()).unwrap_or_else(|e| e.to_string()), - run_dir.display() - ); - false - } - } -} - fn main() { let args: Vec = std::env::args().skip(1).collect(); assert!( @@ -307,7 +287,7 @@ fn main() { .expect("parse summary.json"); report::write_index(&run_dir, &summary); report::write_index_tex(&run_dir, &summary); - render(&run_dir); + figures::render(&run_dir, &summary); report::write_publish(&run_dir, &summary); return; } @@ -494,7 +474,7 @@ fn main() { .unwrap(); report::write_index(&run_dir, &summary); report::write_index_tex(&run_dir, &summary); - render(&run_dir); + figures::render(&run_dir, &summary); report::write_publish(&run_dir, &summary); println!("run folder: {}", run_dir.display()); if !group_matches { diff --git a/studies/branch-coverage/src/report.rs b/studies/branch-coverage/src/report.rs index d3cc13d5..1b3bd10e 100644 --- a/studies/branch-coverage/src/report.rs +++ b/studies/branch-coverage/src/report.rs @@ -806,15 +806,13 @@ fn body_md(s: &Value, figures: bool, figure_prefix: &str) -> String { asserted), one OPER script, one instrumented run plus its five 1-ULP twins.\n\n"; if figures { m += &format!( - "![Branch map]({figure_prefix}branch-map.svg)\n\n*Every branch of the analysis path, one row per \ - subroutine in XFOIL's file order: taken by the finite set (green), taken by the non-finite set \ - (blue), taken only in pathological cases (orange), not reachable — annotated unreachable or a \ - never-taken DO-line edge (grey), not taken (red).*\n\n\ - ![Case × subroutine]({figure_prefix}case-matrix.svg)\n\n*Fraction of each subroutine's branches \ - taken by each case of the set; a dot marks a subroutine in which the case takes a branch no other \ - finite candidate takes. The three columns on the right count, per subroutine, every branch, \ - the reachable ones (not annotated unreachable and not a never-taken DO-line edge) and those taken \ - on any measured run, the non-finite set included.*\n\n" + "[Branch map]({figure_prefix}branch-map.fig) ([PDF]({figure_prefix}branch-map.pdf)): every branch of \ + the analysis path, one row per subroutine in XFOIL's file order: taken by the finite set (green), \ + taken by the non-finite set (blue), taken only in pathological cases (orange), not reachable — \ + annotated unreachable or a never-taken DO-line edge (grey), not taken (red).\n\n\ + [Case × subroutine]({figure_prefix}case-matrix.fig) ([PDF]({figure_prefix}case-matrix.pdf)): \ + fraction of each subroutine's branches taken by each case of the set; a dot marks a subroutine in \ + which the case takes a branch no other finite candidate takes.\n\n" ); } m += "## DO loops of the analysis path\n\n"; @@ -889,7 +887,7 @@ pub fn write_index(run_dir: &Path, s: &Value) { let mut m = String::from("# Branch-coverage study\n\n"); m += &format!( "Generated by `cargo run --release -p branch-coverage` on {} (git {}{}, host {}); provenance in \ - `metadata.json`, every number in `summary.json`, figures drawn by `plot.py`.\n\n", + `metadata.json`, every number in `summary.json`, figures drawn from it by `figures.rs` (ironlab).\n\n", meta["generated"].as_str().unwrap_or(""), meta["git_describe"].as_str().unwrap_or(""), if meta["git_dirty"].as_bool().unwrap_or(false) { @@ -1075,11 +1073,11 @@ pub fn write_index_tex(run_dir: &Path, s: &Value) { ); w(&mut f, "\\noindent Every count is gcov's on the pristine double-precision reference; yFoil is never instrumented, and a branch taken by the finite set is one the reference took on a run of the set that yFoil matches as a whole run. \\emph{Taken by the non-finite set}: taken only on runs whose reference output contains NaN --- such a run cannot be compared whole, so each branch is tested as an event, replayed from XFOIL's state at that call, NaN where XFOIL has NaN. \\emph{Taken only in pathological cases}: taken only by the runs that hang or take hours, which cannot be tests. \\emph{Not reachable}: a recorded, falsifiable code-reading claim that no execution of XFOIL's analysis path can take the edge (structural, mode, guard, compiler or numerical: a floating-point coincidence or an exhausted Newton cap), or the never-taken arm of the loop-control test on a DO line. \\emph{Not taken}: no measured run took the edge and no argument says it cannot be taken; each carries a reach note. The Markdown page carries the full definitions."); w(&mut f, "\\begin{figure}[h]\\centering\\includegraphics{branch-map.pdf}\\caption{Every branch of the analysis path, one row per subroutine in XFOIL's file order: taken by the finite set (green), taken by the non-finite set (blue), taken only in pathological cases (orange), not reachable (grey), not taken (red).}\\end{figure}"); - w(&mut f, "\\begin{figure}[h]\\centering\\includegraphics{case-matrix.pdf}\\caption{Fraction of each subroutine's branches taken by each case of the set; a dot marks a subroutine in which the case takes a branch no other finite candidate takes; the columns on the right count every branch of the subroutine, the reachable ones (not annotated unreachable, not a never-taken DO-line edge) and those taken on any measured run, the non-finite probes included.}\\end{figure}"); + w(&mut f, "\\begin{figure}[h]\\centering\\includegraphics{case-matrix.pdf}\\caption{Fraction of each subroutine's branches taken by each case of the set; a dot marks a subroutine in which the case takes a branch no other finite candidate takes; a subroutine the case never entered is light grey.}\\end{figure}"); } /// The run's docs bundle (`publish/`): one fragment per table the study's pages include, and the -/// two SVG figures. +/// two figures. pub fn write_publish(run_dir: &Path, s: &Value) { let meta: Value = serde_json::from_str(&fs::read_to_string(run_dir.join("metadata.json")).unwrap()).unwrap(); let u = &s["universe"]; @@ -1133,7 +1131,8 @@ pub fn write_publish(run_dir: &Path, s: &Value) { ), ); bundle.table("non-finite-set", &excluded_md(s)); - for name in ["branch-map.svg", "case-matrix.svg"] { + bundle.table("subroutine-counts", &crate::figures::subroutine_counts_md(s)); + for name in ["branch-map.fig", "case-matrix.fig"] { bundle.figure(&run_dir.join(name)); } println!( diff --git a/studies/figures/render.sh b/studies/figures/render.sh deleted file mode 100755 index 601bfc96..00000000 --- a/studies/figures/render.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bash -# -# Draw the branch-coverage study's occupancy figures (branch map, case matrix) from its run folder -# with matplotlib: the only figures ironlab cannot yet express (docs/development/conventions/figures.md; every -# other figure is an ironlab figure built in Rust). -# -# studies/figures/render.sh e.g. branch-coverage studies/branch-coverage/runs/2026-10-15T14-17-40Z -# -# The study's Rust program writes every number into the run folder; studies//plot.py only -# presents them (studies/figures/style.py holds the shared conventions). matplotlib is pinned here -# and fetched by uv into its own cache on first use, as scripts/docs.sh does for zensical; there is -# no Python environment in the working tree. -set -euo pipefail - -matplotlib_version="3.11.1" - -repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" -study="${1:?usage: render.sh }" -run_dir="${2:?usage: render.sh }" - -if ! command -v uv >/dev/null 2>&1; then - echo "error: uv is not installed. Install it from https://docs.astral.sh/uv/ and re-run." >&2 - exit 1 -fi - -cd "$repo_root" -exec uv run --no-project --quiet --with "matplotlib==$matplotlib_version" \ - python "studies/$study/plot.py" "$run_dir" diff --git a/studies/figures/style.py b/studies/figures/style.py deleted file mode 100644 index cc89e3f3..00000000 --- a/studies/figures/style.py +++ /dev/null @@ -1,67 +0,0 @@ -"""Presentation rules of the branch-coverage study's occupancy figures (branch map, case matrix), -the only figures still drawn by matplotlib; every other figure is an ironlab figure built in Rust -(docs/development/conventions/figures.md). - -The Rust study writes every number (data, derived quantities, extents, selections) into its run -folder; the plot script only presents them: - -- figures are drawn at their final physical size and go into the paper at natural size - (`\\includegraphics{...pdf}` with no `width=` and no `\\resizebox`); -- width is TEXT_WIDTH_PT, the `\\textwidth` of the target template (A4 with 1 in margins), or less; -- Times New Roman throughout; tick values and legends at 8 pt (the journal minimum), axis labels - at 9 pt; math through mathtext in a Times-compatible face; -- SVG with text kept as text, and PDF with the fonts embedded, from the same figure object. - -Run through studies/figures/render.sh, which pins matplotlib. -""" - - -import matplotlib - -matplotlib.use("Agg") -import matplotlib.pyplot as plt # noqa: E402 - -TEXT_WIDTH_PT = 451.0 -FONT = "Times New Roman" -TICK_PT = 8.0 -LEGEND_PT = 8.0 -AXIS_LABEL_PT = 9.0 -PT = 1.0 / 72.0 # inches per point -GRID_COLOUR = "#e4e4e4" - -def apply() -> None: - plt.rcParams.update( - { - "font.family": "serif", - "font.serif": [FONT, "Times", "Nimbus Roman", "DejaVu Serif"], - "mathtext.fontset": "stix", - "font.size": TICK_PT, - "axes.labelsize": AXIS_LABEL_PT, - "axes.titlesize": AXIS_LABEL_PT, - "xtick.labelsize": TICK_PT, - "ytick.labelsize": TICK_PT, - "legend.fontsize": LEGEND_PT, - "axes.linewidth": 0.75, - "xtick.major.width": 0.75, - "ytick.major.width": 0.75, - "axes.spines.top": False, - "axes.spines.right": False, - "axes.grid": True, - "grid.color": GRID_COLOUR, - "grid.linewidth": 0.5, - "axes.axisbelow": True, - "svg.fonttype": "none", - "pdf.fonttype": 42, - "figure.dpi": 100, - } - ) - - -def figure(width_pt: float, height_pt: float): - return plt.figure(figsize=(width_pt * PT, height_pt * PT)) - - -def axes(fig, x0_pt: float, y0_pt: float, w_pt: float, h_pt: float): - """An axes placed in points from the figure's bottom-left corner.""" - fw, fh = fig.get_size_inches() - return fig.add_axes([x0_pt * PT / fw, y0_pt * PT / fh, w_pt * PT / fw, h_pt * PT / fh]) diff --git a/studies/input-sensitivity/Cargo.toml b/studies/input-sensitivity/Cargo.toml index 7e870b8b..82f9455b 100644 --- a/studies/input-sensitivity/Cargo.toml +++ b/studies/input-sensitivity/Cargo.toml @@ -11,7 +11,7 @@ workspace = true [dependencies] yfoil = { path = "../..", default-features = false, features = ["plotting"] } -ironlab = "=0.12.0" +ironlab = "=0.16.0" cr-libm = { path = "../../cr-libm" } serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["float_roundtrip"] } diff --git a/studies/input-sensitivity/src/figures.rs b/studies/input-sensitivity/src/figures.rs index 9ca56824..6f16f169 100644 --- a/studies/input-sensitivity/src/figures.rs +++ b/studies/input-sensitivity/src/figures.rs @@ -3,14 +3,15 @@ //! //! Seven stacked rows share α — CL, CD, δ* and H at the upper-surface trailing edge, the //! upper-surface transition and trailing-edge separation x/c, and the final RMSBL on a log axis — -//! one column per input family. The base case is black, 1.5 pt, with filled markers, drawn last -//! so it lies on top; the perturbation levels are drawn behind it, largest first, coloured by -//! perturbation size (`figures::ylgnbu`, lightest = smallest). Unconverged points carry open -//! circles. The axes span the converged extents `metrics.json` records; a value beyond them (an +//! one column per input family; only the bottom row carries α tick labels. The base case is +//! black, 1.5 pt, with filled markers, drawn last so it lies on top, and listed first in the key +//! on a row of its own; the perturbation levels are drawn behind it and listed after it by +//! increasing perturbation, coloured by perturbation size (`figures::ylgnbu`, lightest = +//! smallest). Unconverged points carry open circles. The axes span the converged extents `metrics.json` records; a value beyond them (an //! unconverged state can be anything, up to CD = 1e19) is drawn on the axis edge, and a value //! that cannot be drawn (non-finite, or non-positive on the log axis) is left out. Each column -//! has a title and its own legend panel below it. The node and alpha-step families share a -//! two-column sheet; every family also gets a one-column figure. +//! has a title and its own legend panel below it, in a row fitted to the tallest key. The node +//! and alpha-step families share a two-column sheet; every family also gets a one-column figure. //! //! Each figure is saved as `/naca_.fig` with its PDF beside it (`README.tex` //! includes the PDFs). @@ -92,9 +93,9 @@ const ROWS: [(&str, Get, &str, bool); 7] = [ ("rmsbl", |p| p.rmsbl, "RMSBL", true), ]; -/// Height (mm) of a unit row weight: a data row has weight 1, so the figure's height (at the -/// default width) follows from its rows and its legend -const ROW_HEIGHT_MM: f64 = 29.0; +/// Height (mm) of the figure per data row; the legend row is fitted to its key and the data rows +/// share the rest +const ROW_HEIGHT_MM: f64 = 32.0; /// Legend columns: two under a full-width column, one under each column of the sheet (two /// would not fit its width at the default font) const LEGEND_COLUMNS_FULL_WIDTH: u32 = 2; @@ -103,9 +104,6 @@ const BASE_WIDTH_PT: f64 = 1.5; const LEVEL_WIDTH_PT: f64 = 0.75; const MARKER_PT: f64 = 2.4; const OPEN_MARKER_PT: f64 = 4.4; -/// Height of one legend entry, and of the legend's insets, relative to a data row -const LEGEND_ENTRY_WEIGHT: f64 = 0.14; -const LEGEND_INSET_WEIGHT: f64 = 0.36; fn exponent(v: f64) -> i32 { -(cr_libm::log10(v).round() as i32) @@ -189,22 +187,13 @@ fn draw(run_dir: &Path, meta: &Metadata, metrics: &Metrics, families: &[Family], } else { LEGEND_COLUMNS_FULL_WIDTH }; - let legend_rows = families - .iter() - .map(|f| metrics.levels_by_family.get(f.slug()).map_or(0, Vec::len)) - .max() - .unwrap_or(0) - .div_ceil(legend_columns as usize); - let legend_weight = LEGEND_ENTRY_WEIGHT * legend_rows as f64 + LEGEND_INSET_WEIGHT; - let mut weights = vec![1.0; ROWS.len()]; - weights.push(legend_weight); - let height_mm = ROW_HEIGHT_MM * (ROWS.len() as f64 + legend_weight); + let height_mm = ROW_HEIGHT_MM * ROWS.len() as f64; let slugs: Vec<&str> = families.iter().map(|f| f.slug()).collect(); let mut fig = Figure::new() .size_mm(figures::WIDTH_MM, height_mm) .tiles(nrow + 1, ncol) - .row_weights(weights) + .fit_row(nrow) .parameter("study", "input-sensitivity") .parameter("figure", "sensitivity-sheet") .parameter("foil", meta.foil.as_str()) @@ -216,12 +205,12 @@ fn draw(run_dir: &Path, meta: &Metadata, metrics: &Metrics, families: &[Family], .parameter("ncrit", meta.base.ncrit) .parameter("max_iterations", meta.base.iter as i64) .parameter("alpha_step_deg", meta.base.alpha_step_deg) - .label("testing"); + .tag("testing"); if meta.base.mach > 0.0 { - fig = fig.label("compressible"); + fig = fig.tag("compressible"); } if meta.base.re < 1e5 { - fig = fig.label("low-re"); + fig = fig.tag("low-re"); } let mut row_axes: Vec> = vec![Vec::new(); ROWS.len()]; @@ -233,19 +222,16 @@ fn draw(run_dir: &Path, meta: &Metadata, metrics: &Metrics, families: &[Family], .ok_or_else(|| format!("metrics.json has no family {}", family.slug()))?; let levels = ordered(levels); let n = (levels.len().max(2) - 2).max(1) as f64; - let key = { - let mut panel = fig.panel_legend(nrow, c); - panel - .columns(LegendEntryColumns::Count(legend_columns)) - .order(LegendEntryOrder::RowMajor) - .align(LegendLocation::North); - panel.id() - }; + let key = fig.panel(nrow, c).legend().id(); + // the top row's artists, in key order (base first), which the key lists explicitly + let mut keyed: Vec<(bool, NodeId)> = Vec::new(); for (r, (quantity, get, label, log)) in ROWS.iter().enumerate() { let (lo, hi) = y_limits(extent(quantity)?, *log); let mut ax = fig.axes(r as u32, c); - // the stack, largest perturbation first so the base lands on top - for (i, level) in levels.iter().enumerate().rev() { + // the levels by increasing perturbation, then the base last so it lands on top + let (bases, rest): (Vec, Vec) = (0..levels.len()).partition(|&i| levels[i].magnitude == 0.0); + for i in rest.into_iter().chain(bases) { + let level = levels[i]; let is_base = level.magnitude == 0.0; let colour = if is_base { Color::BLACK @@ -267,10 +253,13 @@ fn draw(run_dir: &Path, meta: &Metadata, metrics: &Metrics, families: &[Family], let mut line = ax.plot(&a, &v); line.color(colour) .line_width(if is_base { BASE_WIDTH_PT } else { LEVEL_WIDTH_PT }) - .display_name(level_label(level, meta)); + .label(level_label(level, meta)); if is_base { line.marker(Marker::Circle).marker_size(MARKER_PT).marker_face(colour); } + if r == 0 { + keyed.push((is_base, line.id())); + } } let (ua, uv): (Vec, Vec) = drawn.iter().filter(|d| !d.2).map(|d| (d.0, d.1)).unzip(); if !ua.is_empty() { @@ -293,9 +282,23 @@ fn draw(run_dir: &Path, meta: &Metadata, metrics: &Metrics, families: &[Family], } if r + 1 == ROWS.len() { ax.xlabel("$\\alpha$ (°)"); + } else { + ax.xtick_labels_hidden(); } row_axes[r].push(ax.id()); } + // the key: the base alone on its first row, then the levels by increasing perturbation + let mut panel = fig.panel(nrow, c); + let mut legend = panel.legend(); + legend + .columns(LegendEntryColumns::Count(legend_columns)) + .order(LegendEntryOrder::RowMajor); + for &(_, id) in keyed.iter().filter(|(is_base, _)| *is_base) { + legend.full_row(id); + } + for &(_, id) in keyed.iter().filter(|(is_base, _)| !*is_base) { + legend.item(id); + } } fig.link_all_x().map_err(|e| format!("{e}"))?; if ncol > 1 { @@ -397,6 +400,7 @@ mod tests { for f in written { let fig = Figure::load(dir.join(&f)).unwrap(); assert!(fig.validate().errors.is_empty(), "{f}"); + assert!(fig.layout_warnings().is_empty(), "{f}: {:?}", fig.layout_warnings()); assert!(dir.join(f.replace(".fig", ".pdf")).exists()); } } diff --git a/studies/series-cases/Cargo.toml b/studies/series-cases/Cargo.toml index 526d7804..ded10929 100644 --- a/studies/series-cases/Cargo.toml +++ b/studies/series-cases/Cargo.toml @@ -11,7 +11,7 @@ workspace = true [dependencies] yfoil = { path = "../..", default-features = false, features = ["plotting"] } -ironlab = "=0.12.0" +ironlab = "=0.16.0" cr-libm = { path = "../../cr-libm" } serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["float_roundtrip"] } diff --git a/studies/series-cases/src/figures.rs b/studies/series-cases/src/figures.rs index 810779f8..10260695 100644 --- a/studies/series-cases/src/figures.rs +++ b/studies/series-cases/src/figures.rs @@ -7,6 +7,7 @@ use crate::{Case, Study}; use cr_libm::{log10, pow}; use ironlab::prelude::*; +use ironlab::{LineStyle, MarkerStyle}; use serde_json::Value; use std::path::Path; use yfoil::output::figures::{self, FigureError, BLUE, ORANGE, RED, WIDTH_MM}; @@ -14,6 +15,8 @@ use yfoil::output::figures::{self, FigureError, BLUE, ORANGE, RED, WIDTH_MM}; const PT_TO_MM: f64 = 25.4 / 72.0; /// XFOIL's α range for every polar of the study. const ALPHA_RANGE: (f64, f64) = (-30.0, 30.0); +/// Where a NaN tick sits: at its α, this fraction of the panel's height up, whatever the limits. +const NAN_TICK_HEIGHT: f64 = 0.985; /// Legend names: one entry per look, shared by every panel of a sheet. const XFOIL_NAME: &str = "XFOIL (reference)"; @@ -21,13 +24,17 @@ const YFOIL_NAME: &str = "yFoil"; const FOLLOWED: &str = "yFoil point: every iteration agrees with XFOIL's"; const INFORMATIONAL: &str = "yFoil point: parts from XFOIL within it, or after a point that did (informational)"; const NONFINITE: &str = "went through a non-finite value within the point"; -const NAN_TICK: &str = "plotted value is NaN (marker at the top of the panel at its α)"; +const NAN_TICK: &str = "plotted value is NaN (tick at the top of the panel at its α)"; const ILL: &str = "parts where XFOIL is ill-conditioned (its own 1-ULP twins part there too)"; const MISMATCH: &str = "parts where XFOIL is well-conditioned (its twins agree): a mismatch, replayed step by step"; const STATIONS: &str = "final stagnation or transition station (IST, ITRAN) differs between the codes"; -const TRANSITION_FOLLOWED: &str = "every iteration of the point agrees with XFOIL's"; -const TRANSITION_INFORMATIONAL: &str = "parts from XFOIL within the point, or after a point that did (informational)"; +// The strip's key is as wide as its two plot areas, which the case names leave narrow, so its +// entries are the short forms of the sheet's. +const TRANSITION_FOLLOWED: &str = "followed: every iteration agrees with XFOIL's"; +const TRANSITION_INFORMATIONAL: &str = "informational: parts from XFOIL"; +const TRANSITION_ILL: &str = "divergent: XFOIL ill-conditioned there"; +const TRANSITION_MISMATCH: &str = "mismatch: XFOIL well-conditioned there"; /// How one kind of mark is drawn and what the key calls it. struct Look { @@ -40,18 +47,19 @@ struct Look { edge_width: f64, } -/// Draw marks of one look as a marker-only series; nothing when there are none, so the key lists -/// only what the figure shows. A non-positive value on a logarithmic axis is not drawn. -fn draw(ax: &mut AxesMut<'_>, m: &Marks, look: &Look, log: bool) { +/// Draw marks of one look as a marker-only series, returning its id; nothing when there are none, +/// so the key lists only what the figure shows. A non-positive value on a logarithmic axis is not +/// drawn. +fn draw(ax: &mut AxesMut<'_>, m: &Marks, look: &Look, log: bool) -> Option { if m.x.is_empty() { - return; + return None; } let y: Vec = m.y.iter() .map(|&v| if log && v <= 0.0 { f64::NAN } else { v }) .collect(); let mut line = ax.plot(&m.x, &y); - line.display_name(look.name) + line.label(look.name) .color(look.edge) .dash(Dash::None) .line_width(look.edge_width) @@ -61,6 +69,75 @@ fn draw(ax: &mut AxesMut<'_>, m: &Marks, look: &Look, log: bool) { if let Some(f) = look.face { line.marker_face(f); } + Some(line.id()) +} + +/// The key of a sheet: the first artist of each name, in the order first drawn. One item stands +/// for every artist of its name and look, in every panel. +#[derive(Default)] +struct Key { + items: Vec<(&'static str, NodeId)>, +} + +impl Key { + fn add(&mut self, name: &'static str, id: Option) { + if let Some(id) = id { + if !self.items.iter().any(|(n, _)| *n == name) { + self.items.push((name, id)); + } + } + } +} + +/// Mark, at the top edge of a panel, the α of each value that is NaN: a vertical tick at data α +/// and an axes-fraction height, so it stays on the edge whatever the limits, pan or zoom, and +/// takes no part in them. It is unnamed: the key explains the ticks once ([`nan_tick_sample`]). +fn nan_ticks(ax: &mut AxesMut<'_>, alpha: &[f64]) { + if alpha.is_empty() { + return; + } + ax.plot(alpha, vec![NAN_TICK_HEIGHT; alpha.len()]) + .y_coords(Coords::AxesFraction) + .clip(false) + .color(ORANGE) + .dash(Dash::None) + .marker(Marker::VLine) + .marker_size(4.0) + .marker_edge(ORANGE); +} + +/// The key's sample for the NaN ticks, which stand for no single artist. +fn nan_tick_sample() -> LegendSample { + LegendSample::Line { + line: LineStyle { + color: ColorSpec::Rgba { color: ORANGE }, + dash: Dash::None, + ..LineStyle::default() + }, + marker: MarkerStyle { + shape: Marker::VLine, + size_pt: 4.0, + face: ColorSpec::None, + edge: ColorSpec::Rgba { color: ORANGE }, + }, + } +} + +/// A case name (a code identifier) in monospace, its LaTeX specials escaped. +fn mono(name: &str) -> String { + let mut out = String::with_capacity(name.len() + 10); + for c in name.chars() { + if matches!(c, '_' | '{' | '}' | '$' | '&' | '%' | '#') { + out.push('\\'); + } + out.push(c); + } + format!("\\texttt{{{out}}}") +} + +/// Plain text with the one character that opens LaTeX math outside it escaped. +fn text(s: &str) -> String { + s.replace('$', "\\$") } fn grey_diamond() -> Color { @@ -82,9 +159,9 @@ fn describe(fig: Figure, study: &Study, figure: &str, case: &Case, run: &Value) if let Some([leg, alpha]) = run["parted"].as_array().map(Vec::as_slice) { fig = fig.parameter("parted", format!("leg {leg} α {alpha}°")); } - fig = fig.label("validation").label("xfoil-comparison"); + fig = fig.tag("validation").tag("xfoil-comparison"); for tag in regime_labels(case) { - fig = fig.label(tag); + fig = fig.tag(tag); } fig } @@ -207,16 +284,6 @@ fn limits(values: &[f64], log: bool, floor: Option) -> (f64, f64) { } } -/// Where a marker sits at the top of a panel: 1.5 % of the panel's height below its top edge. -fn near_top(lo: f64, hi: f64, log: bool) -> f64 { - if log { - let (l, h) = (log10(lo), log10(hi)); - pow(10.0, h - 0.015 * (h - l)) - } else { - hi - 0.015 * (hi - lo) - } -} - /// One case's polar sheet: CL and CD (top), the last RMSBL and the iterations (second row), the /// transition location on each side (third), the position of transition within its station /// interval on each side (fourth) and, where the fixture kept the reference's per-call state, @@ -239,37 +306,29 @@ pub fn polar_sheet(study: &Study, case: &Case, run: &Value) -> Figure { panels.push(panel("state_diff", "worst state array: $|\\Delta|$, floor (red)", true)); } let rows = panels.len().div_ceil(2) as u32; - // the matplotlib sheet's proportions: 118 pt panels 30 pt apart, a 107 pt legend strip + // the matplotlib sheet's proportions: 118 pt panels 30 pt apart, about 107 pt for the key, + // whose row is fitted to it let (panel_pt, gap_pt, legend_pt, frame_pt) = (118.0, 30.0, 107.0, 42.0); let height_pt = frame_pt + rows as f64 * panel_pt + (rows - 1) as f64 * gap_pt + legend_pt; - let row_weights: Vec = (0..rows) - .map(|_| 1.0) - .chain([legend_pt / (panel_pt + gap_pt)]) - .collect(); let mut fig = Figure::new() .size_mm(WIDTH_MM, height_pt * PT_TO_MM) .tiles(rows + 1, 2) - .row_weights(row_weights) - .title(Text::plain(format!( + .fit_row(rows) + .title(format!( "{}: {}", - run["section"].as_str().unwrap_or(""), - case.name - ))); + text(run["section"].as_str().unwrap_or("")), + mono(&case.name) + )); fig = describe(fig, study, "polar-sheet", case, run); - let key = { - let mut legend = fig.panel_legend_span(rows, 0, 1, 2); - legend - .boxed(false) - .columns(LegendEntryColumns::Count(1)) - .align(LegendLocation::West); - legend.id() - }; + let key_panel = fig.panel_span(rows, 0, 1, 2).align(PanelLocation::West).legend().id(); + let mut key = Key::default(); + let mut any_nan = false; let mut ids = vec![]; for (i, pn) in panels.iter().enumerate() { let (row, col) = ((i / 2) as u32, (i % 2) as u32); let mut all: Vec = vec![]; - let mut nan_ticks: Vec = vec![]; + let mut nan_ticks_at: Vec = vec![]; let mut ax = fig.axes(row, col); let usable = |v: Option| v.filter(|v| !pn.log || *v > 0.0); for leg in [1, 2] { @@ -289,7 +348,7 @@ pub fn polar_sheet(study: &Study, case: &Case, run: &Value) -> Figure { .map(|p| { let v = value(p, "xfoil", pn.key); match v { - None => nan_ticks.push(alpha(p)), + None => nan_ticks_at.push(alpha(p)), Some(y) if p["xfoil_nonfinite"].as_bool().unwrap_or(false) => xfoil_nonfinite.push(alpha(p), y), _ => {} } @@ -298,13 +357,16 @@ pub fn polar_sheet(study: &Study, case: &Case, run: &Value) -> Figure { .unzip(); if !xa.is_empty() { all.extend(&xy); - ax.plot(&xa, &xy) - .display_name(XFOIL_NAME) + let id = ax + .plot(&xa, &xy) + .label(XFOIL_NAME) .color(RED) .dash(Dash::Dashed) .line_width(0.9) .marker(Marker::Cross) - .marker_size(3.2); + .marker_size(3.2) + .id(); + key.add(XFOIL_NAME, Some(id)); } let mut followed = Marks::default(); let mut informational = Marks::default(); @@ -318,7 +380,7 @@ pub fn polar_sheet(study: &Study, case: &Case, run: &Value) -> Figure { let a = alpha(p); let v = value(p, "yfoil", pn.key); let Some(y) = v else { - nan_ticks.push(a); + nan_ticks_at.push(a); return (a, f64::NAN); }; let filled = p["followed"].as_bool().unwrap_or(false); @@ -343,7 +405,8 @@ pub fn polar_sheet(study: &Study, case: &Case, run: &Value) -> Figure { .unzip(); all.extend(&yy); if !ya.is_empty() { - ax.plot(&ya, &yy).display_name(YFOIL_NAME).color(BLUE).line_width(0.8); + let id = ax.plot(&ya, &yy).label(YFOIL_NAME).color(BLUE).line_width(0.8).id(); + key.add(YFOIL_NAME, Some(id)); } let look = |name, shape, size, edge, face| Look { name, @@ -353,114 +416,140 @@ pub fn polar_sheet(study: &Study, case: &Case, run: &Value) -> Figure { face, edge_width: 0.6, }; - draw( - &mut ax, - &xfoil_nonfinite, - &look(NONFINITE, Marker::Square, 5.2, ORANGE, None), - pn.log, + key.add( + NONFINITE, + draw( + &mut ax, + &xfoil_nonfinite, + &look(NONFINITE, Marker::Square, 5.2, ORANGE, None), + pn.log, + ), ); - draw( - &mut ax, - &followed, - &look(FOLLOWED, Marker::Circle, 2.4, BLUE, Some(BLUE)), - pn.log, + key.add( + FOLLOWED, + draw( + &mut ax, + &followed, + &look(FOLLOWED, Marker::Circle, 2.4, BLUE, Some(BLUE)), + pn.log, + ), ); - draw( - &mut ax, - &informational, - &look(INFORMATIONAL, Marker::Circle, 2.4, BLUE, None), - pn.log, + key.add( + INFORMATIONAL, + draw( + &mut ax, + &informational, + &look(INFORMATIONAL, Marker::Circle, 2.4, BLUE, None), + pn.log, + ), ); - draw( - &mut ax, - &yfoil_nonfinite, - &look(NONFINITE, Marker::Square, 5.2, ORANGE, None), - pn.log, + key.add( + NONFINITE, + draw( + &mut ax, + &yfoil_nonfinite, + &look(NONFINITE, Marker::Square, 5.2, ORANGE, None), + pn.log, + ), ); - draw( - &mut ax, - &ill, - &look(ILL, Marker::Diamond, 5.8, Color::BLACK, None), - pn.log, + key.add( + ILL, + draw( + &mut ax, + &ill, + &look(ILL, Marker::Diamond, 5.8, Color::BLACK, None), + pn.log, + ), ); let grey = grey_diamond(); - draw( - &mut ax, - &mismatch, - &look(MISMATCH, Marker::Diamond, 5.8, grey, Some(grey)), - pn.log, + key.add( + MISMATCH, + draw( + &mut ax, + &mismatch, + &look(MISMATCH, Marker::Diamond, 5.8, grey, Some(grey)), + pn.log, + ), ); - draw( - &mut ax, - &stations, - &look(STATIONS, Marker::TriangleDown, 6.5, Color::BLACK, None), - pn.log, + key.add( + STATIONS, + draw( + &mut ax, + &stations, + &look(STATIONS, Marker::TriangleDown, 6.5, Color::BLACK, None), + pn.log, + ), ); } - // reference lines: the tolerance itself, and the ends and middle of the station interval + // reference lines across the panel at any zoom: the tolerance itself, and the ends and + // middle of the station interval let reference_lines: &[f64] = match pn.key { "state_ratio" => &[1.0], "trpos_upper" | "trpos_lower" => &[0.0, 0.5, 1.0], _ => &[], }; - all.extend(reference_lines); - let (lo, hi) = match pn.key { - "trpos_upper" | "trpos_lower" => (-0.02, 1.02), - "rmsbl" => limits(&all, true, Some(1e-8)), - _ => limits(&all, pn.log, None), - }; for &y in reference_lines { - ax.plot([ALPHA_RANGE.0, ALPHA_RANGE.1], [y, y]) - .color(Color::BLACK) - .line_width(0.5) - .dash(Dash::Dotted); - } - if !nan_ticks.is_empty() { - // an axes-fraction tick in the matplotlib figure; here a marker at the panel's top - let top = near_top(lo, hi, pn.log); - ax.plot(&nan_ticks, vec![top; nan_ticks.len()]) - .display_name(NAN_TICK) - .color(ORANGE) - .dash(Dash::None) - .marker(Marker::Plus) - .marker_size(4.0); + ax.hline(y).color(Color::BLACK).line_width(0.5).dash(Dash::Dotted); } + any_nan |= !nan_ticks_at.is_empty(); + nan_ticks(&mut ax, &nan_ticks_at); ax.xlabel("$\\alpha$ / deg") .ylabel(pn.ylabel) .xlim(ALPHA_RANGE.0, ALPHA_RANGE.1) - .ylim(lo, hi) - .legend_in(key); + .legend_in(key_panel); + // fixed limits only where they help reading: the interval with its ends clear of the box, + // and RMSBL from the convergence threshold's decades up; every other panel autoscales + match pn.key { + "trpos_upper" | "trpos_lower" => { + ax.ylim(-0.02, 1.02); + } + "rmsbl" => { + let (lo, hi) = limits(&all, true, Some(1e-8)); + ax.ylim(lo, hi); + } + _ => {} + } if pn.log { ax.yscale(Scale::Log); } - ids.push((pn.key, ax.id(), lo, hi)); + ids.push((pn.key, ax.id())); } fig.link_all_x().expect("link α"); // the upper/lower pairs plot one quantity: one y range for both for pair in [["xtr_upper", "xtr_lower"], ["trpos_upper", "trpos_lower"]] { - let found: Vec<_> = ids.iter().filter(|(k, ..)| pair.contains(k)).collect(); - if let [a, b] = found.as_slice() { - let (lo, hi) = (a.2.min(b.2), a.3.max(b.3)); - fig.axes((ids.iter().position(|i| i.1 == a.1).unwrap() / 2) as u32, 0) - .ylim(lo, hi); - fig.link(Dim::Y, &[a.1, b.1]).expect("link the pair"); + let found: Vec = ids + .iter() + .filter(|(k, _)| pair.contains(k)) + .map(|(_, id)| *id) + .collect(); + if found.len() == 2 { + fig.link(Dim::Y, &found).expect("link the pair"); } } + let mut panel = fig.panel_span(rows, 0, 1, 2); + let mut legend = panel.legend(); + legend.boxed(false).columns(LegendEntryColumns::Count(1)); + for (_, id) in &key.items { + legend.item(*id); + } + if any_nan { + legend.custom_item(nan_tick_sample(), NAN_TICK); + } fig } /// Every recorded point of every case: where transition sits within its station interval, one -/// row per case (numbered, the key names the case), one panel per side. +/// row per case, named by the case and first at the top, one panel per side. pub fn transition_strip(study: &Study, cases: &[&Case], runs: &[Value]) -> Figure { let n = runs.len().max(1); let panel_pt = (11.0 * n as f64 + 8.0).max(120.0); - let key_rows = (runs.len() + 4) as f64; - let legend_pt = 11.0 * key_rows + 10.0; + // about four key rows, in a row fitted to the key + let legend_pt = 11.0 * 4.0 + 10.0; let height_pt = 42.0 + panel_pt + legend_pt; let mut fig = Figure::new() .size_mm(WIDTH_MM, height_pt * PT_TO_MM) .tiles(2, 2) - .row_weights([1.0, legend_pt / panel_pt]) + .fit_row(1) .title("Position of transition in its station interval, every recorded point of every case") .parameter("study", study.slug) .parameter("figure", "transition-strip") @@ -475,23 +564,23 @@ pub fn transition_strip(study: &Study, cases: &[&Case], runs: &[Value]) -> Figur "matched", runs.iter().map(|r| r["matched"].as_i64().unwrap_or(0)).sum::(), ) - .label("validation") - .label("xfoil-comparison"); + .tag("validation") + .tag("xfoil-comparison"); let mut tags: Vec<&str> = cases.iter().flat_map(|c| regime_labels(c)).collect(); tags.sort_unstable(); tags.dedup(); for t in tags { - fig = fig.label(t); + fig = fig.tag(t); } let key = { - let mut legend = fig.panel_legend_span(1, 0, 1, 2); - legend - .boxed(false) - .columns(LegendEntryColumns::Count(1)) - .align(LegendLocation::NorthWest); + let mut panel = fig.panel_span(1, 0, 1, 2); + panel.align(PanelLocation::NorthWest); + let mut legend = panel.legend(); + legend.boxed(false).columns(LegendEntryColumns::Count(1)); legend.id() }; - let (ylo, yhi) = (0.5, n as f64 + 0.5); + // a case with no transition on the aerofoil (an inviscid one) keeps its row: rows are categories + let names: Vec = runs.iter().map(|r| mono(r["name"].as_str().unwrap_or(""))).collect(); let mut ids = vec![]; for (col, label) in [(0u32, "upper side"), (1, "lower side")] { let mut ax = fig.axes(0, col); @@ -500,8 +589,7 @@ pub fn transition_strip(study: &Study, cases: &[&Case], runs: &[Value]) -> Figur let mut ill = Marks::default(); let mut mismatch = Marks::default(); for (row, run) in runs.iter().enumerate() { - let y = (row + 1) as f64; - let mut case_marks = Marks::default(); + let y = row as f64; for p in run["points"].as_array().into_iter().flatten() { let t = &p["transition_position"][col as usize]; if t.is_null() || t["at_te"].as_bool().unwrap_or(false) { @@ -510,7 +598,6 @@ pub fn transition_strip(study: &Study, cases: &[&Case], runs: &[Value]) -> Figur let Some(x) = t["fraction"].as_f64().filter(|x| x.is_finite()) else { continue; }; - case_marks.push(x, y); if p["followed"].as_bool().unwrap_or(false) { followed.push(x, y); } else if p["outcome"] == "mismatch" { @@ -521,30 +608,10 @@ pub fn transition_strip(study: &Study, cases: &[&Case], runs: &[Value]) -> Figur informational.push(x, y); } } - // a row label in the matplotlib figure; here a transparent series named by the case, - // so the key and the data tips say which row is which (a case with no transition on - // the aerofoil, an inviscid one, keeps its row and its key entry with a NaN point) - if case_marks.x.is_empty() { - case_marks.push(f64::NAN, y); - } - { - ax.plot(&case_marks.x, &case_marks.y) - .display_name(Text::plain(format!( - "{}: {}", - row + 1, - run["name"].as_str().unwrap_or("") - ))) - .color(Color::rgba(0.0, 0.0, 0.0, 0.0)) - .dash(Dash::None) - .marker(Marker::Circle) - .marker_size(5.0); - } } + // the ends and middle of the interval, across every row at any zoom for x in [0.0, 0.5, 1.0] { - ax.plot([x, x], [ylo, yhi]) - .color(Color::BLACK) - .line_width(0.4) - .dash(Dash::Dotted); + ax.vline(x).color(Color::BLACK).line_width(0.4).dash(Dash::Dotted); } let blue = figures::with_alpha(BLUE, 0.6); let look = |name, shape, size, edge, face| Look { @@ -571,20 +638,26 @@ pub fn transition_strip(study: &Study, cases: &[&Case], runs: &[Value]) -> Figur draw( &mut ax, &ill, - &look(ILL, Marker::Diamond, 5.0, Color::BLACK, None), + &look(TRANSITION_ILL, Marker::Diamond, 5.0, Color::BLACK, None), false, ); draw( &mut ax, &mismatch, - &look(MISMATCH, Marker::Diamond, 5.0, grey, Some(grey)), + &look(TRANSITION_MISMATCH, Marker::Diamond, 5.0, grey, Some(grey)), false, ); - ax.xlabel(format!("position of transition in its station interval, {label}")) - .ylabel(if col == 0 { "case (number in the key)" } else { "" }) + ax.ycategories(names.iter().map(String::as_str)) + .title(label) + .xlabel("position in the interval") .xlim(-0.02, 1.02) - .ylim(ylo, yhi) + .ylim(-0.5, n as f64 - 0.5) .legend_in(key); + if col == 0 { + ax.ylabel("case"); + } else { + ax.ytick_labels_hidden(); + } ids.push(ax.id()); } fig.link_all_x().expect("link the interval position"); @@ -637,10 +710,8 @@ mod tests { } #[test] - fn nan_marker_sits_inside_the_panel() { - let y = near_top(1e-8, 1.0, true); - assert!(y < 1.0 && y > 1e-8); - let y = near_top(0.0, 1.0, false); - assert!(y < 1.0 && y > 0.98); + fn case_names_are_monospace_with_latex_specials_escaped() { + assert_eq!(mono("naca0012_n160"), "\\texttt{naca0012\\_n160}"); + assert_eq!(text("a $5"), "a \\$5"); } } diff --git a/studies/series-cases/src/report.rs b/studies/series-cases/src/report.rs index 18e0ce89..35171ec7 100644 --- a/studies/series-cases/src/report.rs +++ b/studies/series-cases/src/report.rs @@ -158,7 +158,7 @@ fn body_md(study: &Study, s: &Value, figure: bool) -> String { marks a point during which that code went through a non-finite value: a station Newton whose residual \ was NaN (the iterate fallback then carries the march past it — for the reference, any `NaN` it \ printed during the call; for yFoil, its own count of such stations) or a non-finite RMSBL, CL, CD or CM \ - in any iteration; where the plotted value is itself NaN and cannot be placed, an orange plus at the top \ + in any iteration; where the plotted value is itself NaN and cannot be placed, an orange tick at the top \ of the panel marks its α. A black outer diamond marks a point within which yFoil parts from the \ reference where XFOIL is **ill-conditioned** (one of XFOIL's own twins parts there too: a divergent \ comparison, `docs/development/conventions/terminology.md`; the one-step replay from XFOIL's state at that \ @@ -326,7 +326,7 @@ pub fn write_index_tex(study: &Study, run_dir: &Path, s: &Value) { w(&mut f, &format!("\\noindent {}", tex_escape(study.purpose_tex))); w(&mut f, "Polars are swept $0\\rightarrow+30^\\circ$ and after INIT $0\\rightarrow-30^\\circ$ by $1^\\circ$ with XFOIL's polar procedure (ASEQ, ITMAX+5 per point, the sequence halting after NSEQEX = 4 consecutive unconverged points); yFoil driven the same way and compared point by point with the equivalence comparison. Provenance in \\texttt{metadata.json}, every number in \\texttt{summary.json}."); for r in s["polars"].as_array().unwrap() { - w(&mut f, &format!("\\begin{{figure}}[h]\\centering\\includegraphics{{polars-{}.pdf}}\\caption{{{} (\\texttt{{{}}}): $C_L$ and $C_D$ against $\\alpha$ (top), the last iteration's RMSBL and the iterations taken (second row), the transition location on each side (third row; an open triangle where the final IST or ITRAN differs between the codes), the position of transition within its station interval on each side (fourth row) and, where the fixture keeps the reference's per-call state, yFoil's final state against the reference's, worst array, as a ratio to its tolerance and as $|\\Delta|$ against the twin's 1-ULP floor (fifth row). XFOIL red (dashed, crosses), yFoil blue (solid, circles filled where every iteration of the point agrees with the reference's); an orange outer square marks a point in which that code went through a non-finite value, an orange plus at the top of the panel a plotted value that is itself NaN; a black outer diamond a point within which yFoil parts from the reference where XFOIL is ill-conditioned (its own 1-ULP twins part there too), a filled grey diamond one where XFOIL is well-conditioned (a mismatch). Where the red line stops short of the blue one the reference's sequence halted or hung there.}}\\end{{figure}}", r["name"].as_str().unwrap(), tex_escape(r["section"].as_str().unwrap()), tex_escape(r["name"].as_str().unwrap()))); + w(&mut f, &format!("\\begin{{figure}}[h]\\centering\\includegraphics{{polars-{}.pdf}}\\caption{{{} (\\texttt{{{}}}): $C_L$ and $C_D$ against $\\alpha$ (top), the last iteration's RMSBL and the iterations taken (second row), the transition location on each side (third row; an open triangle where the final IST or ITRAN differs between the codes), the position of transition within its station interval on each side (fourth row) and, where the fixture keeps the reference's per-call state, yFoil's final state against the reference's, worst array, as a ratio to its tolerance and as $|\\Delta|$ against the twin's 1-ULP floor (fifth row). XFOIL red (dashed, crosses), yFoil blue (solid, circles filled where every iteration of the point agrees with the reference's); an orange outer square marks a point in which that code went through a non-finite value, an orange tick at the top of the panel a plotted value that is itself NaN; a black outer diamond a point within which yFoil parts from the reference where XFOIL is ill-conditioned (its own 1-ULP twins part there too), a filled grey diamond one where XFOIL is well-conditioned (a mismatch). Where the red line stops short of the blue one the reference's sequence halted or hung there.}}\\end{{figure}}", r["name"].as_str().unwrap(), tex_escape(r["section"].as_str().unwrap()), tex_escape(r["name"].as_str().unwrap()))); } w(&mut f, "\\begin{figure}[h]\\centering\\includegraphics{transition-position.pdf}\\caption{Where transition sits within its station interval (0 at station ITRAN$-$1, 1 at station ITRAN) at the end of every recorded point of every case, per side, from yFoil's state; diamonds where the run parts (black: the reference is unstable against its 1-ULP twins there; grey: it is stable).}\\end{figure}"); w(&mut f, "\\begin{table}[h]\\centering\\small"); diff --git a/studies/study-support/publish.rs b/studies/study-support/publish.rs index 9af6a5f7..80bb6f84 100644 --- a/studies/study-support/publish.rs +++ b/studies/study-support/publish.rs @@ -13,7 +13,7 @@ //! publish/ //! tables/run.md which run this is (included at the top of the study's index page) //! tables/*.md Markdown table fragments -//! figures/* ironlab `.fig` (and the branch-coverage study's SVGs) +//! figures/* ironlab `.fig` //! ``` use std::fs; diff --git a/studies/xfoil-instrumentation-check/Cargo.toml b/studies/xfoil-instrumentation-check/Cargo.toml index 6fb76135..24036e3c 100644 --- a/studies/xfoil-instrumentation-check/Cargo.toml +++ b/studies/xfoil-instrumentation-check/Cargo.toml @@ -13,4 +13,4 @@ workspace = true serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["float_roundtrip"] } yfoil = { path = "../..", features = ["plotting"] } -ironlab = "=0.12.0" +ironlab = "=0.16.0" diff --git a/studies/xfoil-instrumentation-check/src/figure.rs b/studies/xfoil-instrumentation-check/src/figure.rs index 9cbbd464..57ab8c79 100644 --- a/studies/xfoil-instrumentation-check/src/figure.rs +++ b/studies/xfoil-instrumentation-check/src/figure.rs @@ -1,6 +1,7 @@ //! The instrumentation-check figure, built from `summary.json` alone as an ironlab figure -//! (`docs/development/conventions/figures.md`): a legend panel across the top with one entry per build (its -//! converged count and where its plotted series ends), then a 2×2 grid against α — CL, CD and CM +//! (`docs/development/conventions/figures.md`): a legend panel across the top, in a row fitted to it, with +//! one entry per build (its converged count and where its plotted series ends), then a 2×2 grid +//! against α (tick labels on the bottom row only) — CL, CD and CM //! (pristine red with crosses, instrumented blue with open circles) and the upper-surface //! trailing-edge boundary layer on a logarithmic axis (one colour per variable, pristine solid, //! instrumented dashed). α is linked across all four panels; no two panels plot the same @@ -72,9 +73,9 @@ pub fn build(s: &Summary) -> Figure { let fig = Figure::new() .size_mm(figures::WIDTH_MM, HEIGHT_MM) .tiles(3, 2) - .row_weights([0.5, 2.0, 2.0]) - .label("testing") - .label("xfoil-comparison") + .fit_row(0) + .tag("testing") + .tag("xfoil-comparison") .parameter("study", "xfoil-instrumentation-check") .parameter("figure", "instrumentation-check") .parameter("foil", format!("naca{}", s.foil)) @@ -85,7 +86,8 @@ pub fn build(s: &Summary) -> Figure { .parameter("dumps_identical", s.comparison.dumps_identical as i64); let mut fig = figures::flow_condition_parameters(fig, &conditions(s)); let key = fig - .panel_legend_span(0, 0, 1, 2) + .panel_span(0, 0, 1, 2) + .legend() .boxed(false) .columns(LegendEntryColumns::Count(1)) .id(); @@ -102,7 +104,7 @@ pub fn build(s: &Summary) -> Figure { let end = b.series_end_alpha_deg.map_or("—".to_string(), |a| format!("{a:.1}°")); let values: Vec = b.series.iter().map(get).collect(); ax.plot(alphas(&b.series), values) - .display_name(format!( + .label(format!( "{}: {} converged points, series to {end}", b.label, b.points.len() @@ -112,10 +114,13 @@ pub fn build(s: &Summary) -> Figure { .marker(marker) .marker_size(MARKER_PT); } - ax.xlabel(r"$\alpha$ (°)") - .ylabel(*label) - .xlim(x_lo, x_hi) - .legend_in(key); + if i == 2 { + ax.xlabel(r"$\alpha$ (°)"); + } + ax.ylabel(*label).xlim(x_lo, x_hi).legend_in(key); + if i < 2 { + ax.xtick_labels_hidden(); + } if let Some(e) = s.extents.get(*metric) { let (lo, hi) = padded(e); ax.ylim(lo, hi); @@ -149,7 +154,7 @@ pub fn build(s: &Summary) -> Figure { .marker_size(MARKER_PT); // one entry per variable, drawn from the pristine (solid) line if b.build == Build::Pristine { - line.display_name(*label); + line.label(*label); } } } @@ -206,19 +211,12 @@ mod tests { } #[test] - fn figure_validates_and_exports_without_legend_warnings() { + fn figure_validates_and_lays_out_without_warnings() { let fig = build(&summary()); let report = fig.validate(); assert!(report.errors.is_empty(), "{:?}", report.errors); - // temporary files go in the repository's .tmp/, never /tmp - let dir = Path::new(env!("CARGO_MANIFEST_DIR")) - .join("../../.tmp") - .join(format!("xfoil-instrumentation-check-{}", std::process::id())); - std::fs::create_dir_all(&dir).unwrap(); - let export = fig.export_pdf(dir.join("check.pdf")).unwrap(); - let _ = std::fs::remove_dir_all(&dir); - assert!(export.scene.is_empty(), "{:?}", export.scene); + assert!(fig.layout_warnings().is_empty(), "{:?}", fig.layout_warnings()); assert_eq!(fig.parameters().get("re"), Some(&1e6.into())); - assert_eq!(fig.labels(), ["testing", "xfoil-comparison"]); + assert_eq!(fig.tags(), ["testing", "xfoil-comparison"]); } } diff --git a/tests/application/cli_plot.rs b/tests/application/cli_plot.rs index 518b8eeb..a6b90d37 100644 --- a/tests/application/cli_plot.rs +++ b/tests/application/cli_plot.rs @@ -1,6 +1,6 @@ //! (c) yFoil functionality: `yfoil plot foil | analysis | polar` end to end through the binary: //! geometry, analysis and polar inputs, the ironlab figures they write (loaded back and checked -//! for their axes, artists, key, links, parameters and labels), and the input-dispatch errors. +//! for their axes, artists, key, links, parameters and tags), and the input-dispatch errors. //! Needs the `plotting` feature. //! //! Fixtures: none (every input is generated by the binary). @@ -9,7 +9,7 @@ use std::path::{Path, PathBuf}; use std::process::Command; -use ironlab::ir::{Artist, Dimension, Line, Parameter}; +use ironlab::ir::{Artist, Dimension, LegendEntries, LegendItemContent, PanelContent, Parameter, Series}; use ironlab::Figure; use yfoil::output::AnalysisOutput; @@ -120,13 +120,13 @@ impl Case { } } -/// The lines of axes `k`, in drawing order -fn lines(fig: &Figure, k: usize) -> Vec<&Line> { +/// The series of axes `k`, in drawing order +fn lines(fig: &Figure, k: usize) -> Vec<&Series> { fig.ir().axes[k] .artists .iter() .filter_map(|a| match a { - Artist::Line(l) => Some(l), + Artist::Series(l) => Some(l), _ => None, }) .collect() @@ -136,13 +136,37 @@ fn lines(fig: &Figure, k: usize) -> Vec<&Line> { fn names(fig: &Figure, k: usize) -> Vec { lines(fig, k) .iter() - .filter_map(|l| l.display_name.as_ref().map(|t| t.content.clone())) + .filter_map(|l| l.label.as_ref().map(|t| t.content.clone())) .collect() } -/// A line's x and y values -fn data(fig: &Figure, line: &Line) -> (Vec, Vec) { - let get = |id| fig.ir().data[&id].as_f64().unwrap().to_vec(); +/// The labels of the items a panel legend lists for no artist (the foil figure's marker key), +/// over every legend panel +fn custom_items(fig: &Figure) -> Vec { + fig.ir() + .panels + .iter() + .filter_map(|p| match &p.content { + PanelContent::Legend(l) => Some(l), + _ => None, + }) + .flat_map(|l| match &l.entries { + LegendEntries::Explicit(items) => items.iter().collect(), + _ => Vec::new(), + }) + .filter_map(|item| match &item.content { + LegendItemContent::Custom { label, .. } => Some(label.content.clone()), + _ => None, + }) + .collect() +} + +/// A series' x and y values +fn data(fig: &Figure, line: &Series) -> (Vec, Vec) { + let get = |id| { + let a = fig.ir().data.iter().find(|d| d.id == id).unwrap(); + a.array.as_f64().unwrap().to_vec() + }; (get(line.x), get(line.y)) } @@ -182,7 +206,7 @@ fn geometry_inputs_overlay_panels_only() { ok_in(Some(c.dir.path()), &["plot", "foil", c.p("n0012.json")]); let fig = c.figure("n0012_foil.fig"); assert_eq!(fig.ir().axes.len(), 1); - assert!(fig.ir().panel_legends.is_empty(), "no key for a single geometry"); + assert!(fig.ir().panels.is_empty(), "no key for a single geometry"); // outline + one line of 60 notches (the default for geometry input), NaN between notches let ls = lines(&fig, 0); assert_eq!(ls.len(), 2); @@ -214,7 +238,7 @@ fn geometry_inputs_overlay_panels_only() { assert_eq!(dots.len(), 2); assert!(dots.iter().all(|l| l.line.dash == ironlab::Dash::None)); assert_eq!(names(&fig, 0), ["n0012", "n0012_40"], "the key names each geometry"); - assert_eq!(fig.ir().panel_legends.len(), 1); + assert_eq!(fig.ir().panels.len(), 1); assert!(param(&fig, "n_nodes").is_none(), "the panel counts differ"); assert_eq!(param(&fig, "points"), Some(&Parameter::from(2_i64))); @@ -246,7 +270,8 @@ fn analysis_inputs_draw_quantities_wake_and_markers() { ]); let fig = c.figure("one.fig"); assert_eq!(fig.ir().axes.len(), 1); - assert_eq!(fig.ir().panel_legends.len(), 1, "one key, in a panel below the axes"); + assert_eq!(fig.ir().panels.len(), 2, "a curve key and a marker key, below the axes"); + assert_eq!(fig.ir().axes[0].aspect, ironlab::Aspect::Equal); let ls = lines(&fig, 0); // surface, wake line, aerofoil and wake notches, then per quantity: upper, lower, wake // upper, wake lower; then one line per marker @@ -261,14 +286,24 @@ fn analysis_inputs_draw_quantities_wake_and_markers() { let key = names(&fig, 0); assert!(key[0].starts_with("$\\delta^*$, scaled ×"), "{key:?}"); assert!(key[1].starts_with("$\\theta$, scaled ×"), "{key:?}"); - // the stagnation circle, and a key entry per kind and side drawn (this case has a laminar - // separation bubble) - for name in ["stagnation", "transition (upper surface)"] { - assert!(key.iter().any(|k| k == name), "{name} in {key:?}"); + assert_eq!( + key.len(), + 2, + "the markers are explained by the marker key, not named: {key:?}" + ); + // the stagnation circle, and a marker-key entry per kind drawn then per side (this case has + // a laminar separation bubble) + let marks = custom_items(&fig); + for name in [ + "stagnation", + "transition", + "separation", + "reattachment", + "upper surface", + "lower surface", + ] { + assert!(marks.iter().any(|k| k == name), "{name} in {marks:?}"); } - assert!(key.iter().any(|k| k.ends_with("transition (lower surface)")), "{key:?}"); - assert!(key.iter().any(|k| k.starts_with("separation (")), "{key:?}"); - assert!(key.iter().any(|k| k.starts_with("reattachment (")), "{key:?}"); let circles = ls.iter().filter(|l| l.marker.shape == ironlab::Marker::Circle).count(); assert_eq!(circles, 1); // one operating point: its inputs and outputs @@ -287,7 +322,7 @@ fn analysis_inputs_draw_quantities_wake_and_markers() { assert!(number(&fig, name).is_finite(), "{name}"); } assert!( - fig.labels().is_empty(), + fig.tags().is_empty(), "incompressible, free transition, Re 1e6: no regime tags" ); @@ -430,28 +465,30 @@ fn polar_distributions_select_design_points() { } #[test] -fn analysis_figure_draws_minus_cp_and_ue_over_a_linked_x() { +fn analysis_figure_draws_cp_reversed_and_ue_over_a_linked_x() { let c = Case::new(); ok(&["plot", "analysis", c.p("a4.json")]); let fig = c.figure("a4.fig"); let ir = fig.ir(); - assert_eq!(ir.axes.len(), 3, "−Cp, Ue/U∞ and the aerofoil strip"); - assert!(ir.panel_legends.is_empty(), "the key belongs to the Cp panel"); + assert_eq!(ir.axes.len(), 3, "Cp, Ue/U∞ and the aerofoil strip"); + assert!(ir.panels.is_empty(), "the key belongs to the Cp panel"); + assert!(ir.axes[0].y.reverse, "suction up"); + assert!(!ir.axes[1].y.reverse); assert_eq!(links(&fig, Dimension::X), vec![vec![0, 1, 2]]); assert!(links(&fig, Dimension::Y).is_empty()); let label = |k: usize| ir.axes[k].y.label.as_ref().map(|t| t.content.clone()); - assert_eq!(label(0).as_deref(), Some("$-C_p$")); + assert_eq!(label(0).as_deref(), Some("$C_p$")); assert_eq!(label(1).as_deref(), Some("$U_e/U_\\infty$")); assert_eq!(names(&fig, 0), ["Upper surface", "Lower surface"]); assert!(names(&fig, 1).is_empty()); assert_eq!(lines(&fig, 2).len(), 1); - // the data are the record's, Cp negated + // the data are the record's let analysis: AnalysisOutput = serde_json::from_str(&std::fs::read_to_string(c.path("a4.json")).unwrap()).unwrap(); let (xu, cpu, qu) = analysis.upper_surface().unwrap(); let (x, y) = data(&fig, lines(&fig, 0)[0]); assert_eq!(x, xu); - assert!(y.iter().zip(&cpu).all(|(a, b)| *a == -*b)); + assert_eq!(y, cpu); assert_eq!(data(&fig, lines(&fig, 1)[0]).1, qu); assert_eq!(param(&fig, "figure"), Some(&Parameter::from("analysis"))); @@ -463,7 +500,7 @@ fn analysis_figure_draws_minus_cp_and_ue_over_a_linked_x() { ok(&["plot", "analysis", c.p("a4m3.json"), "-o", c.p("m3.fig")]); let fig = c.figure("m3.fig"); assert_eq!(number(&fig, "mach"), 0.3); - assert_eq!(fig.labels(), ["compressible"]); + assert_eq!(fig.tags(), ["compressible"]); fails( &["plot", "analysis", c.p("a4.json"), "-o", c.p("a4.svg")], @@ -485,7 +522,7 @@ fn polar_figure_is_a_linked_sheet_with_one_key() { let fig = c.figure("polar_comparison.fig"); let ir = fig.ir(); assert_eq!(ir.axes.len(), 4); - assert_eq!(ir.panel_legends.len(), 1); + assert_eq!(ir.panels.len(), 1); // α across the CL–α, CM–α and CD–α panels; CL between CL–α and CL–CD assert_eq!(links(&fig, Dimension::X), vec![vec![0, 2, 3]]); assert_eq!(links(&fig, Dimension::Y), vec![vec![0, 1]]); diff --git a/xtask/src/publish.rs b/xtask/src/publish.rs index a2f8cb4d..3af5e4f8 100644 --- a/xtask/src/publish.rs +++ b/xtask/src/publish.rs @@ -6,7 +6,7 @@ //! this command replaces `docs/validation//{tables,figures}/` with the chosen run's bundle. //! It never writes a page: the pages of `docs/validation//` are written by hand, and only //! include the tables (`--8<-- "validation//tables/.md"`) and embed the figures -//! (``, `![...](figures/.svg)`). +//! (``). //! //! Before copying, every reference the study's pages make into its `tables/` or `figures/` is //! checked against the bundle, and the publish is refused if one is missing (Zensical would fail on