Requirements#

Tests

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

req-stab-14

results_to_dataframe returns exactly one row per StResult

Row count must equal result count; extra or missing rows would corrupt downstream analysis

test_results_to_dataframe_one_row_per_result

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

Stdin command script begins with LOAD <avl_file> to load geometry

AVL is driven entirely via stdin; LOAD must precede PLOP and OPER to initialize geometry before sweep commands

test_command_starts_with_load

req-cmd-18

Stdin command script loads the run-case file via CASE <run_file> before OPER commands

CASE initializes run-case variables from reset.run; without it AVL uses uninitialized defaults

test_command_loads_case_file

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

req-cmd-19

Alpha and beta values appear verbatim in the A A and B B assignment commands

Formatting errors or rounding in the command string would silently run a different flight condition

test_command_alpha_beta_appear_in_commands

req-cmd-20

Reset run block initialises alpha to 0.0

The reset case must start from zero so subsequent A A commands apply from a known baseline

test_reset_run_scalar_alpha

Binary management (avl_bin)#

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 with no positional CLI args

AVL is driven entirely via stdin; geometry and run-case are loaded via LOAD/CASE commands inside the script

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 verify subcommand returns exit code 1 on FileNotFoundError

Distinguishes binary absent from binary broken so diagnostic scripts can act accordingly

test_cli_verify_subcommand_failure

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

CLI commands and config (avl_cli / avl_config)#

id

requirement

rationale

test

req-cli-1

A complete .yml project file round-trips through load_config without error

Confirms the parser accepts all valid fields without silent data loss

test_load_config_bd_roundtrip

req-cli-2

load_config raises SystemExit when the [sweep] section is absent

Missing sweep section makes the file unusable; fail-fast with a clear error is better than a downstream AttributeError

test_load_config_missing_sweep_exits

req-cli-3

load_config raises SystemExit when the [input] section is absent

Missing input section means no geometry file can be located

test_load_config_missing_input_exits

req-cli-4

load_config raises SystemExit when alpha is an empty list

An empty alpha sweep would produce no .st files silently

test_load_config_empty_alpha_exits

req-cli-5

load_config raises SystemExit when beta is an empty list

An empty beta sweep would produce no .st files silently

test_load_config_empty_beta_exits

req-cli-6

load_config raises SystemExit for an unrecognised output format

Unknown format cannot be written; failing fast avoids a silent no-op after a full sweep

test_load_config_invalid_format_exits

req-cli-7

load_config raises SystemExit when a ctrl_sweeps entry is an empty list

An empty deflection list silently omits that surface from the sweep

test_load_config_ctrl_sweeps_empty_list_exits

req-cli-8

load_config raises SystemExit for non-numeric alpha values

Non-numeric values cannot be passed to AVL; early error avoids a downstream traceback

test_load_config_non_numeric_alpha_exits

req-cli-9

load_config raises SystemExit for syntactically invalid YAML

Malformed YAML is always a user error; fail-fast with a clear message

test_load_config_invalid_yaml_exits

req-cli-10

ProjectConfig defaults output.format to csv and ctrl_sweeps to empty dict when omitted

Sensible defaults reduce boilerplate in simple project files

test_project_config_defaults

req-cli-11

SweepSpec emits a UserWarning when a ctrl_sweeps entry omits 0.0

0.0 is required for neutral aero tables; silently omitting it corrupts stab tables

test_sweep_spec_warns_when_ctrl_sweeps_missing_zero

req-cli-12

SweepSpec inserts 0.0 into ctrl_sweeps entries that omit it

Auto-insertion ensures stab tables are always populated without requiring the user to know why

test_sweep_spec_inserts_zero_into_ctrl_sweeps

req-cli-13

input.geometry is resolved relative to the .yml file’s directory

Geometry and .yml travel together; relative paths survive moving the project directory

test_path_resolution_relative_to_yml

req-cli-14

CLI sweep writes output to _runs// one directory above the .yml

Keeps generated outputs out of the geometry directory without requiring an explicit out_dir argument

test_out_dir_pattern

req-cli-15

CLI sweep passes alpha / beta / ctrl_sweeps / out_dir / out_format correctly to avl_sweep.run

