Migrating to 0.27.0¶
This release takes the basic steps of a FlightStream session through pyfs and fixes inputs and outputs that were wrong. Nothing is removed, and recorded run manifests are read without rewriting them. What changes for you is what some products hold, and what a few files say.
1. A steady probe export holds each declared line once (B04)¶
A pproc declaring N probe lines gave N squared NEW_PROBE_LINE commands on a
steady row: three lines of eleven points exported 99 points where 33 were asked
for, as N repeated blocks of the declared lines. From 0.27.0 each line is
emitted once.
The repetition was of the LINES of one probe entry: rectangles and circles were emitted once, point by point. So for an export made before 0.27.0 from a pproc whose only probe entry declares lines and nothing else, keep its first N-th rows: they are the declared lines, in declaration order, and the other blocks repeat them value for value (measured on a 26.124 export of three lines). For any other layout (several entries, or lines beside a rectangle or a circle) do not cut the file: rerun the point, or rebuild the post from a 0.27.0 run. An export from an entry with one line is unaffected.
2. An induced drag the solver did not compute is NA (B03)¶
A boundary on the vorticity induced-drag list (SET_VORTICITY_DRAG_BOUNDARIES)
without a defined trailing edge is not computed, and the export prints its
CDi as zero. A polar group holding such a surface summed that zero. From
0.27.0, when the point's run record puts a surface on the list and its printed
CDi is exactly zero, the group's CDI is NA, and so is every axis column
the export's x force reaches at that point's angles (CDB, CDS, CDW; CLS
and CLW off zero incidence; CYW under sideslip). post.log names the
surfaces. The fixed-width custom polar writes the same missing value as nan.
If a table of yours now shows NA where it showed a number, the surface is on
the list without a trailing edge: give it one, or leave it off the list, which
is what the manual prescribes for a bluff body. A trailing-edged surface whose
induced drag rounds to zero at the printed precision reads NA too; raise
SET_SIGNIFICANT_DIGITS to narrow that band.
3. post.log logs the package's own warnings only (R01)¶
Each post now collects the package's warnings in a sink of its own thread, so
two posts running in two threads of one process no longer write each other's
warnings. A warning raised during a post by code outside pyflightstream is no
longer written to post.log; it still reaches your warning filters, and
pyflightstream's own warnings are logged as before.
4. The moments of the unsteady step exports are about the moment point (B05)¶
The loads frame and the moments model were emitted after START_SOLVER, so
every step export an unsteady row wrote during the march stated its moments
about the reference frame's origin, while the final export stated the row's
moment point. Both are now emitted before the solve, on every run type, and
are init-phase commands: a script of yours that places either after
START_SOLVER is refused. A loads series (series/<point>_loads_series.csv)
made before 0.27.0 from an unsteady row states its moment columns about the
reference origin; its forces are right, so rebuild it from a rerun if you
need the moments. In your own scripts, call
analysis_setup(loads_frame=..., moments_model=...) before start_solver,
and the analysis selections (load_units, boundaries, inviscid_only)
in a second call after it.
5. post.log.json beside post.log, and a WARNING line names its own point (R02)¶
The top of each matrix's products folder holds a third loose file,
post.log.json, beside post.log and products.json. It carries the same
header and records as the log, one per WARNING line, each with point,
product, message and remedy. products.json names it under a new key,
log_json, and log still names post.log. A check that lists that
folder's loose files or the manifest's keys will see one more of each. A
rebuild archives it with the log.
A warning that names its own point and product is now logged under them:
WARNING point=camp/sim_7001/AL-020 product=available-exports: ... where
0.26.0 wrote WARNING point=campaign product=stage: point=camp/sim_7001/AL-020
product=available-exports: .... A warning that names none still reads
point=campaign product=stage. An interrupted post's line ends Remedy:
correct the stated input and post again. If you parse post.log, read
post.log.json instead: it holds the same records as fields.
6. A pproc can no longer switch the saved simulation off (G11)¶
A post-processing artifact whose [exports] table says simulation = false
is refused, as one saying loads = false has been. pyfs-matrix plan stops
with matrix not planned: matrix row POL <pol>: the pproc artifact at <path>
does not validate ... the saved simulation cannot be deselected; remove the
key. This applies to every row whose PPROC names such an artifact, LEGACY
rows included, where the key never had an effect. Every point of a row that
names a run type then saves its final state as
datapoints/DP-<point>/P<POL>-<point>.fsm, one .fsm per point on disk.
Separately, pyfs-matrix plan now warns naming each LEGACY row whose
OUTPUTS declare no .fsm; the warning blocks nothing.
7. Run records carry the geometry's names, and some sections files change (R03, R04)¶
- A manifest holding a 0.27.0 workflow record of a
.fsmwith a mesh block carriesinventory, and 0.26.0 refuses it (extra_forbidden); post it with 0.27.0. A 0.27.0 record with no names writes no key, and 0.26.0 still reads it. - Integrated columns now appear where 0.26.0 kept the raw ones: a stem, a
numbered name,
all, for a block recorded in a common frame, or in any frame with the live rotor definitions in hand. This applies to 0.25 and 0.26 records too, while their geometry file still matches the recorded hash, and it changes those CSVs' column set. A block in a frame spelt like a rotor's with no rotor definition in hand, a user's ownX_RMRPincluded, is matched as in 0.26.0, and so is every block of a geometry that gives one name to two boundaries: those names settle no selection. - A 0.24.x split recorded in a common frame may be renamed after the entry
that emitted it (a block of rotor ACTIVE's blades in
MRP:..._sloads_Blade1.csvbecomes..._ACTIVE.csv,distribution2). A check that lists the sections files will see the new name. A split in a frame spelt like a rotor's keeps its 0.26.0 name. - A multi-point steady row now writes per-distribution sections files and
identity columns where 0.26.0 wrote a named
#distributionsskip. A job run before 0.27.0 keeps the skip; run it again.
8. A raw mesh runs through a workflow, with its unit and its trailing edge (G01, G02, G03, T06)¶
A row whose GEOMETRY names an .obj or .stl was refused before any seat.
It now runs once the sidecar beside the file, <stem>.boundaries.toml,
states the unit the file is written in, its surface names and its trailing
edge:
boundaries = ["Wing"] # the file's surfaces, in its order, written by hand
[import]
units = "MILLIMETER" # the unit the mesh file is written in
[trailing_edges]
file = "wing.te.txt" # a unit line, then one edge mid-point per line
# or: detect = "auto"
The simulation's length unit is metres whatever the file's unit; whether
IMPORT converts a body written in another unit is measured on no build
yet, and a mesh written in metres (units = "METER") does not depend on the
answer. A raw mesh whose sidecar declares no trailing edge is refused at
plan. On the file route the row must export its solver log (a run type's
default outputs do), and a pproc that turns [exports] log off, or a row
that states EXPORT_LOG: false, is refused there, because the run compares
the solver's count of imported edges with the points it wrote. The file
route runs on 26.124 only. A file-route script whose sidecar writes
[wake_termination] carries INITIALIZE_SOLVER twice, with the same
settings and the detection between them, because right after a file import
the detection marks nothing (G02). A .fsm row is unchanged, except that a .fsm
whose sidecar states an [import], [trailing_edges], [wake_termination]
or [base_regions] table is now refused, because nothing would read it;
delete the table. The raw-mesh
refusal's text changed: it names [import] and units =, and quotes the
anchor 'A RAW MESH STATES ITS UNITS'. A record of a point that imported a
raw mesh carries mesh_import, which 0.26.0 refuses; post such a workspace
with 0.27.0.
9. The wake-edge helpers write what 26.124 reads (G02, T05)¶
mark_wake_edges(script, edge_type=, tolerance=)becomesmark_wake_edges(script, edge_type=, tolerance=, units="<simulation unit>", node_file="<path>", midpoints=<edge mid-points in that unit>). It works on 26.124 only; 26.122 and 26.123 now refuse. The node file is written in the folder the point runs in (script.working_dir, a name relative to it): a data file the run writes and hashes is the point's own, and one parked anywhere else, beside the geometry for instance, is refused before the solver starts, because another point's run would rewrite it before a queued point read it. The same holds for a disc's profile copy (actuator_disc). An action script parked withhelpers.unsteady_actionis written in the point's folder too (a name relative toscript.working_dir); one parked elsewhere, the simulation folder included, is refused before the solver starts.write_node_file(path, nodes, unit=U)becomeswrite_node_file(path, midpoints, unit=U, simulation_unit=<simulation unit>). The file has no unit line and no ids, and it takes mid-points, not vertices.extract_trailing_edge(...).write_node_file(...)now refuses. Usewrite_trailing_edge_node_file(mesh, path, axis=, hub=, unit=)ortrailing_edge_midpoints(...); the output ofwrite_trailing_edge_node_fileis a unit line, then one mid-point per trailing-edge mesh edge, not one per section.
10. BASE_REGIONS names the base boundary (RPT-066)¶
A row writing BASE_REGIONS: Body on a body whose base is its own boundary
got no base region and no error: DETECT_BASE_REGIONS_BY_SURFACE marks the
boundary it is given as the base, and given the body it marks nothing. Write
the base boundary (BASE_REGIONS: Base), and the same in a pproc's
base_regions. Nothing refuses the body's name, since nothing offline tells a
base from a body.
11. A steady point saves its residual and load plots (G04)¶
- Every steady workflow script now saves the solver's residual and load plots
after its exports and before
EXPORT_LOG, and the section Cp plot where the pproc declares sections. Each point leaves two more files (three with sections) indatapoints/DP-<point>/, listed in its run record with their sha256. A missing one fails the pointFAILED_INCOMPLETE_OUTPUT, as any declared export does. - To keep the previous export set, state
plot_residuals = false,plot_loads = falseandplot_sections_cp = falseunder[exports]. - A stand-in solver or harness that writes each declared export must also
write the file named on the line after
SAVE_PLOT_TO_FILE. - Unsteady rows,
LEGACYrows and cases written in Python with their ownoutputsare unchanged. - A
[[raw]]SET_PLOT_TYPEdeclared beforeanalysisorexecis now refused at plan, because the command is export-phase; declare it beforeexport. plot_sections_cp = truewith no[[sections.distributions]]is refused.
12. Two setup commands 26.124 does not answer, and loads not in coefficients (G14, G09)¶
- A
[[raw]]SET_VORTICITY_LIFT_MODELon a 26.124 row is refused at plan, naming RPT-068. Until now it reached the solver, which stops the script at that line. - A point exported with
SET_LOADS_AND_MOMENTS_UNITSset to anything butCOEFFICIENTS(through[[raw]]before 0.27.0, orload_unitsnow) writes no product row. Its loads export is still collected and hashed.
13. Two new tables, and four words a flag can no longer take (G05, G06)¶
- A case written in Python that declares an output ending in
_vsec.vtkor_vsec.datused to export it as a surface file. That suffix now names the volume-section export, and without a[volume_section]table the case is refused. Rename the output. A run already RECORDED with such an output keeps it: a record written before 0.27.0 is read by the kinds its release knew, so its_vsec.vtkor_vsec.datstays a surface export inproducts.jsonand PROV-JSON, and nothing is renamed. - A setup whose
[[flags]]declares a flag namedACTUATOR,ACTUATOR_RPM,ACTUATOR_THRUSTorPROFILE(any case) is now refused, because a run type reads those words. Rename the flag. - A reference block with
kind = "actuator"used to be refused as naming no point kind; it is now an actuator disc.
14. A row may name an additional pproc, and the products gain an additional/ family (G12)¶
- A matrix whose rows state
ADDITIONAL_PPROCcannot be planned by 0.26.0 or older, which refuses it as a key of no run type. A row without the key plans and runs exactly as before. products.jsonmay hold entries underadditional/<pid>/, and entries for native files underdatapoints/DP-<point>/additional/<pid>/, each marked"additional": true, carryingextractionandderives_fromand noruns. A reader that iterates every entry as a product of a run filters on that key.additional.jsonis new, at the workspace root besideruns.json, and nothing older reads it.runs.jsonand every run record are unchanged by the additional post.pyfs-matrix postgains--additional-pproc, and with it--fs-version,--fs-exe,--localand--recipe, each refused without it.- A forced rerun or a continuation archives a point's
additional/folder with the rest of its files; extract it again after one. - A point recorded by 0.26.0 or older, over a geometry declaring boundary
names, is extracted only while the geometry file its record hashes is on
disk with those bytes, since that file is the one source of the boundary
order its saved simulation holds; where it is gone or changed the point is
skipped
SCRIPT_DRIFT, naming the file. ResolvedMatrixgains a last field,additional_pprocs; positional construction keeps working.
15. A roll or yaw rate turns the other way (G13)¶
A row stating roll_rate or yaw_rate now writes its free-stream rotation with
the opposite sign to 0.26.0: roll_rate:40 writes
SET_FREESTREAM ROTATION <frame> X -6.667 where 0.26.0 wrote X 6.667, and
yaw_rate likewise about Z. The old line solved the OPPOSITE rate on 26.124
(RPT-060, the probe T11): the coefficients of such a point are those of -p or
-r, and they are not converted. A row stating pitch_rate writes what it wrote.
- A point of 0.21.0 to 0.26.0 is affected when its name carries a non-zero
P(roll) orR(yaw). Its name does not change, so--resumeskips it: redo it by naming it,--force-rerun <point>, which archives the old record and its outputs first. - If you wrote the opposite sign in the cell to get the rate you meant, remove that: 0.27.0 solves the rate as written.
- Code that read
cases.workflows.FREESTREAM_ROTATION_SIGNas a number now gets a mapping: readFREESTREAM_ROTATION_SIGN["roll"],["pitch"]or["yaw"].
16. Where a local point runs, its log on a cluster that aborts at EXPORT_LOG, and a missing output¶
- On a cluster whose HPC profile states
[log] export_log = false,pyfs-matrix run --localno longer writesEXPORT_LOGinto the script, as a submitted job never did. The declared log of such a point holds what the solver printed, written by the run, and a point whose solver printed nothing has no log among itsoutputs, aresidual_notesaying why, and the status its loads export gives it. Nothing changes on any other machine. - On such a cluster the extraction scripts of
pyfs-matrix post --additional-pproccarry noEXPORT_LOG, with--localor planned for a submission; an extraction's log is what the solver printed, or absent with the reason in itsnote. The build-identity pre-flight exports no log there either, and a build the solver did not print is a warning, not a refusal. - A submitted steady job of several points on such a cluster is collected
once its points' other outputs and its scheduler's log have settled: the log
is filed as
<job script stem>_log.txtin the simulation folder, its points carry no_log.txtamong theiroutputs, and each point'sresidual_notenames the job's log. A job left WAITING by an earlier version is collected by runningpyfs-matrix collectagain. - A record
FAILED_INCOMPLETE_OUTPUTfor a missing declared output now lists the outputs that were written, indatapoints/DP-<point>/, with their hashes; before, it listed none and they stayed where the solver wrote them. Code that tookoutputs == []to mean such a failure readsstatus. CampaignWorkspace.collect_outputsfiles the declared outputs that exist before it raises for the missing ones, and raisesMissingOutputsError, aWorkspaceError, so an existingexcept WorkspaceErrorstill catches it.- A point run on this machine runs in its own
datapoints/DP-<point>/, as a submitted point does: its exports, per-step ones included, itsFlightStreamLog.txt, its action files and its node file are written there, and its record'scwdnames the folder. A script of your own that looked for them insims/sim_<id>/looks in the point's folder; a leftover in the simulation folder no longer stops a point, and a leftover in the point's folder does. A steady row of several points still runs in the simulation folder. - A campaign is refused, before anything is prepared, when a simulation its
rows name holds another campaign's work still in a scheduler's queue: the
folder carries no campaign name, so the second would stage and write where
the queued jobs read, and this holds for
--localtoo. Collect the queued work first (pyfs-matrix collect), or give the row another simulation id.--force-rerunof a point whose record is still SUBMITTED is refused the same way, and so is a new point of a simulation whose geometry changed since one of its points was submitted, or whose geometry sits in another folder of the library, while that job is still queued. - An HPC profile's
[descriptor] nameis a plain file name, because the descriptor is written in the folder each point runs in. A name with a folder (actions/job.sh), a parent folder (..) or a form Windows reads as another file's (job.sh.,JOB~1.SH) is refused when the profile is read. Rename it,submit.yamlfor instance.
17. Every table opens with the polar, no cell holds a comma, and a rotor table's first line is its header (G16)¶
Every table the post writes under post/<matrix>/, the additional post's under
additional/<pid>/ included, and the campaign sweep table campaign_sweep.csv
now open with POL, the polar each row comes from, named as the matrix names
its polar column. The campaign sweep, whose rows mix polars, carries each row's
own.
POLARis gone, and a reader by name readsPOL. The steady polarpolars/P<sim>-<sweep name>_<group>.csvand its super file opened withPOLARin 0.26.0; they open withPOLin its place and carry noPOLARcolumn, so the polar is stated once in every table. A script that read the columnPOLARreadsPOL. Every other column 0.26.0 wrote is still there, under the same name and in the same order after the new ones. The super file's union reads thePOLARof a polar table written before 0.27.0 asPOL.- A reader by position must change, except on a steady polar. Every column
moved one place to the right, and in a rotor table two:
POLand thenROTOR, the rotor's alias, lead every row ofpolars/P<sim>-<alias>_rotor.csv. A script that took the first column of a sections table asSTEPor of a probes table asPROBEfinds it one place further on. A steady polar and its super file keep every position:POLstands wherePOLARstood. - A rotor table's first line is its header. From 0.23.0 to 0.26.x the alias
stood alone on the first line, so a reader skipped one line
(
read_csv_table(path, skip=1),pandas.read_csv(path, skiprows=1)). Drop the skip for a table written by 0.27.0 and read the rotor from itsROTORcolumn.ROTOR_TABLE_LEAD_LINESis now 0. - The plots table
probes/<point>_plots.csvopens withPOLbefore the export's own header.plots_table_seriesreads every column after it as a plotted quantity, and reads a plots table written before 0.27.0 as it did. post.products.REDUCTION_COLUMNSbegins withPOL, so a pproc[names]entry may not give a plotted column that name.- The table writers
write_sections_table,write_plots_table,write_probes_table,write_reduction_table,write_per_blade_table,write_phase_locked_table,write_unsteady_polarandwrite_rotor_tabletake a keywordpol=; a caller that states none writesNAin the column.rotor_table_alias_line, a helper outside the module's public list (__all__), is removed with the line it wrote. results.sweep_tableandresults.run_tablereturnPOLas their first column.- No cell holds a comma or a double quote, and nothing is quoted, so a
reader that splits each line on
,(numpy.genfromtxt) reads every row to the header's count. A text cell writes;where it held a comma: the super content a super file or an unsteady polar echoes from the matrix row reads-2.0;0.0underSWEEP_VALUESandMACH:0.2; REmi:2.3; ALPHA:sweepunderFLIGHT_CONDITION, where 0.26.0 wrote them quoted with commas inside. A double quote is written as a single one and a line break as a space. A script that split such a cell on,splits it on;; a CSV reader of a table written before 0.27.0 still reads its quoted cells as it did. - A table written by an earlier release keeps its old shape until you post
again;
pyfs-matrix postarchives it before writing the new one. The solver's own files underdatapoints/DP-<point>/are not touched.
18. A run records the empty section layout, and an older record's is read off its script¶
- A record whose script adds or removes no surface section now carries
sections_layout: [], where it carriednull: every steady point, and every steady job, of a pproc declaring no distribution.nullnow means the layout is not known (a record before 0.24.0, a script changing sections no run type built). Code that readnullas "no distribution" reads[]. A 0.24.0 to 0.26.0 reader reads the empty list. - The post no longer names
sections/<point>_sloads#distributionsandsections/<point>_cp#distributionsas skipped for such a point, which advised a new run that recorded nothing more. A record written before this, on 0.24.0 to 0.26.0 or an earlier 0.27.0 build, is given the empty layout when its recorded script hashes as the record says and creates no surface section, so posting it again with 0.27.0 drops the two skips; no new run is needed. - A continuation records the layout of the run it continues, so its split and its identity columns are written where they were refused. A continuation recorded before this keeps the refusal.
19. The plan calls a recorded job's points recorded, and new angles of its row run one each¶
pyfs-matrix planreports every point a recorded steady job ran as already recorded, where it reported them ready althoughrun --resumeskipped them. A plan's ready count, and--cost, drop by those points.run --resumeover a steady row whose job is recorded runs the angles the job did not run one each, each its own record ending with its point name, as a single new angle already ran. Two or more used to run as a second job under the recorded job's id, which spent the solver and was then refused its record. Such an attempt left its exports in the new points'datapoints/DP-<point>/and nothing in the manifest, so resuming it as it is records each of those pointsFAILED_INCOMPLETE_OUTPUTbefore the solver starts, naming the files: move them out of those folders first. Naming the job to--force-rerunstill runs the whole row as one job.- A row cut back to angles its recorded job already ran runs nothing on
resume; it was run again as a point and refused by that point's own
outputs, which left a
FAILED_INCOMPLETE_OUTPUTrecord.
20. A row may name a custom free stream (G15)¶
- A matrix whose rows state
FREESTREAMcannot be planned by 0.26.0 or older, which refuses it as a key of no run type. A row without the key plans and runs exactly as before, its script byte for byte what it was. - A row stating
FREESTREAMruns at zero angle of attack and sideslip: on 26.124SOLVER_SET_AOAdoes not turn a custom field (the licensed probe T14, RPT-071), so a non-zeroALPHAorBETAbeside the key, fixed or swept, is refused at plan, every point of the row. Write the incidence into the field'svyandvzcomponents and stateALPHAandBETAas 0. The sideslip was not measured and is refused for the same reason. pyfs-workspace initcreatesinputs/freestreams/besideinputs/profiles/; run it again on an existing workspace, which keeps everything it holds, or make the folder by hand.workspace.INPUT_KINDSlists it.- A setup whose
[[flags]]declares a flag namedFREESTREAM(any case) is now refused, because a run type reads that word. Rename the flag. - The record of a point that ran with a custom field carries the file's sha256
in
inputs_sha256, under the file's name. A record without it, which is every record written before, posts as it did. SimCasegainsfreestream_profile, None by default.- A row stating
FREESTREAMandRESTARTcontinues only a run that read the same field: a stopped run whose record hashes no file of that name, or other bytes under it, is refused at plan and at run. RemoveRESTARTto march the point from the start in the field. - A field whose file name is the name of a file the run writes for the solver (the trailing-edge node file of a raw mesh, the disc's profile copy) is refused before the solver starts. Rename the field's file.
21. A glossary of the input keys, and the force distribution as an export (G08, G10)¶
- A workspace gains
inputs/pproc/INPUTS.mdbesideVARIABLES.md, written bypyfs-workspace init,pyfs-matrix planandpyfs-matrix post: one row per key of the matrix, the setup, the pproc, the reference and the geometry sidecar, with what it sets, its unit or values, the run types or builds that accept it, and the solver command it reaches. It is generated from the code, so edit the input artifacts, not the glossary. A key whose value reaches no line of the script says so and names what takes it (G08). [exports] force_distributions = trueis a new export kind: the per-panel pressure and viscous force coefficients of every surface, saved as<point>_force_distributions.txtonce at the end of the run. It is off by default, so a pproc that does not state it exports what it did (G10).