Skip to main content

pyflightstream

ci PyPI DOI

Version-aware, didactic Python driver for the FlightStream panel-method solver. Successor of the project's own legacy research scripts. MIT licensed.

Documentation

nevesgeovana.github.io/pyflightstream

New here? Start with Getting started, which walks a first workspace file by file and says what each of the four kinds of artifact answers. Then The workspace and the workflow is the reference chapter, and Which build do I have answers the question a version-aware driver exists for.

Every example on those pages is compared against an artifact this repository ships, most of them under tests/tier3_licensed/, by a test on every commit, so a page cannot quietly stop being true.

Status: v0.16.0 is the current release. It publishes to PyPI and archives on Zenodo from the tag, so the concept DOI in CITATION.cff resolves to the newest archived version and the version DOI is recorded one commit after the tag that names it. CHANGELOG.md carries the release history.

v0.16.0 is the release the reference workspace asked for. The additions came from running 0.15.0 at work and reading what came back, and two of them are defects found while measuring for the others. A surface-section distribution is created AFTER the solver is initialised, which is where the recorded reference scripts put it and which is why fifty sections said nothing; a steady row now CREATES the probe points it exports, instead of asking the solver to export something nobody made; a probe entry prescribes a rectangular or a circular plane, point by point, or cites a points file the user wrote; and [probes] became [[probes]], a list of tables, so one artifact can probe several frames.

What a campaign WRITES moved with it. A simulation's collected outputs live under sims/<sim>/datapoints/DP-<point>/, the per-polar tables under post/<matrix>/polars/, and the flow-field samples under post/<matrix>/probes/ whatever the run type was. Each polar and group also gets one derived file, SUPER-...csv, whose columns are a superset of everything the workspace knows about that simulation: if you have to open a second file to know something about it, that file failed.

Two new answers a study wants before it is run and after. pyfs-matrix plan --cost tables what each POINT will cost: mesh size, trailing edges marked, farfield layers, viscous coupling, run type, time steps, processors, an expected time, and the number of recorded runs that estimate was fitted from. The table says under every printing that the time is an extrapolation from this workspace's own recorded wall times and not a measurement, and a point with no comparable recorded run gets no number at all. And a probe table now says WHERE each sample is, with the frame it is measured in, which an unsteady export never stated at all.

A swept row now runs and is judged end to end. Each point collects its outputs into its own folder, sims/<sim>/datapoints/DP-<point>/, named by the same point tag that ends the run id and names the generated script. Until this release every point of a row wrote into one shared folder, so from the second point onward the standard assessor found two files that both read as loads tables and refused rather than guess between them. Name your outputs per point, as before: two points of one row still may not share a file name, because the per-point products are named after it and would collide there even though the folders no longer do. A workspace recorded under the older layout is read exactly as before.

What changes for you at v0.16.0. This is the most breaking release of the set, and the break is first: [probes] IS NOW [[probes]], a list of tables, and the old spelling is REFUSED BY NAME, so every 0.15.0 artifact that declares probe lines must be edited. That is a recorded decision rather than an accident. Beside it: the five names the 0.14.0 polar rename deprecated are gone; a simulation's collected outputs are under sims/<sim>/datapoints/DP-<point>/, one folder per point, and a workspace holding either older folder is still read; the per-polar tables moved to post/<matrix>/polars/ and are named by the point convention with the swept variable written literally as sweep; and sweep.csv is gone in favour of campaign_sweep.csv, which it duplicated byte for byte.