Ensures config fields are not silently dropped or transformed between YAML parse and AVL invocation

test_sweep_passes_correct_args

req-cli-16

CLI sweep exits with error when a ctrl_sweeps key is not a valid surface name in the .avl

Typos in the .yml would silently sweep with no deflections applied

test_sweep_bad_ctrl_key_exits

req-cli-17

CLI plot geometry calls avl_fileplot with the parsed geometry

Confirms the command wires geometry parsing to the plotting function without silent skips

test_plot_geometry_calls_fileplot

req-cli-18

CLI plot geometry writes _geometry.html to the .avl file’s directory

Predictable output path keeps the plot alongside the geometry file it depicts

test_plot_geometry_writes_html

req-cli-19

CLI plot totals auto-selects the latest timestamped subdirectory when given a parent directory

Avoids requiring users to type full timestamps for the most common case of plotting the most recent run

test_plot_totals_picks_latest_dir

req-cli-20

CLI plot totals accepts a specific run directory directly

Enables reproducible re-plotting of any historical run

test_plot_totals_specific_dir

req-cli-21

CLI plot totals exits with error when no sweep results exist in the given directory

Prevents a silent empty plot when the sweep has not been run or the path is wrong

test_plot_totals_no_results_exits

req-cli-22

CLI plot totals exits with error when the given directory does not exist

Catches path typos before attempting to read results

test_plot_totals_nonexistent_dir_exits

req-cli-23

CLI plot totals resolves run directories named _ (prefixed timestamp pattern)

Run directories include the geometry name as a prefix; the auto-latest logic must match this pattern

test_plot_totals_prefixed_timestamp_dir

req-cli-24

CLI plot totals accepts a directory containing a .raw/ subdirectory directly without searching for a parent

Supports direct paths to known run directories bypassing the auto-latest search

test_plot_totals_raw_dir_fallback

req-cli-25

CLI plot totals writes stab.html and ctrl_.html to the run directory

Predictable filenames enable scripted post-processing and the index.html link list

test_plot_totals_writes_html_files

req-cli-37

CLI plot totals –beta-ref forwards the sideslip angle to aero_fileplot

Users must be able to choose which beta slice is shown in control-surface plots without modifying code

test_plot_totals_beta_ref_flag

req-cli-38

CLI plot stab-deriv auto-selects the latest timestamped subdirectory when given a parent directory

Consistent directory-discovery behaviour across all plot subcommands

test_plot_stab_deriv_picks_latest_dir

req-cli-39

CLI plot stab-deriv writes stabderiv_{alpha beta p q r}.html to the run directory

Predictable filenames let users identify which perturbation variable each file covers without opening it

test_plot_stab_deriv_writes_html_files

req-cli-40

CLI plot ctrl-deriv auto-selects the latest timestamped subdirectory when given a parent directory

Consistent directory-discovery behaviour across all plot subcommands

test_plot_ctrl_deriv_picks_latest_dir

req-cli-41

CLI plot ctrl-deriv writes ctrlderiv_{surface}.html per control surface to the run directory

One file per surface keeps plots focused; filenames identify the surface without opening the HTML

test_plot_ctrl_deriv_writes_html_files

req-cli-42

CLI plot all invokes totals stab-deriv and ctrl-deriv in a single command writing all HTML files

Avoids three separate commands for the common case of generating the full plot set

test_plot_all_calls_all_three_plotters

req-cli-43

CLI plot all –beta-ref forwards the sideslip angle to plot_totals

Consistent –beta-ref behaviour between plot totals and plot all

test_plot_all_beta_ref_forwarded

req-cli-26

_write_index_html creates an index.html listing all .html files in the directory

Provides a single entry point for reviewing all plots from a run in a browser

test_write_index_html

req-cli-27

All shipped example .yml files parse without error

Ensures examples remain valid as the config schema evolves

test_example_yml_parses

req-cli-28

ctrl_sweeps keys in example .yml files all match surface names in the corresponding .avl

Prevents shipping broken examples that would error at runtime

test_example_yml_ctrl_sweeps_match_avl

req-cli-29

End-to-end: CLI verify returns exit code 0 with the real AVL binary installed

