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

test_all_avl_files_parse

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

test_bubble_dancer_header

req-geom-3

Parse all SURFACE sections and return them keyed by name

Surface names are used as labels in plots and output tables

test_bubble_dancer_surfaces

req-geom-4

Parse per-section geometry (xle, yle, chord) for each surface

Section coordinates are required for geometry visualization

test_bubble_dancer_wing_sections

req-geom-5

Parse CONTROL entries on each section

Control names are carried through to the command generator and aero tables

test_bubble_dancer_controls

req-geom-6

Parse BODY section when present

Body data must not halt parsing of real aircraft files that include a fuselage

test_bubble_dancer_body

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

test_ctrl_names_bd

req-geom-8

Return empty ctrl_names for geometry with no control surfaces

Wing-only models must not produce phantom control entries or errors

test_ctrl_names_no_controls

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

test_ctrl_names_order_stable

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

test_single_file_returns_one_result

req-stab-2

StResult.filename matches the source file basename

Filename is used as a row key in the results DataFrame

test_filename

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

test_controls

req-stab-4

Extract flight-condition scalars: Alpha, Beta, Mach

Alpha and beta become the table breakpoints in aero_filewrite

test_flight_condition_scalars

req-stab-5

Extract total force coefficients: CLtot, CDtot, CDvis, CYtot, etc.

These are the primary outputs users build lookup tables from

test_total_forces

req-stab-6

Extract stability derivatives: CLa, Cma, Cnb, Cmq, etc.

Required for flight dynamics modeling and stability margin calculations

test_stability_derivatives

req-stab-7

Extract control derivatives: CLdN, CmdN, etc.

Required for control law design and control authority analysis

test_control_derivatives

req-stab-8

Extract neutral-point location as Xnp

Common stability metric frequently requested independently of the full coefficient set

test_neutral_point

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

test_spiral_stability

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

test_directory_read

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

test_results_to_dataframe_returns_dataframe

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

test_results_to_dataframe_columns_include_filename_and_data

req-stab-13

DataFrame cell values match the parsed .data values exactly

Ensures no silent truncation or type coercion during pivot

test_results_to_dataframe_values

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

test_reset_run_has_separator

req-cmd-2

Case name encodes the geometry name

Name appears in AVL output and helps correlate .st files to source geometry

test_reset_run_case_name

req-cmd-3

Alpha and beta are mapped to their respective AVL variables

Unmapped variables default to zero in AVL silently ignoring sweep inputs

test_reset_run_alpha_beta_mapping

req-cmd-4

All named controls are assigned to their respective variables

Unmapped controls default to zero in AVL silently ignoring requested deflections

test_reset_run_control_mappings

req-cmd-5

CDoref is written into the run case

Ensures viscous drag is included consistently across all cases

test_reset_run_CDo_from_header

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

test_reset_run_cg_from_header

req-cmd-7

Unit strings are written when Lunit/Munit/Tunit are provided

Physical units affect dimensional output quantities

test_reset_run_units

req-cmd-8

Geometry with no controls produces a valid run block without control lines

Wing-only runs must not emit malformed control assignments

test_reset_run_no_controls

req-cmd-9

Full command script begins with LOAD

AVL must load the geometry before any other command

test_command_starts_with_load

req-cmd-10

Graphics are disabled via PLOP followed by G

Suppresses interactive plot windows that would block AVL in headless or CI environments

test_command_disables_graphics

req-cmd-11

Script opens the OPER menu

All run execution and output saving commands are issued inside OPER

test_command_opens_oper

req-cmd-12

Script ends with Quit

Without Quit the AVL subprocess hangs waiting for further input

test_command_ends_with_quit

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

test_command_sets_alpha_beta

req-cmd-14

Each run case saves a .st file to out_dir

Output files are the only persistent artifact of each AVL run

test_command_saves_st_file

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

test_command_runs_and_resets

req-cmd-16

Control sweep emits Di Di commands for each surface and deflection value

Incorrect Di index or deflection value silently runs a different flight condition

test_command_ctrl_sweep_deflection

req-cmd-17

Multiple alphas produce one run block each

Batches the full alpha sweep in a single AVL invocation

test_command_multiple_alphas_produce_multiple_runs

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

test_find_avl_finds_local_binary

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

test_find_avl_raises_when_missing

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

test_find_avl_falls_back_to_path

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

test_verify_returns_path

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

test_verify_raises_on_bad_binary

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

test_run_calls_binary_with_stdin

req-bin-7

CLI verify subcommand returns exit code 0 on success and 1 on failure

Enables health-check scripting and CI pipeline gates

test_cli_verify_subcommand_success

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

test_cli_run_subcommand

req-bin-9

CLI with no arguments triggers SystemExit (help text)

First-time users get usage information rather than an unhandled exception

test_cli_no_args_shows_help

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

test_run_calls_avl_runner

req-sweep-2

