Skip to content

Migrating to 0.25.0

This release stops publishing averages of a frozen solve, changes where an unsteady probe gets its samples, and adds sections and Cp files named by distribution. Read this before upgrading a workspace recorded with 0.24.0.

Post a recorded campaign again, with no solver:

pyfs-matrix post --workspace <root>

0.25.0 reads runs recorded with 0.24.0 without rewriting their records. Each product is written or skipped with its reason in products.json. Posting again can use only the evidence the run kept: missing probe histories, plot names and section identities are named below where a NEW run is needed.


1. What posting again changes

A frozen unsteady solve is a failure

A point whose averaging window touches a frozen step had no solution to average. Re-posting such a record with 0.25.0 leaves that average out with the reason recorded in products.json, even if 0.24.0 recorded the point as a success. The unsteady polar row, rotor table and reductions follow this rule; an earlier post's stale file is archived.

The native log identifies the freeze: at least two consecutive time steps whose inner iterations after the first all print exactly zero velocity residual and whose last inner iteration prints both residuals exactly zero. Assessment records FAILED_DIVERGED, naming the first frozen step and the count. Post reads the log again and excludes an average whose window ends at or after that first step. Windows ending before it, raw histories and explicitly instant products remain available. A new solve is needed to obtain a solution over the frozen stretch; posting again cannot repair it.

ADVANCE_RATIO in the unsteady polar

An unsteady polar of an ADVANCE_RATIO sweep now states the point's J under ADVANCE_RATIO on every row. It wrote NA in that column before. Post again to recover the swept axis from the recorded point.

A rotor table that cannot be planned says why

If a simulation's matrix row was deleted, or its reference no longer resolves, the rotor table is a named skip at polars/<sim>#rotor_tables in products.json. It previously disappeared. Restore the matrix row or repair its reference and post again.

Skip reasons survive when no point has usable loads

When no point of a simulation has usable loads, its per-point reasons now reach products.json. They were collected and then lost. Re-post to see which points were left out and why; an empty simulation is no longer an unexplained absence.

A rotor table from a family-templated group needs recorded plot names

A group named with {family} can supply the unsteady rotor table. A new run records the plot names it emitted, with their frames, families and parameters. Post sums an exact, non-overlapping set of recorded groups in the global MRP frame covering all of the rotor's families, including any general families.

A run recorded before 0.25.0 lacks that emitted-group record and keeps the named skip for this source. Editing the pproc cannot recover those plot names and their meaning; a new run supplies the record.


2. The probe source follows the run type

Every unsteady probe is a fluid plot

On an unsteady row, every [[probes]] entry uses fluid plots, whether it draws a line or cites a points_file. Each point and requested parameter is sampled through the history. A pproc mixing the two forms now yields ONE probes/<point>_probes.csv table, with one row per point and solver step. The probe-points instant export no longer supplies an unsteady probes table.

The cited profile is read at plan: a count followed by X,Y,Z,TYPE CSV rows, with type 0 or 1. Both types supply fixed vertices to fluid plots, using the entry's frame, scale and parameters. Invalid counts, coordinates or types are refused before solving. The script no longer imports or exports standard probe points for an unsteady row, and its default outputs lose {name}_probes.txt (seven outputs instead of eight).

A cited profile recorded as an instant has no history

An unsteady run recorded before 0.25.0 whose cited-profile probes were exported as an instant has no history for them. Posting again leaves those probes out, names the profile and reason in products.json, and keeps the drawn-line histories the same run did record. A new run is needed for the cited profiles' history. An instant cannot be turned into one by re-posting.

A steady probe's parameters list only enables the entry

On a steady row, drawn probes and points_file profiles still use the standard probe-points export. A nonempty parameters list enables the entry; an empty one disables it. It does not filter the variables, because that export has a fixed set. pyfs-matrix plan now warns for each enabled steady entry, naming its entry number and frame. Select columns in the resulting table if a reader needs fewer variables. On an unsteady row, the list selects the fluid-plot variables that are sampled.


3. New surface exports and settings

SOLVER_TIME_AVERAGING from the pproc

The pproc can request a time-averaged native surface export:

[time_averaging]
last_revs = 1.5 # OR last_iters = 54; exactly one, positive