Confirms the full binary-discovery and health-check path works against the actual installed binary

test_cli_verify

req-cli-30

End-to-end: CLI sweep produces results.csv and provenance.json in a timestamped run directory with the correct case count

Full pipeline smoke test from .yml config through AVL execution to structured output files

test_cli_sweep

req-cli-31

End-to-end: CLI plot geometry writes _geometry.html to the .avl directory

Confirms the geometry plot command runs without error against a real geometry file

test_cli_plot_geometry

req-cli-32

End-to-end: CLI plot aero writes stab.html and ctrl_CLtot.html after a real sweep

Confirms the aero plot command correctly finds the latest run and produces all output HTML files

test_cli_plot_aero

req-cli-33

End-to-end: Python API chain (avl_fileread → avl_fileplot → avl_sweep → aero_filewrite → aero_fileplot) produces 9 results with CLtot in every case

Smoke test confirming every public API function works in sequence against the real AVL binary

test_python_api_chain

req-cli-34

CLI verify subcommand returns exit code 0 when the binary is healthy

Enables scripted health checks and CI pipeline gates to confirm the install is working

test_verify_ok

req-cli-35

CLI verify subcommand returns exit code 1 when the binary is missing

Distinguishes binary absent from binary broken for diagnostic scripts

test_verify_binary_not_found

req-cli-36

CLI verify subcommand returns exit code 1 when the binary fails its health check

Distinguishes binary broken from binary absent for diagnostic scripts

test_verify_runtime_error

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

The LOAD command uses a bare filename; cwd=avl_dir ensures it resolves and that relative paths inside the .avl (airfoil data) also resolve

test_run_passes_avl_dir_as_cwd

req-sweep-3

run creates a timestamped subdirectory {avl_stem}_{timestamp} inside out_dir

Fresh directory per run ensures previous results are never overwritten

test_run_creates_subdir_inside_out_dir

req-sweep-4

Timestamped output subdirectory is named {avl_stem}_{timestamp}

Name encodes the geometry and time so multiple runs on the same geometry are distinguishable

test_run_subdir_named_avl_stem_timestamp

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

stdin LOAD command uses the bare geometry filename (not a full path)

cwd=avl_dir means bare filename resolves correctly and stays under AVL’s ~80-char Fortran string limit

test_run_load_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

out_dir is required; omitting it raises TypeError with message out_dir is required

An explicit out_dir forces the caller to choose where results land; no implicit write to cwd

test_run_raises_if_out_dir_not_specified

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

req-sweep-13

Single-point integration run returns at least one StResult with Alpha and CLtot fields

Smoke test confirming the full AVL binary pipeline produces parseable stability output

test_integration_bd_single_point

req-sweep-14

A 180-case bd sweep (5 alpha × 3 beta × 12 ctrl points at 5° steps) completes within 250 ms/case

Baseline throughput measured at 107 ms/case on a 2025 MacBook; 250 ms/case gives 2× headroom for CI and prevents silent performance regressions

test_perf_bd_full_aero_table

req-sweep-15

run writes sweep.inp to the .in/ subdirectory containing LOAD and CASE commands

Provides a human-readable on-disk record of exactly what was piped to AVL to produce the .st files in that directory

test_run_writes_sweep_inp

req-sweep-16

run emits a UserWarning when a ctrl_sweeps entry omits 0.0

0.0 deflection is required for neutral aero tables; a warning alerts users without halting the sweep

test_run_warns_when_ctrl_sweeps_missing_zero

req-sweep-17

run inserts 0.0 into ctrl_sweeps deflection lists that omit it

Auto-insertion ensures stab tables are always populated regardless of what the caller provided

test_run_inserts_zero_into_ctrl_sweeps

Geometry plotting (avl_fileplot)#

id

requirement

rationale

test

req-plot-1

avl_fileplot returns a plotly Figure

Caller can embed the figure in notebooks or save to HTML via fig.write_html()

test_returns_figure

req-plot-2

Figure has exactly one 3-D scene

Single interactive scene enables rotate/zoom; the four-view matplotlib layout is replaced by the interactive plotly viewer

test_figure_has_one_scene

req-plot-3

All figure traces are Scatter3d or Cone instances