What changes for you at v0.15.0. The run matrix LOSES A COLUMN: SWEEP_TYPE is gone, because a sweep is applied to a variable that DEFINES the flight condition and the FLIGHT_CONDITION cell says which by carrying the word sweep where that key's value would be. Run pyfs-matrix upgrade <path> --in-place, which folds the cell and DOES NOT RENAME A RUN: a held angle is carried at every point, so the point tags that end every run_id in your manifests are the ones the converted file plans under, and a resume after the upgrade finds its records. The one row it refuses to convert is one that sweeps BOTH angles, which is one row per sideslip and each needs a POL of its own. CLOCK_MOTION is now REQUIRED on a row that states a MOTIONS list, naming the rotor that owns the time step; the flat pre-0.15.0 form is exempt. The airframe and blades family SELECTORS are REFUSED since 0.15.0, not warned about: declare the set in your reference's [aliases] table and cite it by name, because those two decided what a blade IS from a pattern over the family name and a mesh spelled another way was guessed wrong in silence. An alias of the same name is read FIRST and keeps its own meaning, so a reference that already declares airframe is untouched. New: --ignore-missing-families false on plan and run turns a family the opened mesh does not carry from a skip into a refusal, for a run against the one geometry you believe carries everything.

What changes for you, and what you must do. v0.13.0 changes no column of the run-matrix file. Two inputs that used to plan are refused at plan time now, each naming the cell: a row carrying a key its run type does not register (a misspelt key planned READY and reached the solver as nothing), and a pproc artifact whose [groups] table is keyed by a word or whose groups cite a boundary name the opened geometry does not carry. Python 3.11 leaves the supported window: the floor follows SPEC 0, the scientific-python schedule, computed on 2026-09-09 as Python 3.12, numpy 2.2 and pandas 2.3, and the window now moves by a published rule rather than by choice. The hand-built physics runner is gone: run_physics, run_drift and the four build_phy*_script builders are removed, and pyfs-qa physics --workspace <root> and pyfs-qa drift --workspace <root> read the physics cases as rows of a campaign workspace; an import of a removed name says so and names the replacement. Anything you wrote against runs.json or the campaign products meets three moves: the manifest key broken_commands is waived_commands and the schema stamp is pyfs-manifest/3 (the old key is still read, and goes when no recorded workspace carries it rather than on a stated release); plan.json, campaign_sweep.csv and the product tables live under post/<matrix stem>/, one folder per matrix; and a registered post stage is called with a third keyword, matrix_stem. sweep_editions is manual_editions and propose_type takes its two strings by keyword; the old spellings warn and name the release that removes them. A simulation folder has three managed subfolders, since parsed/ was never written to; an empty one left by an earlier release is left alone. At v0.14.0 one artifact key is renamed: [products] her_polar_format is custom_polar_format, and the four Python names HerPolarTable, her_polar_file_name, write_her_polar_format and read_her_polar_format are spelled custom; the old spellings were read and warned until 0.16.0, WHICH REMOVES THEM. The format itself is untouched: only the spelling that named a person was ever deprecated.

What it adds. An unsteady row may say when its exports begin, EXPORT_UNSTEADY_AFTER_REV or EXPORT_UNSTEADY_AFTER_ITER, and the run registers the two solver actions that make the solver export from that step on, measured on 26.123. The products stage writes a PROV-JSON provenance document per point, every reduction of an unsteady row (the time average over the window the row states, the phase-locked passages, the per-blade split), and the polar tables in the custom polar format when the pproc artifact asks for it. The geometry library may hold one folder per geometry beside the flat layout, pyfs-workspace migrate-geometries moves a library into it, and pyfs-workspace archive <root> <sim_id> zips one recorded simulation. Every command-line option states what it reads, the run record says how the solver was called (executor, export_window, the waived commands by their name), and the whole test suite is organized by tier, with the licensed tier a campaign workspace of seven run matrices whose goldens are rendered offline on every commit.

v0.12.0 changed no column of the run-matrix file and no cell already in one. It added two ways to say what you were already saying. A setup artifact may carry a [flight_condition] table holding the fluid pins (RHOkgm3, MUPas, ASMPS, TK, PPA), so a thirteen-point polar states its campaign's constants once instead of thirteen times; a row that states a pin still overrides the setup's, and the resolved state is the same state either way, which a test holds to the rendered script BYTE FOR BYTE. And an unsteady run that meshes nothing turning may state its clock as DELTA_THETA and REVOLUTIONS, resolved against the rotor speed whose azimuth the step measures, where before only the seconds and the step count were accepted. The reproduction workspace of the reference campaign needs both, which is why that release existed.