run passes the .avl file’s parent directory as cwd

AVL resolves LOAD relative to cwd; running from another directory would fail to find the geometry

test_run_passes_avl_dir_as_cwd

req-sweep-3

run creates out_dir (including missing parents) before invoking AVL

Users should not need to manually create output directories

test_run_creates_out_dir

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

test_run_removes_stale_st_files

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

test_run_raises_on_avl_failure

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

test_run_command_contains_avl_name

req-sweep-7

Generated command script contains A A for each requested alpha

Alpha values must reach AVL unchanged; silent truncation or formatting errors would run the wrong flight condition

test_run_command_contains_alpha

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

test_run_default_out_dir_is_relative_to_avl

req-sweep-9

out_format=csv writes results.csv to out_dir

Common interchange format for spreadsheets and downstream tools

test_out_format_csv_creates_file

req-sweep-10

out_format=json writes results.json to out_dir

Common interchange format for web tools and other language consumers

test_out_format_json_creates_file

req-sweep-11

out_format=df returns results in memory without writing any file

Avoids unnecessary disk I/O in programmatic usage

test_out_format_df_writes_no_file

req-sweep-12

out_format= raises ValueError with message containing not recognised

Fails fast with a clear error rather than silently writing nothing

test_out_format_invalid_raises

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

test_returns_figure

req-plot-2

Figure contains exactly four axes

Four-view orthographic layout is the standard engineering drawing convention

test_figure_has_four_axes

req-plot-3

All four axes are Axes3D instances

2-D axes would silently flatten the geometry and misrepresent dihedral

test_all_axes_are_3d

req-plot-4

Axes carry titles: Isometric, Top, Front, Side

Labels orient the viewer without consulting documentation

test_axes_titles

req-plot-5

Figure suptitle contains the geometry header.name

Distinguishes saved plots when multiple geometries are compared

test_figure_title_contains_geometry_name

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

test_lines_drawn_for_surfaces

req-plot-7

CG position is rendered as a scatter collection

CG visualization is critical for stability interpretation

test_cg_scatter_plotted

req-plot-8

Geometry without a body section plots without error

Wing-only and body-less models are common and must not raise

test_no_body_geometry_no_crash

req-plot-9

With mirroring the isometric axes contains at least 4 lines

Mirrored surfaces must appear on both sides of the symmetry plane

test_mirror_doubles_lines

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

test_returns_aero_database

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

test_empty_results_raises

req-write-3

AeroDatabase.date is a non-empty string

Provenance field lets users identify when a table was generated

test_date_is_set

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

test_ref_fields_populated

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

test_stab_tables_created_for_all_coefs

req-write-6

A CtrlTable is created for every (coef, surface) pair keyed as <d_idx><ctrl_name>

Each control surface independently affects every coefficient; the compound key prevents collisions

test_ctrl_tables_created_for_each_coef_surface

req-write-7

Alpha breakpoints are sorted in ascending order

Monotonic axes are required by scipy RegularGridInterpolator and MATLAB lookup-table blocks

test_alpha_breakpoints_sorted

req-write-8

Beta breakpoints are sorted in ascending order

Monotonic axes are required by scipy RegularGridInterpolator and MATLAB lookup-table blocks

test_beta_breakpoints_sorted

req-write-9

StabTable.data shape is (n_alpha x n_beta)

Correct shape is required for 2-D table indexing and interpolation

test_stab_table_shape

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

test_neutral_case_fills_stab_table

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

test_non_neutral_does_not_fill_stab_table

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

test_ctrl_table_shape

req-write-13

CtrlTable defl breakpoints are sorted in ascending order

Monotonic defl axis is required for 3-D interpolation routines

test_ctrl_table_defl_breakpoints

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

test_ctrl_table_values_filled

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

test_ctrl_table_surface_and_ctrl_name

req-write-16

Geometry with no control surfaces produces an empty ctrl dict

ctrl must not contain phantom entries for non-existent surfaces

test_no_controls_empty_ctrl_dict

req-write-17

Geometry with no control surfaces still fills StabTable

Stability-only models are valid and must produce a complete neutral aero map

test_no_controls_stab_table_filled

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

test_missing_coef_produces_nan

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

test_returns_list

req-aeroplot-2

Every item in the returned list is a Figure

Mixed types would break callers that iterate and call fig.savefig

test_returns_figures

req-aeroplot-3

The first figure is the stability figure with 6 subplots (one per coefficient)

Consistent ordering enables programmatic access by index

test_stability_figure_first

req-aeroplot-4

Stability figure axes titles include coefficient names (CLtot, CDtot, etc.)

Labels identify which subplot carries which coefficient without documentation

test_stability_axes_titles

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

test_no_ctrl_only_stability_figure

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

test_ctrl_figures_produced

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

test_ctrl_figure_has_one_subplot_per_surface

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

test_ctrl_figure_title_contains_coef_name

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

test_beta_ref_nearest_used