Requirements#
Behavioral requirements derived from the test suite. Each row is asserted by
at least one pytest. Source CSVs live in docs/dev/reqs/.
Geometry parsing (avl_fileread)#
id |
requirement |
rationale |
test |
|---|---|---|---|
req-geom-1 |
Parse any .avl file into AvlGeometry without error |
avl_fileread is the sole entry point; if it errors the entire pipeline is blocked |
|
req-geom-2 |
Populate header fields: name, Mach, symmetry flags, Sref/Cref/Bref, CG, CDoref |
Reference quantities are required for normalizing aero coefficients and setting up run cases |
|
req-geom-3 |
Parse all SURFACE sections and return them keyed by name |
Surface names are used as labels in plots and output tables |
|
req-geom-4 |
Parse per-section geometry (xle, yle, chord) for each surface |
Section coordinates are required for geometry visualization |
|
req-geom-5 |
Parse CONTROL entries on each section |
Control names are carried through to the command generator and aero tables |
|
req-geom-6 |
Parse BODY section when present |
Body data must not halt parsing of real aircraft files that include a fuselage |
|
req-geom-7 |
Expose ordered control names via AvlGeometry.ctrl_names |
Ordered names map directly to AVL’s d1..dN slots in the command script |
|
req-geom-8 |
Return empty ctrl_names for geometry with no control surfaces |
Wing-only models must not produce phantom control entries or errors |
|
req-geom-9 |
ctrl_names returns the same sequence on repeated calls |
Sweep code calls ctrl_names multiple times; non-determinism would corrupt Di index assignment |
Stability output parsing (st_fileread)#
id |
requirement |
rationale |
test |
|---|---|---|---|
req-stab-1 |
Parse a single .st file and return exactly one StResult |
One output file per run case; parsing must not split or merge |
|
req-stab-2 |
StResult.filename matches the source file basename |
Filename is used as a row key in the results DataFrame |
|
req-stab-3 |
StResult.controls maps d0N indices to surface names as written in the .st header |
Control map is needed to translate d0N keys back to named surfaces in aero_filewrite |
|
req-stab-4 |
Extract flight-condition scalars: Alpha, Beta, Mach |
Alpha and beta become the table breakpoints in aero_filewrite |
|
req-stab-5 |
Extract total force coefficients: CLtot, CDtot, CDvis, CYtot, etc. |
These are the primary outputs users build lookup tables from |
|
req-stab-6 |
Extract stability derivatives: CLa, Cma, Cnb, Cmq, etc. |
Required for flight dynamics modeling and stability margin calculations |
|
req-stab-7 |
Extract control derivatives: CLdN, CmdN, etc. |
Required for control law design and control authority analysis |
|
req-stab-8 |
Extract neutral-point location as Xnp |
Common stability metric frequently requested independently of the full coefficient set |
|
req-stab-9 |
Store spiral stability ratio under key Clb_Cnr_div_Clr_Cnb and not Cnb |
The compound key prevents the ratio from silently overwriting the real Cnb derivative |
|
req-stab-10 |
Parse all .st files in a directory and return one result per file |
Enables batch processing after a full sweep without requiring individual file paths |
|
req-stab-11 |
results_to_dataframe returns a pd.DataFrame with one row per StResult |
Provides a pandas interface for downstream analysis and CSV export |
|
req-stab-12 |
DataFrame columns include filename and every key present in .data |
All parsed fields must be accessible by name for filtering and analysis |
|
req-stab-13 |
DataFrame cell values match the parsed .data values exactly |
Ensures no silent truncation or type coercion during pivot |
Command generation (avl_rungen)#
id |
requirement |
rationale |
test |
|---|---|---|---|
req-cmd-1 |
make_reset_run output contains a ‘—’ separator line |
AVL requires the exact run-case format; missing fields produce silent zero-value substitutions |
|
req-cmd-2 |
Case name encodes the geometry name |
Name appears in AVL output and helps correlate .st files to source geometry |
|
req-cmd-3 |
Alpha and beta are mapped to their respective AVL variables |
Unmapped variables default to zero in AVL silently ignoring sweep inputs |
|
req-cmd-4 |
All named controls are assigned to their respective variables |
Unmapped controls default to zero in AVL silently ignoring requested deflections |
|
req-cmd-5 |
CDoref is written into the run case |
Ensures viscous drag is included consistently across all cases |
|
req-cmd-6 |
CG coordinates (Xref, Yref, Zref) are written into the run case |
Moment coefficients and neutral point depend on the reference CG position |
|
req-cmd-7 |
Unit strings are written when Lunit/Munit/Tunit are provided |
Physical units affect dimensional output quantities |
|
req-cmd-8 |
Geometry with no controls produces a valid run block without control lines |
Wing-only runs must not emit malformed control assignments |
|
req-cmd-9 |
Full command script begins with LOAD |
AVL must load the geometry before any other command |
|
req-cmd-10 |
Graphics are disabled via PLOP followed by G |
Suppresses interactive plot windows that would block AVL in headless or CI environments |
|
req-cmd-11 |
Script opens the OPER menu |
All run execution and output saving commands are issued inside OPER |
|
req-cmd-12 |
Script ends with Quit |
Without Quit the AVL subprocess hangs waiting for further input |
|
req-cmd-13 |
Each run case sets alpha and beta via A A and B B direct assignments |
Direct assignment bypasses AVL trim logic and uses the specified flight condition exactly |
|
req-cmd-14 |
Each run case saves a .st file to out_dir |
Output files are the only persistent artifact of each AVL run |
|
req-cmd-15 |
Each run case executes (i), saves (st), and resets (x then CINI) before the next |
CINI clears accumulated forces; omitting it causes subsequent results to inherit prior state |
|
req-cmd-16 |
Control sweep emits Di Di |
Incorrect Di index or deflection value silently runs a different flight condition |
|
req-cmd-17 |
Multiple alphas produce one run block each |
Batches the full alpha sweep in a single AVL invocation |
Binary management (avl_bin / avl_cli)#
id |
requirement |
rationale |
test |
|---|---|---|---|
req-bin-1 |
find_avl checks ~/bin/avl first |
Allows a local user build to take priority over a system install |
|
req-bin-2 |
find_avl raises FileNotFoundError with message AVL binary not found when absent from both locations |
Clear error message guides the user to install the binary rather than a cryptic traceback |
|
req-bin-3 |
find_avl falls back to shutil.which (PATH) when ~/bin/avl is absent |
Supports system-wide installs and CI environments without a ~/bin directory |
|
req-bin-4 |
verify returns the resolved Path for a working AVL binary |
Provides a quick pre-flight check before starting a potentially long sweep |
|
req-bin-5 |
verify raises RuntimeError containing exited with code for a non-functional binary |
Prevents silent failures from a corrupt or wrong-platform binary masquerading as a valid install |
|
req-bin-6 |
run passes the command script as input= to the subprocess |
AVL is controlled entirely via stdin; no temporary script files are created |
|
req-bin-7 |
CLI verify subcommand returns exit code 0 on success and 1 on failure |
Enables health-check scripting and CI pipeline gates |
|
req-bin-8 |
CLI run subcommand accepts a command file and returns exit code 0 on success |
Exposes low-level AVL access without requiring a Python script |
|
req-bin-9 |
CLI with no arguments triggers SystemExit (help text) |
First-time users get usage information rather than an unhandled exception |
Sweep orchestration (avl_sweep)#
id |
requirement |
rationale |
test |
|---|---|---|---|
req-sweep-1 |
run invokes avl_runner.run exactly once per call |
All cases are batched into one AVL invocation; multiple calls would spawn redundant processes |
|
req-sweep-2 |
run passes the .avl file’s parent directory as cwd |
AVL resolves LOAD |
|
req-sweep-3 |
run creates out_dir (including missing parents) before invoking AVL |
Users should not need to manually create output directories |
|
req-sweep-4 |
run deletes existing .st files in out_dir before starting |
Stale files from a previous run would be silently included in the new results |
|
req-sweep-5 |
run raises RuntimeError with the exit code when AVL exits non-zero |
AVL can produce partial output before failing; raising prevents the caller from silently using corrupt results |
|
req-sweep-6 |
Generated command script contains LOAD <geometry_name> |
LOAD must match the .avl file stem; a mismatch causes AVL to silently run a different or missing geometry |
|
req-sweep-7 |
Generated command script contains A A |
Alpha values must reach AVL unchanged; silent truncation or formatting errors would run the wrong flight condition |
|
req-sweep-8 |
Default out_dir is <avl_dir>/out/<geometry_name> when not specified |
Provides a predictable output location relative to the geometry file |
|
req-sweep-9 |
out_format=csv writes results.csv to out_dir |
Common interchange format for spreadsheets and downstream tools |
|
req-sweep-10 |
out_format=json writes results.json to out_dir |
Common interchange format for web tools and other language consumers |
|
req-sweep-11 |
out_format=df returns results in memory without writing any file |
Avoids unnecessary disk I/O in programmatic usage |
|
req-sweep-12 |
out_format= |
Fails fast with a clear error rather than silently writing nothing |
Geometry plotting (avl_fileplot)#
id |
requirement |
rationale |
test |
|---|---|---|---|
req-plot-1 |
avl_fileplot returns a matplotlib Figure |
Caller can embed the figure in notebooks or save to disk |
|
req-plot-2 |
Figure contains exactly four axes |
Four-view orthographic layout is the standard engineering drawing convention |
|
req-plot-3 |
All four axes are Axes3D instances |
2-D axes would silently flatten the geometry and misrepresent dihedral |
|
req-plot-4 |
Axes carry titles: Isometric, Top, Front, Side |
Labels orient the viewer without consulting documentation |
|
req-plot-5 |
Figure suptitle contains the geometry header.name |
Distinguishes saved plots when multiple geometries are compared |
|
req-plot-6 |
At least one line is drawn in the isometric axes |
Missing lines indicate that surface parsing failed or coordinates were not transferred |
|
req-plot-7 |
CG position is rendered as a scatter collection |
CG visualization is critical for stability interpretation |
|
req-plot-8 |
Geometry without a body section plots without error |
Wing-only and body-less models are common and must not raise |
|
req-plot-9 |
With mirroring the isometric axes contains at least 4 lines |
Mirrored surfaces must appear on both sides of the symmetry plane |
Aero database construction (aero_filewrite)#
id |
requirement |
rationale |
test |
|---|---|---|---|
req-write-1 |
aero_filewrite returns an AeroDatabase instance |
Defines the structured output contract for the entire pipeline |
|
req-write-2 |
aero_filewrite raises ValueError (message contains empty) for an empty result list |
An empty list is always a caller error; silent NaN-filled tables are harder to diagnose |
|
req-write-3 |
AeroDatabase.date is a non-empty string |
Provenance field lets users identify when a table was generated |
|
req-write-4 |
Reference quantities (Sref, Cref, Bref, Xref, Yref, Zref) are read from the first result |
Carries reference geometry from the .st file through to the aero database without a separate geometry argument |
|
req-write-5 |
A StabTable is created for every name in COEF_NAMES |
All standard 6-DOF coefficients must be queryable even if some were absent from some runs |
|
req-write-6 |
A CtrlTable is created for every (coef, surface) pair keyed as |
Each control surface independently affects every coefficient; the compound key prevents collisions |
|
req-write-7 |
Alpha breakpoints are sorted in ascending order |
Monotonic axes are required by scipy RegularGridInterpolator and MATLAB lookup-table blocks |
|
req-write-8 |
Beta breakpoints are sorted in ascending order |
Monotonic axes are required by scipy RegularGridInterpolator and MATLAB lookup-table blocks |
|
req-write-9 |
StabTable.data shape is (n_alpha x n_beta) |
Correct shape is required for 2-D table indexing and interpolation |
|
req-write-10 |
Neutral-control results (all deflections ≈ 0) populate StabTable at the correct (alpha, beta) cell |
Off-neutral data must not enter the neutral aero map; populating only on zero-deflection runs enforces this |
|
req-write-11 |
Off-neutral results do not overwrite StabTable cells |
Ensures the neutral map is uncontaminated even when neutral and deflected runs share the same breakpoint |
|
req-write-12 |
CtrlTable.data shape is (n_alpha x n_beta x n_defl) |
Correct shape is required for 3-D lookup table blocks and RegularGridInterpolator |
|
req-write-13 |
CtrlTable defl breakpoints are sorted in ascending order |
Monotonic defl axis is required for 3-D interpolation routines |
|
req-write-14 |
CtrlTable cell values match source StResult.data values |
Ensures no silent truncation or reordering during pivot from list to 3-D array |
|
req-write-15 |
CtrlTable.surface is <d_idx>_<ctrl_name> and CtrlTable.ctrl_name is the surface name |
Metadata fields are used for axis labels and export struct field names |
|
req-write-16 |
Geometry with no control surfaces produces an empty ctrl dict |
ctrl must not contain phantom entries for non-existent surfaces |
|
req-write-17 |
Geometry with no control surfaces still fills StabTable |
Stability-only models are valid and must produce a complete neutral aero map |
|
req-write-18 |
A coefficient absent from a result’s .data produces NaN at that table cell |
NaN distinguishes a genuinely missing value from a coefficient that happens to be zero |
Aero database plotting (aero_fileplot)#
id |
requirement |
rationale |
test |
|---|---|---|---|
req-aeroplot-1 |
aero_fileplot returns a list of matplotlib Figure objects |
Caller iterates and saves or displays each figure individually |
|
req-aeroplot-2 |
Every item in the returned list is a Figure |
Mixed types would break callers that iterate and call fig.savefig |
|
req-aeroplot-3 |
The first figure is the stability figure with 6 subplots (one per coefficient) |
Consistent ordering enables programmatic access by index |
|
req-aeroplot-4 |
Stability figure axes titles include coefficient names (CLtot, CDtot, etc.) |
Labels identify which subplot carries which coefficient without documentation |
|
req-aeroplot-5 |
If no control surfaces are present exactly one figure is returned |
Single-figure output for geometry-only models must not produce empty control figures |
|
req-aeroplot-6 |
With one surface and all COEF_NAMES, seven figures are returned (1 stability + 6 control) |
One figure per coefficient for each surface; count must be deterministic |
|
req-aeroplot-7 |
Each control figure has one subplot per surface |
Combines all surfaces for a given coefficient on one figure for side-by-side comparison |
|
req-aeroplot-8 |
Each control figure suptitle contains a coefficient name |
Identifies the figure when saved to disk or displayed in a multi-figure viewer |
|
req-aeroplot-9 |
When beta_ref is not an exact breakpoint the nearest available beta is used |
Exact beta values are rarely available; nearest-neighbor avoids a crash or silent empty plot |