Two derived numbers moved with it, and a row already written felt them. A rotor speed derived from ADVANCE_RATIO is emitted at four decimals, the reference precision: 473.1723 rev/min where the unrounded derivation gives 473.17227304. And the default loads assessor judged a point's OWN declared outputs, so a two-point sweep whose points name their own tables is judged where it used to be refused as ambiguous. Re-baseline against v0.12.0 rather than comparing its tables with v0.11.0's.

v0.11.0 changed the run-matrix FILE FORMAT, and one command moved a workspace. The ENTRY column is now PPROC and names the post-processing artifact, the FS_SCRIPT column is gone and a LEGACY row carries its recipe code in its cell, and the GEOMETRY cell names the file with its extension. Run pyfs-matrix upgrade matriz.fs --in-place --inputs inputs once in the workspace: it rewrites the matrix, moves the groups library to inputs/pproc/ and strips the four rotor facts from the reference artifact. What you get is the study stated once, in the workspace: the campaign is named after its directory, the post-processing artifact says which exports, sections, plots, probes and products every point leaves, the products are CSV tables the run writes itself (pyfs-matrix post rewrites them with no solver), the boundary order of a geometry is read from the file (pyfs-matrix inventory), a row may state several rotors, and every point opens its geometry through a link rather than a copy. The reproduction of the reference campaign, script by script and product by product, is the exit condition of GOAL-011 and is what that release was built against.

v0.10.1 changed no column of the run-matrix file and no cell you had already written. What it changed is what one cell MEANS.

A rotor row's MOVING_BOUNDARIES used to cite boundaries by their POSITION in one geometry's boundary order. Those positions were right for the file they were written against and named different surfaces in any file that ordered them differently, and nothing said so. Write the names now, or the FAMILY they belong to, and the package reads the geometry the row opens and resolves them:

MOVING_BOUNDARIES: Blade,S

One cell, and it is right for a three-boundary sector and an eight-boundary wheel alike. Positions still work and now warn, naming the surfaces they actually selected, so nothing you have written stops running.

And this patch carries a third run type, unsteady, an unsteady run with nothing turning. It is a new capability under a patch number, by an explicit exception, and it is named here because a patch number will not carry that news on its own. It asks nothing of you: no existing row changes. docs/workspace-and-workflows.md describes it.

What v0.10.0 asked of you, kept here because a reader may be arriving from 0.9.x. v0.10.0 changes no column of the run-matrix file, so no conversion is needed. Two things still want a look before you re-run anything.

Your solver preset now reaches the script, and that is the SILENT half. A preset key naming a real setting used to be honoured only where the model already defaulted to it: a preset asking for SUBSONIC_PRANDTL_GLAUERT ran INCOMPRESSIBLE and said nothing. Twelve settings and eleven of the solver's own key spellings now reach the emitted script. Nothing refuses and nothing warns; the NUMBERS move. On the reference campaign the axial force coefficient moved about one percent. If your preset states anything the solver was not already defaulting to, re-baseline before you compare against an older run.

The loud half is smaller: a preset key this package cannot emit at all used to be dropped with a warning and is now refused, naming the key and listing what applies. The remedy is usually one line in the preset file, and for one key it is a line in the row instead, which the refusal says.

Five names are now the package's inside VAR_NAMES_VALUES: ADVANCE_RATIO, RPM_SIGN, DELTA_THETA, REVOLUTIONS and LOG_OUTPUT. The match is on the exact key. If a cell of yours already spells one, rename yours or adopt the meaning; a row spelling ADVANCE_RATIO or RPM_SIGN beside an existing RPM is REFUSED, because that states the rotor speed twice.

If you are coming from v0.8.x you still have the older job. If you have a run matrix written then, v0.9.0 changed its FILE FORMAT and you must convert it. The RE and MACH columns are gone, replaced by one mandatory FLIGHT_CONDITION cell. A file written under either older layout is recognised and refused rather than misread, so nothing runs silently wrong; run this on it:

pyfs-matrix upgrade your_matrix.fs --in-place

Then read the next sentence before you re-run a campaign. The conversion is lossless as a FILE, every value moving across verbatim, and it is not neutral as a RESULT. Under v0.8.x the RE column was recorded metadata that reached no emitted line; from v0.9.0 REmi is a constraint that solves for density, so every upgraded row emits a fluid state it never emitted before and your numbers will move. How far they move is not something this repository can tell you, because the row previously emitted NO fluid state at all and what the solver assumed instead is a question about the solver. What is measured, on this repository's own fixture, is the state your row will now be run at: one row solves at 1.3319 kg/m3, 8.7 percent above sea level. To keep the previous behaviour, state the condition without REmi. docs/flight-conditions.md is the whole grammar, the units, and the migration in full.

The earlier upgrade action still applies if you are coming from before v0.8.1: three names inside the free VAR_NAMES_VALUES cell of a run matrix are the package's: GEOMETRY, SYMMETRY and PERIODIC_COPIES. If a cell of yours already spells one of them, the package reads it, so rename your key or stage the file the id names. The match is on the exact key, so a cell spelling it SYMMETRY_TYPE is untouched, and a row that names none of the three renders the script it always did.

What v0.8.1 fixed, and it is why a patch followed v0.8.0 by two days. A run matrix had no way to name a geometry at all, so a workflow built a script with no OPEN in it: the solver solved whatever it already had in memory, and reported numbers with nothing said. A row names its geometry now, and declares the symmetry a periodic sector needs, which was fixed at NONE so a sector ran as a one-bladed rotor and converged. Separately, pyfs-matrix run could not run at all under its own documented default: --workspace defaults to "." and every path was then spelled from your directory while the solver resolved it from the run's.

Everything each earlier release broke or added is described under its own heading in CHANGELOG.md, which carries the reasoning, the measurements and the rejected alternatives behind every entry. The word "unchanged" used to stand here about v0.8.0's four breaking changes, and a review pointed out that a reader cannot check it: the four were never enumerated, and v0.9.0 has just changed the same file format again.

pip install pyflightstream

A first taste, no solver required (build time is where errors surface):

from pyflightstream.commands import CommandNotInVersionError
from pyflightstream.script import Script

script = Script(version="26.120")  # the FlightStream version is explicit input
script.emit("NEW_SIMULATION")
script.emit("IMPORT", "METER", "STL", "wing.stl", clear=True)
script.emit("SOLVER_SET_AOA", 4.0)
script.emit("START_SOLVER")
print(script.render())  # validated ASCII script, ready for the solver

# The version is checked, and the check ASSERTS rather than prints:
# an example whose only claim is that something is refused goes green
# in CI the day the refusal stops firing, which is how this block came
# to promise a refusal it no longer made.
try:
    Script(version="26.0").emit("CAD_CREATE_BOX", frame=1, x=0.0, y=0.0, z=0.0,
                                len_x=1.0, len_y=1.0, len_z=1.0)
    raise AssertionError("26.000 has no CAD chapter; this must refuse")
except CommandNotInVersionError as error:
    print(error)  # the CAD primitives arrive at 26.100

The worked examples in examples/ take it from here to executed polars, campaign matrices, a static wing deflection, and a Campbell diagram. No example runs the coupled loop: it needs a licensed solver in the loop, and the rotary case was solver-blocked on 26.120 and 26.121 (reports/RPT-007), which reports/RPT-025 measures fixed in 26.122. The capability status below says what the FSI subpackage does and does not claim.

Optional extras: [fsi] (aeroelastic coupling, PyNiteFEA), [geom] (the SPATIAL INDEX behind probe-survey geometry gating, rtree and scipy; the mesh reader itself, trimesh, left this extra and is a runtime dependency since v0.8.0), [plot] (matplotlib for the plotting examples), and [manual] (pypdf), which is maintainer tooling rather than a user feature: it backs pyfs-manual and nothing in a run imports it.