Time-averaged surfaces cannot be produced on the builds measured so far. Licensed C01 on 2026-09-19 found that SOLVER_TIME_AVERAGING hangs FlightStream 26.124 in the position the package emits it, without writing any output before termination at 240.5 seconds. The same script without that command wrote all seven outputs and its final log export, exiting successfully in 133.0 seconds. Both runs left a receipt carrying the executable's digest, committed under reports/pfs0250/time_averaging/. An earlier run with the command moved after INITIALIZE_SOLVER hung the same way; it predates the receipts and is recorded as an observation rather than as certified evidence.

The package now refuses [time_averaging] at plan time, naming the build and the dated measurement, rather than hanging the solver. Remove this table to run on these builds. Tecplot, VTK and CSV surfaces are then written as instants, including any requested per-step exports; the plots-history averages remain available through the matrix's reduction window.

The key, its validation, window resolver and recorded provenance remain. Only a build whose command-database status is verified may emit SOLVER_TIME_AVERAGING ENABLE <first> <last> in initialisation. Documentation alone (from 26.122) does not establish that it runs; earlier builds remain unsupported. Without the table, no averaging command is emitted.

The resolver uses inclusive, 1-based time-step bounds ending at the run's last step. last_iters counts steps; last_revs uses the same rotor clock and rounding as LAST_REVS_AVG. A window longer than the run is clipped at step 1. The manual does not settle time steps against inner iterations: this solver interpretation remains unverified because the C01 hang prevented measurement of the bounds. The export header's inner-iteration count is not the clock.

The run records the window it emitted. Surface entries in products.json and PROV-JSON state kind: average and that window, even if the pproc is edited later. Per-step exports and stopped runs end the window at the available step; exports before the averaging start are named skips. Without the table, the entries state kind: instant.

Changing this window needs a new run. It asks the solver for a different surface export. The matrix's LAST_REVS_AVG or LAST_ITERS_AVG window remains the separate post-processing choice for reductions of the plots history.

Surface flow in VTK and CSV, off by default

Two new [exports] kinds request native surface flow. vtk_variables is a top-level pproc key, before the tables:

vtk_variables = ["X", "Y", "Z", "CP_FREESTREAM"] # optional

[exports]
vtk = true
csv = true

VTK uses EXPORT_SOLVER_ANALYSIS_VTK and, with the list, SET_VTK_EXPORT_VARIABLES; without it, the all-variables form is used. CSV uses EXPORT_SOLVER_ANALYSIS_CSV, exporting CP-FREESTREAM, PASCALS, all surfaces, in the solver reference frame. Both exclude the wake. Variables and commands are checked against the selected build's committed command database; an unavailable one is refused at plan, naming the build.

Both join the per-step exports of EXPORT_UNSTEADY_AFTER_REV or EXPORT_UNSTEADY_AFTER_ITER. They are instants on the builds measured so far; a surface average requires [time_averaging] on a build verified to run that command. Enable the exports and run again to obtain files a previous run did not export. VTK, CSV and the per-step export program completed successfully on 26.124 in licensed C01 on 2026-09-19.

Fourteen setup keys replace the need for [[raw]]

These advanced settings now have setup keys of their own:

setup key setup key
laminar_separation kutta_joukowski_lift
aeroelastic_rbf_type print_rotor_induced_velocities
adaptive_field_grid_refinement rotor_induced_velocity_blending
wake_numerical_relaxation wake_relaxation
wake_decay_constant_per_m wake_streamwise_agglomeration
jet_wake_decay_normalized_length jet_wake_filaments_grid_induction
adverse_gradient_boundary_layer vortex_ring_normalization

Each emits its command only when set; absent keys leave an existing setup's script unchanged. [[raw]] still works. Unknown keys and values or commands the selected build does not carry are refused, naming the build. Use a setup key for a setting previously expressed as a raw command, then plan again.

Boundary-layer fluid-plot parameters, by build

An unsteady probe's parameters can now include BL_MOMENTUM_THICKNESS, BL_DISPLACEMENT_THICKNESS, BL_TOTAL_THICKNESS, BL_SHAPE_FACTOR, BL_SKIN_FRICTION and BL_TRANSITION_MARKER on builds whose database documents them: 26.122, 26.123 and 26.124. Previously the pproc vocabulary refused them on every build. An unsupported parameter is refused naming both the parameter and the build. A new run is needed to sample a newly requested variable; changing the list cannot add samples to an existing history.

What NITER limits on an unsteady run, measured