Non-3D traces would flatten geometry in the interactive viewer and misrepresent dihedral

test_all_traces_are_scatter3d_or_cone

req-plot-5

Figure layout.title.text 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 Scatter3d line trace is present

Missing line traces indicate that surface parsing failed or coordinates were not transferred to the figure

test_line_traces_drawn

req-plot-7

CG position is rendered as a Scatter3d marker trace

CG visualization is critical for stability interpretation

test_cg_marker_present

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 mirrored surfaces at least 4 Scatter3d line traces are present

Mirrored surfaces must produce traces on both sides of the symmetry plane

test_mirror_doubles_traces

req-plot-10

Geometry with a body section plots without error

Files with fuselage bodies must not raise during plotting

test_body_geometry_runs

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

req-write-19

StabTable.data shape is (n_alpha × 1) and values are correct with multiple alpha breakpoints

Multi-alpha tables must not collapse rows or reorder values during pivot from list to 2-D array

test_multiple_alphas_stab_shape_and_values

req-write-20

AeroDatabase.stab_deriv contains all 30 stability-derivative keys (CLa through Cnr) as StabTable objects

All linearised stability derivatives must be accessible for analysis and plotting — missing keys would silently skip derivatives

test_stab_deriv_tables_exist

req-write-21

stab_deriv table values match the fixture data at the correct (alpha beta) index

Validates that the extraction loop maps AVL derivative keys to the right array cell without off-by-one or key collision

test_stab_deriv_alpha_value

req-write-22

stab_deriv tables are NaN for off-neutral runs

Stability derivatives are computed at neutral control; mixing in deflected values would corrupt the linearisation

test_stab_deriv_only_neutral

req-write-23

AeroDatabase.ctrl_deriv contains one StabTable per (coef surface) pair with keys in {coef}{d_idx}{ctrl_name} format

Control derivatives must be keyed consistently so plots and downstream code can look them up without guessing the format

test_ctrl_deriv_tables_exist

req-write-24

ctrl_deriv table values match the fixture CLd01 and Cld02 values at the correct array cell

Validates that CTRL_DERIV_COEFS × d_idx indexing maps to the correct AVL key without transposition or coef confusion

test_ctrl_deriv_value

req-write-25

ctrl_deriv tables are NaN for off-neutral runs

Control derivatives are linearised at the neutral condition; deflected runs must not corrupt the derivative baseline

test_ctrl_deriv_only_neutral

Aero database plotting (aero_fileplot)#

id

requirement

rationale

test

req-aeroplot-1

plot_totals returns a dict mapping string keys to Plotly Figure objects

Caller iterates items() to get both the name and figure without a separate name list

test_returns_dict

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_plotly_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_subplot_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_surface_per_control

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_titles_contain_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

req-aeroplot-10

aero_stabderivplot returns exactly 5 Plotly figures (one per perturbation variable α β p′ q′ r′)

Five perturbation variables are always present in AVL output; returning fewer would silently drop derivative groups

test_aero_stabderivplot_returns_5_figures

req-aeroplot-11

Each stability-derivative figure title contains the partial-derivative notation for its perturbation variable

Title must identify the variable so saved HTML files are self-describing without opening them

test_aero_stabderivplot_titles_contain_pertvar

req-aeroplot-12

plot_stab_derivs returns an empty dict when stab_deriv is empty

Graceful empty-data handling prevents crashes when plotting a database built without derivatives

test_aero_stabderivplot_empty_stab_deriv_returns_empty

req-aeroplot-13

aero_ctrlderivplot returns one figure per control surface

One figure per surface keeps the plots focused; mixing surfaces on one figure obscures which surface drives each coefficient

test_aero_ctrlderivplot_returns_one_figure_per_surface

req-aeroplot-14

Each control-derivative figure title contains the surface name

Title must identify the surface so saved HTML files are self-describing without opening them

test_aero_ctrlderivplot_titles_contain_surface

req-aeroplot-15

plot_ctrl_derivs returns an empty dict when ctrl_deriv is empty

Graceful empty-data handling prevents crashes when plotting a geometry with no control surfaces

test_aero_ctrlderivplot_empty_ctrl_deriv_returns_empty