Why this package

FlightStream is scripted through an ASCII command file, and the solver is under active development: the FlightStream team is responsive to user requests and works with intermediate hotfix builds that consolidate into stable releases. A fast-moving solver naturally means a scripting command set that evolves from version to version, faster than any single document can track. This package makes the FlightStream version an explicit input: every command it emits is validated against a per-version command database, and old versions are only ever added, never dropped, so campaigns stay reproducible across that evolution.

Every database entry carries exactly one piece of evidence: the manual page that documents the command, or, for the few the solver accepts and no manual edition describes, a committed probe report measuring that it does. Every status that rests on a RUN (verified, broken, and a removed the solver refused) can only be promoted by citing a committed probe report from a licensed machine; a removed read off an edition is a hand-written row carrying a note and a page. Nothing is guessed; the honest gaps are reported as such.

What ships

  • Command database with per-version evidence and a manual or probe-report citation on every entry, browsable offline via pyflightstream.help() (including a manual-coverage section) and as a generated docs site; the compatibility matrix carries the live counts.
  • Validating script builder with curated helpers: phase ordering, didactic refusals at build time, entity labels (recipes can name frames, actuators, motions, and boundaries instead of raw indices), and a solver-setup provenance snapshot recording the effective value of every solver flag per run. A command a probe measured broken in the target version is refused too, because that one produces a complete run with wrong numbers rather than no run at all; the waiver that emits it anyway records the report and the reason in the manifest.
  • Campaign workspace: an input-artifact library (references, solver presets, boundary groups, geometries, profiles, executables by build id), a run manifest as the single identity authority, output naming templates, campaign pre-flight with zero solver time, and resumable incremental sweeps.
  • Runner and parsers: headless execution, anchor-based parsers for the solver outputs, and a pandas table layer (per-result tables, one wide row per run, whole-sweep DataFrame straight from the manifest).
  • Run-matrix support as a first-class interface: read, upgrade, convert, pre-flight, and run the pipe-delimited 13-column matrix format. Each row states its whole flow condition in one mandatory FLIGHT_CONDITION cell, and WORKFLOW names the run type the package builds for that row. A matrix written under any older layout is RECOGNISED and refused naming its converter: pyfs-matrix upgrade <path> --in-place.
  • Far-field probe surveys (planar grids, geometry gating, VTK/Tecplot writers, conservation ledgers on xarray) and an aeroelastic coupling subpackage (structural beam, coupled driver, replay harness).
  • Architecture overview from the live module docstrings via pyflightstream.overview().
  • Predictable surfaces: a declared-options registry (pyflightstream.options), one public exception catalog (pyflightstream.exceptions), test assertions with quantified reports (pyflightstream.testing), and the house conventions rendered by help().

Capability status

Not every subpackage carries the same weight of evidence, and until 2026-08-03 nothing said so: an independent review found the feature list above reading as one uniform claim, with the aeroelastic boundary stated only inside the code. The design decision of 2026-08-03 is that the FSI and probe-survey paths are experimental behind an explicit boundary rather than release-supported, and this table is that boundary.

Capability Status Evidence
Command database, script builder, version refusals supported tier 1 over every registered version; probe reports under reports/
Parsers, tables, run manifest, reconstruction supported tier 1 on committed solver fixtures
Campaigns, run matrices, workspace, pre-flight supported tier 1 end-to-end with a stub solver; tier 3 on the licensed machine, tests/tier3_licensed, a workspace of seven matrices with one test per row
Far-field ledgers and probe surveys experimental tier 1 on synthetic fields; the licensed far-field acceptance work is deferred, not done
FSI structural beam and modal analysis experimental tier 1 against analytic beam solutions; examples/wing_static_deflection.py, examples/fsi_campbell_diagram.py
FSI coupled driver (the four-phase loop) experimental tier 1 offline replay on archived WP1 fixtures only; never run against a live solver in CI
Rotary two-way coupling not validated reports/RPT-025 measures the solver defect of reports/RPT-007 fixed in 26.122 and still present in 26.120 and 26.121, so the path is no longer blocked on 26.122. It stays NOT VALIDATED: that report says only that an imposed deformation reaches the mesh, not that the morphing is correct, and no coupled acceptance run has been made against a build where it applies