NITER bounds EACH TIME STEP, not the run. The licensed verification of this release measured it on build 26.124 with a short rotor run of 36 time steps (reports/pfs0250/pfs0250_verification.json, check niter_limit): with NITER = 5 every one of the 36 steps ran exactly five inner iterations, 180 in all, and the run was recorded COMPLETED_MAX_ITER.

The setup's convergence_iterations is NOT a cap: with it set to 3 and NITER at 300 the same geometry ran between 6 and 90 inner iterations per step, a median of 32.5 and 1100 in all, and converged. It states how many consecutive iterations must satisfy the convergence criterion before a step is taken as done.

Size an unsteady budget accordingly: the work is NITER times the number of time steps in the worst case, and a step that converges early costs less.

This measurement is the release's own, on one build and one geometry; it is stated as such rather than as a property of every build.

The export header's Current solver iteration number does count INNER iterations: the recorded 0.24.0 measurement had 2813 inner iterations over 144 time steps. That observation does not answer what NITER limits. Do not use that header as a time-step count or as evidence of a per-step limit.


4. Output formats changed in this pre-1.0 release

Update readers that assume a fixed list of post-stage files. The new tables are one sectional-loads file and one Cp file per distribution:

new file content
sections/<point>_sloads_<name>.csv sectional loads of one distribution
sections/<point>_cp_<name>.csv Cp at every chordwise station of that distribution

<name> is the distribution's alias or its families joined with -, not the expanded blade names. Filename-invalid characters become _; colliding names receive the entry's 1-based position as a suffix. All planes and blade blocks of one distribution share its file. With per-step exports enabled, each file holds EVERY available exported step in ascending STEP order; without them, it holds the end-of-run export. Cp was only listed in the manifest before.

Rows lead with STEP, time_s, FAMILY, PLANE, ROTOR, AZIMUTH, then the condition block and export columns. Cp also carries SECTION, the export's 1-based cross-section index. Missing values are NA. The combined sections/<point>_sections.csv and series/<point>_sections_series.csv remain available. The definitions page lists the columns of each new table.

products.json records distribution, the original families selection and steps_tabled for each split file. Missing exports or steps, empty Cp distributions and malformed exports are named skips. Read the manifest to distinguish unavailable data from a distribution that was never requested.

A record that does not identify its distributions is not split by guess

New runs record each section block's distribution entry and original families selection. A 0.24.0 run can be split when its recorded layout and pproc identify each block unambiguously by families, plane, frame and count. Editing today's pproc cannot reassign the recorded blocks.

Without that evidence, the split files are a named skip in products.json instead of a guessed split. A layout whose counts disagree with an export is also refused for that export kind. A new run is needed when the record does not identify its section distributions. Posting again cannot reconstruct an identity that was never recorded.


5. Earlier refusals and the manifest lock

An input artifact that exists and does not validate says so first

A row citing an invalid reference, setup or pproc now says the <kind> artifact at <path> does not validate, followed by its validation errors. The advice to put an artifact at that path is reserved for a file that does not exist. Fix the stated validation errors and plan again.

A [names] target cannot take a context column's heading

A target colliding with a context column such as ALPHA or MACH, or with a window or reference heading, REDUCTION or ROTOR, is refused when the pproc is read. Previously the collision was found at write time and the product was dropped. Choose a distinct target name and plan again.

A live writer keeps its manifest lock

The lock records its process, host and owner token. A live writer renews it every 5 seconds; a waiter no longer takes it merely because 30 seconds have passed. Takeover requires that the owner's process is gone on this host or that its heartbeat is older than 300 seconds. A release removes only the lock its releaser owns.

Do not run 0.24.0 and 0.25.0 writers against one workspace at once. Older writers do not follow this protocol. Finish the old writer before upgrading.


6. Python callers: moved modules and deprecations

write_sections_table(iteration=) is now write_sections_table(step=). The old keyword still works with a deprecation warning and is removed in 0.26.0. Passing both is refused with ProductArgumentError, a catalogued TypeError that is also caught by PyflightstreamError.

run.assess_unsteady_from_plots is deprecated and is also removed in 0.26.0. It still returns what it returned. Use LoadsAssessor for campaign assessment of native loads and solver residuals; whether the history has settled remains a separate judgement made from that history.

post.provenance and post.custom_polar are now separate public modules. Existing imports from post.products keep their spellings. The new post.section_distributions module writes the per-distribution tables.