Experimental means the interface may change without the deprecation window of NFR-20, and that the evidence behind it is narrower than the supported rows: replaying archived fixtures shows the machine runs, not that its physics is right for a case nobody has measured.

Command-line tools

Tool Purpose
pyfs-qa Tier 2 command-validity probes, Tier 3 physics regression and cross-version drift read out of a campaign workspace's physics matrix, status promotion from committed reports
pyfs-workspace Initialize the managed campaign workspace tree (init); zip one recorded simulation under archive/ (archive); move a flat geometry library into one folder per geometry (migrate-geometries)
pyfs-matrix Upgrade, convert, pre-flight and run run matrices
pyfs-manual Compare FlightStream manuals against the command database: one manual, every registered edition at once (sweep, which reports both what has no entry and what an edition documents that its build cannot emit), what each build documents and what changed between builds (surface), or whether the citations already written still point where they say (citations, the one subcommand that fails by default on a finding, because a citation that does not hold is a statement already shipped rather than work remaining). register carries a build's documentation forward: for every command a new edition describes exactly as its predecessor did, it writes a documented row citing the new edition, and it reports rather than writes anything described differently. Maintainer tool; needs the [manual] extra, and only draft and register write, both with --write and both dry-run by default. register is the one that edits the command database rather than emitting a file
pyfs-fsi The structural executable of the aeroelastic coupling loop

Supported FlightStream versions

"Supported" covered four different states, so it is now four named values (pyflightstream.SupportLevel), every one of them derived from the evidence rather than declared:

Version Vendor name Support level What that means here
25.000 25.0 documented Vendor build 12162024, December 2024. Registered on 2026-08-09 so that published work run on it has an identifier that resolves, and its own manual read command by command on 2026-08-10. Not yet operational, and the blocker is solver evidence rather than database rows: no command has been measured on this build, and the level stops at documented for that reason before the workflow is even considered. 26.000 shows it, having every workflow command and the same level. Its manual documents 272 commands and 268 are emittable, the difference being four readings withheld where a version row cannot express a layout (PLN-20260810-1200). Behind the evidence gap there is also a workflow one: this edition runs the trailing-edge autodetection from inside a PHYSICS block and the standalone command arrives at 26.000
25.100 25.1 documented Vendor build 5062025, May 2025. Registered for the same reason. Its manual documents 274 commands and 270 are emittable, with the same four layout withholdings, and it uses the same PHYSICS block as 25.000. The 25 series checks out an EDU licence rather than the full feature set, so what either of these builds refuses may be the licence rather than the build; that is not yet measured
26.000 26.0 documented Vendor build 10202025, October 2025. Its manual documents 276 commands and 274 are emittable, the two withheld for the same layout reason. Nothing has been probed on it either, which is what holds it at this level. The CAD BODY operations, the four CAD primitives and the three CCS mesh chapters do not exist in this edition; they arrive with 26.100. What it does document, and what its sixteen CAD rows are, is CAD import and conversion plus the curve and cross-section commands
26.100 26.1 operational The February 2026 build, and the last of the pre-26.12 builds to reach this level, on 2026-08-08. It was held at verified less by the solver than by the database: the per-edition sweep that day found 40 commands its own manual documents and this database had no row for, so the emitter refused them and the minimal end-to-end workflow could not be built. With those rows written the workflow builds. Probe coverage is still thinner here than on the newer builds, the harness reaching only commands that carry a probe spec; the compatibility matrix carries the live counts
26.101 26.1 operational The May 2026 build. Commands drafted from the manual with page citations, with the first harness promotions on 2026-08-08, which also carried it to the level where the minimal end-to-end workflow builds. It sits at a hotfix index and does NOT inherit from 26.100: the two are separate vendor releases under one name
26.120 26.12 operational Probe evidence from a licensed machine, and the minimal end-to-end workflow builds
26.121 26.12 operational Hotfix build 1. It inherits the 26.120 records except where a probe on this build overrode them; the compatibility matrix marks every inherited cell and counts them
26.122 26.12 operational Hotfix build 2, vendor build 8092026, registered 2026-08-10 the day after it was issued. Its manual documents the largest command surface of the nine editions, 372 against 364 for the one before it and 371 for the one after, which deletes a command from its chapter body. Measured on 2026-08-11: 84 commands probed on this build (83 verified, 1 broken) and the Tier 3 matrix passing 30 of 30 metrics (reports/physics/PHY-26122_2026-08-11_rotor.yaml). The rest of its record is still inherited from 26.120 and the matrix marks every inherited cell. The run refuted the inheritance once, on AIR_ALTITUDE, which is broken on the base releases and works here
26.123 26.12 operational Hotfix build 3, delivered 2026-08-16 and registered 2026-08-17, the day after. It is the first build in this project that INHERITS NOTHING, by the design decision, so it claims support only for what has been measured or read on IT rather than on 26.120. Read the level as a statement about evidence and not about the build: it reached operational the same day, in two steps. First 369 of the 371 commands its own edition documents were compared word for word against the edition before it and given a row. Then a probe run measured 85 of them on this build, 84 accepted and one refused (reports/compat/CMP-26123_2026-08-17_full-sim.yaml), which is one more verified than 26.122 has and the same single broken command, NEW_OFF_BODY_STREAMLINE, that three builds now carry on their own evidence. The emitter still refuses the commands that carry no row at all, and the enumeration of exactly which, with their count in its own header, is committed as tests/tier1_offline/goldens/absent_on_26123.txt. The number is NOT repeated here: this sentence said 45 against a golden that says 43, in the paragraph whose whole purpose is that the gap is a number a reader can check. Its manual is 417 pages like 26.122's and every page outside seventeen is text-identical, so a page citation transfers where the seventeen do not touch it
import pyflightstream

for row in pyflightstream.support_table():
    print(row.summary)

operational is the level that claims a user can get from geometry to a loads file, and it is checkable rather than asserted: it holds only when pyflightstream.support.minimal_workflow(version) builds, which a tier 1 test builds for every version reported at that level.

Not sure which one you have? Every install prints its release name and its build number when it starts, and the generated Which build do I have page maps that pair onto the identifier to pass. The release name alone does not identify a build.

Canonical identifiers use the YY.XXX scheme, the last digit indexing vendor hotfix builds, so 26.121 is hotfix build 1 of the 26.12 release. The vendor reuses a release name across builds, so a release name may name more than one, and the families it has produced are not all alike: one is a release with its hotfixes, the other is two separate releases that happen to share a name. Which builds sit in either is a fact about the registry rather than about this page, so it is not written here; the refusal enumerates them from the registry and the generated build page carries the tally. Pass the canonical identifier. A vendor name is unique only until the vendor ships the next build under it, which is why a script should not rely on one. The ordered list in src/pyflightstream/commands/_meta.yaml is the only ordering authority, and it orders releases, not support: 26.100 is newer than 26.000 and both sit below 26.120. Supported versions are only ever added, never dropped, which is why the February 2026 build entered as 26.100 and the May build it displaced was appended as 26.101 rather than either being renamed away. The compatibility matrix in the docs is generated from the database at build time.

What is each folder?

Folder Purpose in plain language
src/pyflightstream/ The package, one subpackage per pipeline stage (versions, commands, script, results, cases, run, workspace, post, qa, plus fsi, probes, farfield)
src/pyflightstream/commands/ The command database: what exists in which FlightStream version, with a manual page or probe-report citation per entry
tests/ Three tiers in three folders: tier1_offline runs anywhere with no FlightStream, tier2_validity and tier3_licensed need the licensed machine, and tier3_licensed IS a campaign workspace (docs/tiers.md)
reports/ Committed evidence from licensed machines: command validity (compat), physics regression, drift, and research cards
docs/ Documentation source (ProperDocs); reference pages are generated from the database, never committed
examples/ Runnable example scripts in percent format
guide/ LaTeX source of the user guide (the built pdf never enters Git)
deprecated/ Discontinued public items, grouped here instead of scattered at the top level
_private/ Local only, never committed: FlightStream manuals, executables, research geometry, the design documents and the plan ledger

Development setup

Maintainers: no tracked file here reads a named environment variable to configure this package, and a fresh clone is owed no list of them. The wording is careful and the earlier draft was not: the run layer does hand the AMBIENT environment to the solver subprocess, because the solver's own licence server address and installation variables live there, and a sentence saying this repository reads nothing at all would send a maintainer hunting a licence failure that is not a bug. What no tracked file does is read a NAMED variable to decide how this package behaves. The licensed solver is located by a required argument that is never guessed, --fs-exe on the quality-assurance command line; a run matrix locates it instead through its workspace's own executables table. An earlier version of this paragraph said a few machine-specific variables located the solver and its manuals for the licensed tiers, and pointed at a file that stopped being published. Both halves were wrong, and the paragraph shipped inside the package metadata, so it reached every reader of the published page.

The contributor rules a clone does need are the numbered hard invariants in CONTRIBUTING.md.

pip install -e .[dev,fsi,geom]
pre-commit install
pytest

Tier 1 (the pytest suite) runs anywhere. Tier 2 (command validity probes) and Tier 3 (physics regression) require a local FlightStream license and are documented in CONTRIBUTING.md, which also says which extras a full run needs and why two of them are left out on purpose. The docs build with properdocs build --strict.

License

MIT. Contributions must be original or MIT-compatible; code derived from the AGPL pyFlightscript package is not accepted. See CONTRIBUTING.md.

Release files for pyflightstream 0.16.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pyflightstream 0.16.0
File Size Uploaded
pyflightstream-0.16.0.tar.gz 1.1 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for pyflightstream 0.16.0
File Interpreter ABI Platform
pyflightstream-0.16.0-py3-none-any.whl Python 3 none any Details

Total release size: 2.2 MB

Release files / pyflightstream-0.16.0.tar.gz

Download URL pyflightstream-0.16.0.tar.gz
Size 1.1 MB
Tags Source
SHA-256 checksum
How to use checksums
347a58b900ee47cb3ab835f210a8d9efe6b1628ffbf2cb708f80a0c2a7d9b557
BLAKE2b-256 checksum
How to use checksums
eeb2fe1f9ae33c6332574934211d10f06fd5361b2de919f0627957b91dfcdc67
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 12, 2026.

Transparency log

Release files / pyflightstream-0.16.0-py3-none-any.whl

Download URL pyflightstream-0.16.0-py3-none-any.whl
Size 1.2 MB
Tags Python 3
SHA-256 checksum
How to use checksums
0e478af287c9ca1a1e8e846c50b139e0cb6e94a18940794635612b09bffaba92
BLAKE2b-256 checksum
How to use checksums
eb2f9b7fd5718f7e72128f967c1f2fe02676a073efa810c2fa59c91b6291a1d1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 12, 2026.

Transparency log

Release history Release notifications | RSS feed

0.27.0

2 release files

0.26.0

2 release files

0.25.1

2 release files

0.25.0

2 release files

0.24.0

2 release files

0.23.0

2 release files

0.22.0

2 release files

0.21.1

2 release files

0.21.0

2 release files

0.20.1

2 release files

0.20.0

2 release files

0.19.0

2 release files

0.18.1

2 release files

0.18.0

2 release files

0.17.0

2 release files

This release

0.16.0 This release

2 release files

0.15.0

2 release files

0.9.0

2 release files

0.8.1

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page