visualdynamics.project¶
project
¶
The project: every object a test holds, and how they belong together.
The GUI's window keeps exactly what a Project keeps — the named
objects, the object groups, which group is the Basis, the project type,
the active geometry — so a project built by clicking and one built in
a script are the same thing, and either saves to the same .vdyn
file. Scripts get the GUI's own verbs (add, link, set_basis,
rename, save) instead of hand-assembling dicts and lists.
random_vibration_report at the bottom is the whole of one of those
workflows in a single call — a Rattlesnake run in, an HTML report out —
and random_vibration_run is the project it does that from.
Classes:
| Name | Description |
|---|---|
Selection |
Objects reached by what they are instead of what they were named. |
Project |
Every object in one test, by name, plus the structure around them. |
Functions:
| Name | Description |
|---|---|
type_rank |
Where an object sits in the canonical order. |
describe |
A few words about what an object holds — how big it is, not what |
remap_links |
Object groups translated through a {old name: new name} mapping. |
retarget |
Point an object's references at renamed objects. |
random_vibration_run |
A Rattlesnake random vibration run, worked up into a project. |
work_up_system_id |
An imported system identification, worked up in place. |
system_id_run |
A Rattlesnake system identification, worked up into a project. |
mixed_run |
A Rattlesnake random run with a sine sweep under it, worked up |
sine_run |
A Rattlesnake sine sweep run, worked up into a project. |
sine_report |
A Rattlesnake sine sweep run in, an HTML report out. |
report_kind |
Which one-call reports a Rattlesnake run gets: 'random', 'mixed', |
run_report |
A Rattlesnake run in, the report its type calls for out. |
system_id_report |
A Rattlesnake system identification in, an HTML report out. |
random_vibration_report |
A Rattlesnake random vibration run in, an HTML report out. |
Classes¶
Selection
¶
Selection(project: Project, names: Iterable[str] | None = None, label: str = 'project')
Objects reached by what they are instead of what they were named.
project.geometry # anywhere in the project
project.basis.frf # in the Basis group
project.basis.psds[0] # when you mean to choose
A name is arbitrary — it came from whatever an importer or a user called it, and importing a second geometry renames nothing but makes 'Geometry' ambiguous. A type is not, so this is the path worth typing, and the only one an editor can complete.
The singular gives the one object of that kind, and says which ones it found when there are several. The plural is always a list, so code written against one FRF keeps working when a second arrives.
Methods:
| Name | Description |
|---|---|
of_type |
The names in scope holding objects of this class — and not |
Attributes:
| Name | Type | Description |
|---|---|---|
names |
list[str]
|
The names in scope, in the order the tree shows them. |
Source code in src/visualdynamics/project.py
Attributes¶
Methods:¶
of_type
¶
The names in scope holding objects of this class — and not
of a class registered as its own kind beneath it (see _NARROWER:
a transient specification is not the answer to time_history).
Source code in src/visualdynamics/project.py
Project
¶
Project(name: str = 'Project', objects: dict[str, Any] | None = None, active_geometry: str | None = None, project_type: str | None = None, object_groups: Iterable[ObjectGroup] | None = None, provenance: dict[str, dict[str, Any]] | None = None)
Bases: dict
Every object in one test, by name, plus the structure around them.
A Project is the name-to-object mapping, so project['FRF'],
list(project) and project.items() read the way the tree reads.
It is also what the desktop app holds: the window's objects,
links, project_type and active_geometry are properties over one
of these, and its buttons call the verbs below. A project built by
clicking and one built by calling are the same object, and open in
each other.
Objects are usually reached by type rather than by name —
project.geometry, project.basis.frf, project.other.shapes. The
singular gives the one there is and says so when several qualify; the
plural is always a list.
Attributes:
links: The groups objects have been declared to belong to, as
{'members': [...], 'role': 'Basis' | None}.
Association is explicit here, never inferred from names.
project_type: What kind of test this is — 'Modal Test', 'Random
Vibration', 'Shock', 'Transient' — which decides the report
template and the skeleton of slots the tree shows.
active_geometry: The name of the geometry data is drawn on when
nothing says otherwise.
name: What the project is called, which is what a saved .vdyn
and a rendered report are titled.
Methods:
| Name | Description |
|---|---|
grouped_names |
[(group or None, [names])] in the order the tree shows them: |
ordered_names |
Every name, flat, in the order the tree shows them. |
add |
Add an object under a unique name; returns the name used. |
duplicate |
Copies of objects, added beside them (Copy, then Paste, in |
import_file |
Import a file into this project; returns the names added. |
remove |
Delete objects, pruning them out of every object group. |
rename |
Rename an object; every reference to it follows. |
rename_dof |
Correct a channel's coordinate on an object and on everything |
link |
Declare objects part of one group, merging any they are in. |
unlink |
Take objects out of their groups; a group of one dissolves. |
relink |
Move one object into the group holding |
name_object_group |
Name the object group an object belongs to, or unname it. |
object_group_of |
The members linked with |
role_of |
'Basis', or None for an unroled or unlinked object. |
placed |
{role: [members]} — which objects are in each named group. |
sides |
{side: [object names]} for the typed skeleton: the Basis by |
missing |
The typed skeleton's empty slots — what the tree shows gray. |
object_group_with_role |
The object group carrying a role, or None. |
place |
Put one object into the named group, making it if need be. |
set_channel_role |
Give one channel a role — reference, response or monitor — |
set_role |
Name what a group is. The Basis is unique: taking the role |
set_basis |
Declare the Basis of comparisons: the group whose DOFs |
geometry_for |
(name, geometry) the object answers to: its group's, else |
absorb_links |
Take on the object groups of a project being imported. |
verbs |
The processing verbs that apply to an object, each with its |
selection_verbs |
The processing verbs a selection can act on, each with its |
compute_spectra |
Spectra from a time history's averages (the averaging |
compute_psds |
PSDs from a time history's averages (Compute PSDs). |
compute_octave |
A spectrum integrated onto proportional bands (Compute |
compute_frfs |
Frequency response functions from a time history (Compute |
compute_multiple_coherence |
Multiple coherence from a time history (Compute Multiple |
compute_srs |
Shock response spectra from a time history's shocks (Compute |
detect_shocks |
Find the events in a time history and mark them on it (the |
filter_data |
A time history through its low-pass (the filter view's |
truncate_data |
A time history cut to its truncation's span (the |
integrate |
One integration of a time history (Integrate): acceleration |
differentiate |
One differentiation of a time history (Differentiate): |
compute_cpsds |
The full cross-spectral matrix from a time history's averages |
transform |
Physical responses through a shape set to modal responses |
expand |
Modal responses back through a shape set to physical |
author_specification |
A specification written from a sheet (the Specification |
generate_rigid_body_modes |
The six rigid-body mode shapes of a geometry (Generate Rigid |
merge_coincident_nodes |
Make a geometry's coincident nodes one node (Merge Coincident |
new_geometry |
An empty geometry, to build a model in (the project's +): |
set_view |
Set the view a geometry opens on in 3-D, in the app, the report |
add_plane |
Add a meshed rectangle of plates to a geometry (Add Plane): a |
tie_elements |
Tie a patch of a geometry's elements rigidly to the part under |
merge_groups |
Merge a geometry's element groups into one (Merge Element Groups): the first |
add_block |
Add a meshed box of solid bricks to a geometry (Add Block): |
solve_modes |
The normal modes of a geometry whose element groups carry their |
fit_modes |
Fit a modal model to an FRF set (the fitting screen). |
project_onto_basis |
A shape set sampled at the Basis set's DOFs (Project onto |
match_modes |
Commit matched mode pairs (the comparison screen's +). |
comparison_mac |
The MAC between two shape sets as the comparison screen |
plot_mac |
The MAC picture the comparison screen draws: |
merge |
Combine compatible objects into one (Merge). |
export |
Write an object to a foreign format, chosen by suffix — |
generate_report |
Build a report from a starter template, bound symbolically |
work_up |
Every missing object the project's type expects, computed |
export_report |
Write a report as one self-contained HTML file (Export). |
table |
(headers, rows) for an object that reads as a table. |
plot |
Plot an object the way the GUI plots it: data as curves, a |
animate |
A mode shape — or a complex spectrum's operating deflection — |
name_of |
The name an object goes by here; a name passes through. |
extract_sine |
Each specification tone's level, read out of a recording |
stale |
{derived name: why} for everything whose source's settings |
refresh |
Recompute a derived object in place, under its own name. |
refresh_stale |
Refresh everything stale, sources before their dependents, |
save |
Write the whole project to one file: |
open |
Read a project back, from |
journal_as |
Record a stretch of front-end work as one replaying line. |
record_setting |
A settings write, journaled the way a script would make it. |
record_call |
A method call on an object, journaled as a script makes it. |
session_script |
This sitting's acts as a runnable Python script. |
Attributes:
| Name | Type | Description |
|---|---|---|
basis |
Selection
|
The Basis group, reached by type: |
object_group_selections |
list[Selection]
|
Every object group, the Basis first, each reached by type. |
other |
Selection
|
The one object group that is not the Basis — the model side of |
names |
list[str]
|
Every object's name, in the order the tree shows them. |
Source code in src/visualdynamics/project.py
Attributes¶
basis
property
¶
basis: Selection
The Basis group, reached by type: project.basis.frf.
Empty (and falsy) when no group has been declared the Basis,
so if project.basis: still asks the question it reads as.
Its member names are project.basis.names.
object_group_selections
property
¶
object_group_selections: list[Selection]
Every object group, the Basis first, each reached by type.
other
property
¶
other: Selection
The one object group that is not the Basis — the model side of a correlation, usually. Says so when there are several.
Methods:¶
grouped_names
¶
[(group or None, [names])] in the order the tree shows them: the Basis group first, then the other object groups, then what is unlinked — each in the canonical type order, and objects of one type in the order they arrived.
Source code in src/visualdynamics/project.py
ordered_names
¶
add
¶
Add an object under a unique name; returns the name used.
A clash is numbered rather than refused or overwritten, exactly as importing twice does in the GUI. The first geometry added becomes the active one.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
What to call it. A clash gets a numbered suffix. |
required |
obj
|
object
|
Any object the project can hold. |
required |
Returns:
| Type | Description |
|---|---|
str
|
The name the result was added under, which is unique within the project — a clash gets a numbered suffix. |
Source code in src/visualdynamics/project.py
duplicate
¶
Copies of objects, added beside them (Copy, then Paste, in the tree): each under its own name with ' copy', numbered when that is taken. Returns the names added.
Independent objects, not views: a copy's arrays are its own, so editing one leaves the other as it was. Links and provenance stay with the originals — a copy is a fresh object that happens to hold the same numbers, and what it is for is the user's to say.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*names
|
str or object
|
The objects to copy, by name or as the objects. |
()
|
Returns:
| Type | Description |
|---|---|
list of str
|
The names the copies were added under, in order. |
Source code in src/visualdynamics/project.py
import_file
¶
Import a file into this project; returns the names added.
Anything visualdynamics reads: a geometry, a Rattlesnake run, or a whole saved project. A project brings its structure with it — its object groups follow the objects even when a name clash renamed them — and, into an empty project, its name, type and active geometry too. Foreign readers' keys become readable names ('Modal_frf' is an FRF), the way the tree spells them.
A file that knows what kind of test it was says so: a controller's own save records which environment drove the run, and adopting it here settles the project type in a script the same way importing one settles it in the window.
options pass through to the format's reader — an exodus
file's steps='time' and nodes=[...], a geometry's
length_unit='m' — so a script can declare what the
window asks about in a dialog.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str or PathLike
|
The file to read. The importer is chosen by content and extension. |
required |
**options
|
Any
|
Passed through to the importer. |
{}
|
Returns:
| Type | Description |
|---|---|
list of str
|
The names of every object added, in the order added. |
Source code in src/visualdynamics/project.py
remove
¶
Delete objects, pruning them out of every object group.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*names
|
str
|
The objects to act on, by name. |
()
|
Returns:
| Type | Description |
|---|---|
None
|
|
Source code in src/visualdynamics/project.py
rename
¶
Rename an object; every reference to it follows.
Object groups, matched-modes sets and report block bindings all name their objects, and a rename that left any of them pointing at the old name would strand a figure or a bracket.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
old
|
str
|
The current name. |
required |
new
|
str
|
The name to give it. |
required |
Returns:
| Type | Description |
|---|---|
str
|
The name actually used, which may carry a suffix. |
Source code in src/visualdynamics/project.py
rename_dof
¶
Correct a channel's coordinate on an object and on everything derived from it (double-click a row or reference column of the grid and type).
The channel, not the point: a force labeled at the wrong node moves without taking the accelerometer at that node with it (Brandon, 2026-09-06 — the other is changed explicitly if it should be). The spectra computed from a time history inherited its channels, so one found mislabeled is mislabeled in every one of them; the correction follows the derivation chain rather than leaving each derived object to be fixed by hand or recomputed. An object downstream that does not carry the channel is left alone.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str or object
|
The object whose grid row or column was edited. |
required |
old
|
str
|
The coordinate as it is, '101Z+'. |
required |
new
|
str
|
The coordinate to give it, normalized the way every DOF is. |
required |
quantity
|
str
|
Which channel at |
None
|
Returns:
| Type | Description |
|---|---|
list of str
|
The names of the objects changed, the source first. |
Source code in src/visualdynamics/project.py
link
¶
Declare objects part of one group, merging any they are in.
A group holds at most one geometry — its members are read against it — and a member naming nodes that geometry lacks is refused, because the link would be a claim that is not true. Returns the group's members.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*names
|
str
|
The objects to act on, by name. |
()
|
role
|
str
|
The role to give the group — 'Basis', or None. |
None
|
name
|
str
|
What to call the group (Brandon, 2026-09-30: an activity in the Engineering Sciences Common Data Format has a name, and a named group exports as one that keeps its identity). Left out, a group being merged into keeps the name it had. |
None
|
Returns:
| Type | Description |
|---|---|
list of str
|
The group's members after linking. |
Source code in src/visualdynamics/project.py
797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 | |
unlink
¶
Take objects out of their groups; a group of one dissolves.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*names
|
str
|
The objects to act on, by name. |
()
|
Returns:
| Type | Description |
|---|---|
None
|
|
Source code in src/visualdynamics/project.py
relink
¶
Move one object into the group holding target.
link merges the groups its arguments are in, which is right
for declaring two things related and wrong for moving one thing
between groups — linking a geometry to the other side would pull
its whole group across with it. This takes the object out first,
so only it moves; target of None just takes it out.
The group it lands in keeps its role, so dropping something into the Basis makes it part of the Basis rather than dissolving it. Returns the members of the group it ends up in, empty if none.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str or object
|
The object to move. |
required |
target
|
str or object
|
An object whose group it should join. None removes it from its current group. |
None
|
Returns:
| Type | Description |
|---|---|
list of str
|
The group's members afterwards. |
Source code in src/visualdynamics/project.py
name_object_group
¶
Name the object group an object belongs to, or unname it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
member
|
str
|
Any member of the group, by name. |
required |
name
|
str or None
|
The group's new name; None removes it. |
required |
Returns:
| Type | Description |
|---|---|
list of str
|
The group's members. |
Source code in src/visualdynamics/project.py
object_group_of
¶
The members linked with name, or None.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str or object
|
The object to look up. |
required |
Returns:
| Type | Description |
|---|---|
list of str, or None
|
The names sharing its object group, or None when it is in no group. |
Source code in src/visualdynamics/project.py
role_of
¶
'Basis', or None for an unroled or unlinked object.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str or object
|
The object to look up. |
required |
Returns:
| Type | Description |
|---|---|
str or None
|
Its object group's role, or None if it has none. |
Source code in src/visualdynamics/project.py
placed
¶
{role: [members]} — which objects are in each named group.
Nothing is guessed here. An object nobody has placed is in no named group, which is what makes the tree's gray slots mean anything: a slot is filled by an object in its own group, so a modal test whose only geometry is the model's still shows a slot for the measured one.
Source code in src/visualdynamics/project.py
sides
¶
{side: [object names]} for the typed skeleton: the Basis by
its role, and under OTHER_SIDE every member of every group
that is not the Basis — the other side has no name, so it is
read off the groups rather than declared. What the tree's gray
slots and missing are computed from.
Returns:
| Type | Description |
|---|---|
dict of str to list of str
|
|
Source code in src/visualdynamics/project.py
missing
¶
The typed skeleton's empty slots — what the tree shows gray.
Each is project_expectations' (label, class, icon key,
ordinal, optional, side). An untyped project expects nothing.
Until a Basis is declared every object counts for every slot:
the window places what arrives, a script may never, and a
script's project with everything in it and no groups is not
one with nothing in it.
Returns:
| Type | Description |
|---|---|
list of tuple
|
|
Source code in src/visualdynamics/project.py
object_group_with_role
¶
The object group carrying a role, or None.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
role
|
str
|
Which named group to fetch — 'Basis' is the only name. |
required |
Returns:
| Type | Description |
|---|---|
ObjectGroup or None
|
That group, or None if unset. |
Source code in src/visualdynamics/project.py
place
¶
Put one object into the named group, making it if need be.
Unlike an ordinary link this takes a single object, because a named group is a declaration rather than an observed relation — and needing two members before the group can exist at all is what made it impossible to move a wrongly-sorted pair across one at a time. Returns the group's members.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str or object
|
The object being placed. |
required |
role
|
str or None
|
'Basis', or None for the other group — the one group that is not the Basis, made if there is none yet. With several groups besides the Basis there is no one other, and this refuses: relink onto a member of the one meant. |
required |
Returns:
| Type | Description |
|---|---|
list of str
|
The group's members. |
Source code in src/visualdynamics/project.py
1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 | |
set_channel_role
¶
Give one channel a role — reference, response or monitor — on a time history or on a channel table, and on whatever is linked with it.
The time history's role is the truth: it is what FRFs and multiple coherence read. A channel table linked with a history whose channels it describes shows the history's roles, so a change made on either lands on both (Brandon, 2026-09-25: the channel table is just a pointer to the linked time data's role). A channel table with no such history holds its own roles, as it always did.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str or object
|
The time history or channel table, by name or as itself. |
required |
dof
|
str
|
The channel's degree of freedom, '101Z+'. |
required |
quantity
|
str
|
What it measures, as a history's |
required |
role
|
str
|
'reference', 'response' or 'monitor'. |
required |
Returns:
| Type | Description |
|---|---|
list of str
|
The objects whose roles changed. |
Source code in src/visualdynamics/project.py
1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 | |
set_role
¶
Name what a group is. The Basis is unique: taking the role takes it from whatever group held it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
The object whose group is being labeled. |
required |
role
|
str or None
|
The role, or None to clear it. |
required |
Returns:
| Type | Description |
|---|---|
None
|
|
Source code in src/visualdynamics/project.py
set_basis
¶
Declare the Basis of comparisons: the group whose DOFs comparisons happen in, whose modes are the MAC rows and the frequency-error baseline. One name marks that object's group; several link them first.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*names
|
str or object
|
The objects that form the basis set. |
()
|
Returns:
| Type | Description |
|---|---|
list of str
|
The basis group's members. |
Source code in src/visualdynamics/project.py
geometry_for
¶
geometry_for(name: Any) -> tuple[str, Geometry] | None
(name, geometry) the object answers to: its group's, else the active one. What it is drawn on, and checked against.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str or object
|
The object whose geometry is wanted. |
required |
Returns:
| Type | Description |
|---|---|
tuple of (str, Geometry), or None
|
The geometry's name and the geometry itself, or None when the object is not linked to one. |
Source code in src/visualdynamics/project.py
absorb_links
¶
Take on the object groups of a project being imported.
The arriving objects have already been placed in named
groups by the project type's rules — one at a time, as each arrived,
which is a guess made without the file's own structure to go
on. The file knows better, so it goes last and _prune_links
lets it win.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
groups
|
iterable of ObjectGroup
|
Object groups from another project, merged into this one's. |
required |
Returns:
| Type | Description |
|---|---|
None
|
|
Source code in src/visualdynamics/project.py
verbs
¶
The processing verbs that apply to an object, each with its
one-line reading — how a script writer discovers what can be
done with what (Brandon, 2026-08-31: a flat method list says
nothing about what integrate is for).
The applicability table is the same one the window's bar reads, so the two surfaces cannot disagree; the summaries are the first paragraph of each verb's own docstring, so this and the API reference cannot disagree either.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str or object
|
The object to ask about — a name looks it up here, an object answers for itself whether or not it has been added (what applies to a result is knowable before it is kept). Omitted, every processing verb is listed. |
None
|
Returns:
| Type | Description |
|---|---|
list of (str, str)
|
|
Source code in src/visualdynamics/project.py
selection_verbs
¶
The processing verbs a selection can act on, each with its one-line reading — what the window's bar offers (Brandon, 2026-09-04: every act on the bar, none behind a menu).
One object: its own verbs, less the ones that need a partner
(transform needs a shape set beside the record). Several: the
partner verbs that apply to exactly that combination — a
record and a shape set transform or expand, two shape sets on
two geometries project, siblings of one type merge — and none
of the verbs that apply to one of them alone. The same table
verbs reads, so the bar and the API cannot disagree.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*names
|
str or object
|
The selection, by name or as the objects themselves. |
()
|
Returns:
| Type | Description |
|---|---|
list of (str, str)
|
|
Source code in src/visualdynamics/project.py
compute_spectra
¶
Spectra from a time history's averages (the averaging view's Compute Spectra).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str or object
|
The object to read, by name or as the object itself;
|
required |
Returns:
| Type | Description |
|---|---|
str
|
The name the result was added under, which is unique within the project — a clash gets a numbered suffix. |
Source code in src/visualdynamics/project.py
compute_psds
¶
PSDs from a time history's averages (Compute PSDs).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str or object
|
The object to read, by name or as the object itself;
|
required |
Returns:
| Type | Description |
|---|---|
str
|
The name the result was added under, which is unique within the project — a clash gets a numbered suffix. |
Source code in src/visualdynamics/project.py
compute_octave
¶
A spectrum integrated onto proportional bands (Compute Octave Bands) — the same power, arranged the way it is read; a specification with its warning and abort limits banded the same way.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str or object
|
The object to read, by name or as the object itself;
|
required |
per_octave
|
int
|
Bands per octave. Defaults to |
None
|
Returns:
| Type | Description |
|---|---|
str
|
The name the result was added under, which is unique within the project — a clash gets a numbered suffix. |
Source code in src/visualdynamics/project.py
compute_frfs
¶
Frequency response functions from a time history (Compute FRFs) — one per response and drive, over the frames a PSD uses.
method is 'Hv', 'H1' or 'H2': where the noise is assumed to
be, which is the one thing the three estimators disagree about.
The name goes on the object, since two FRF sets from one history
differ in nothing else a reader can see.
The frames are detected if the history has none, the same way the coherence does it, so the two describe one measurement.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str or object
|
The object to read, by name or as the object itself;
|
required |
method
|
str
|
Which estimator: 'Hv', 'H1' or 'H2' — where the noise is assumed to be, which is the one thing they disagree about. |
'Hv'
|
Returns:
| Type | Description |
|---|---|
str
|
The name the result was added under, which is unique within the project — a clash gets a numbered suffix. |
Source code in src/visualdynamics/project.py
compute_multiple_coherence
¶
Multiple coherence from a time history (Compute Multiple Coherence) — how much of each response the drives account for.
Averaged over frames, and the frames are detected if the history has none. Computed over the whole selection instead, the reference set fits every response exactly and the answer is 1.0 at every line — a number that says nothing, arrived at honestly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str or object
|
The object to read, by name or as the object itself;
|
required |
Returns:
| Type | Description |
|---|---|
str
|
The name the result was added under, which is unique within the project — a clash gets a numbered suffix. |
Source code in src/visualdynamics/project.py
compute_srs
¶
compute_srs(source: Any, *, per_octave: int | None = None, q: float | None = None, kind: str = 'maximax') -> str
Shock response spectra from a time history's shocks (Compute SRS) — one curve per channel per event.
The events are the history's own — detected only when there is nothing else to say where they are, so this answers rather than asking the caller to go and find them.
Detection is the last resort and not the first. A record being read as frames already says where its events are: a transient run's playings are its averaging, and set loose on one the detector answered with thirty-one events where there were six. A target is one playing by definition and gets no detector at all — it was being cut into three.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str or object
|
The object to read, by name or as the object itself;
|
required |
per_octave
|
int
|
Natural-frequency lines per octave. Defaults to the module's convention (12). |
None
|
q
|
float
|
The oscillator amplification, Q = 1/(2ζ); 10 — 5% damping, the shock-test convention — when omitted. |
None
|
kind
|
str
|
Which peak each oscillator reports: 'maximax' (largest magnitude of either sign), 'positive' or 'negative'. |
'maximax'
|
Returns:
| Type | Description |
|---|---|
str
|
The name the result was added under, which is unique within the project — a clash gets a numbered suffix. |
Source code in src/visualdynamics/project.py
detect_shocks
¶
Find the events in a time history and mark them on it (the shock view's Detect), returning how many.
The verb the API was missing (Brandon, 2026-08-25). compute_srs
detects as a side effect when a record carries no windows, which
served while the SRS came straight off the recording — but the
recommended shock workflow filters first, and then the detection
happened on the filtered record and the recording itself was
left unmarked. The events belong to the recording: mark them
there and every derivation carries them forward, because
core.filters copies the marks onto whatever it makes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str or object
|
The object to read, by name or as the object itself;
|
required |
Returns:
| Type | Description |
|---|---|
int
|
How many events were found and marked on the record. |
Source code in src/visualdynamics/project.py
filter_data
¶
A time history through its low-pass (the filter view's Apply Filter) — every channel, zero phase, so the peaks stay put.
The settings are the history's own filtering, set in the
filter view; with none set, the suggestion is adopted the way
compute_frfs adopts a suggested averaging, so the button
works before the view has been visited.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str or object
|
The object to read, by name or as the object itself;
|
required |
Returns:
| Type | Description |
|---|---|
str
|
The name the result was added under, which is unique within the project — a clash gets a numbered suffix. |
Source code in src/visualdynamics/project.py
truncate_data
¶
A time history cut to its truncation's span (the truncate view's Apply Truncation) — every channel between start and stop, the clock kept.
The span is the history's own truncation, set in the
truncate view. Unlike Filter Data there is no suggestion to
adopt: the whole record is the only neutral span and keeping
all of it is not an act, so with none set this refuses and
says where to set one.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str or object
|
The object to read, by name or as the object itself;
|
required |
Returns:
| Type | Description |
|---|---|
str
|
The name the result was added under, which is unique within the project — a clash gets a numbered suffix. |
Source code in src/visualdynamics/project.py
integrate
¶
One integration of a time history (Integrate): acceleration channels become velocity, velocity becomes displacement.
Whole record, never the shock windows — the reasons live in
core.filters. The drift corner is a parameter of the act,
recorded in the recipe: ... takes the default, None
integrates raw.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str or object
|
The object to read, by name or as the object itself;
|
required |
drift_corner
|
float or None
|
High-pass corner in Hz applied after integration, to stop a
sensor bias becoming a ramp. |
...
|
Returns:
| Type | Description |
|---|---|
str
|
The name the result was added under, which is unique within the project — a clash gets a numbered suffix. |
Source code in src/visualdynamics/project.py
differentiate
¶
One differentiation of a time history (Differentiate): displacement channels become velocity, velocity becomes acceleration.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str or object
|
The object to read, by name or as the object itself;
|
required |
Returns:
| Type | Description |
|---|---|
str
|
The name the result was added under, which is unique within the project — a clash gets a numbered suffix. |
Source code in src/visualdynamics/project.py
compute_cpsds
¶
The full cross-spectral matrix from a time history's averages (Compute CPSDs) — every channel against every channel.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str or object
|
The object to read, by name or as the object itself;
|
required |
Returns:
| Type | Description |
|---|---|
str
|
The name the result was added under, which is unique within the project — a clash gets a numbered suffix. |
Source code in src/visualdynamics/project.py
transform
¶
transform(source: Any, shapes: Any, *, records: Sequence[int] | None = None, name: str | None = None) -> str
Physical responses through a shape set to modal responses
(Transform to Modal Responses) — q = Φ⁺u for the motions,
Φᵀf for the forces, one record per mode and quantity at the
modal coordinates M1 … Mn.
Any set serves: the six rigid-body shapes of a geometry make
this the virtual point transformation. The result stands alone
in the tree — its DOFs are on no geometry — with its
provenance naming both the record and the set, and a
transform_report saying what was shared, dropped and left
unexplained.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str or object
|
The time history, by name or as the object itself;
|
required |
shapes
|
str or object
|
The shape set to transform through, by name or as itself. |
required |
records
|
sequence of int
|
Which of the record's channels to carry through — the ones picked in the tree. All of them when omitted. |
None
|
name
|
str
|
What to call the result. Defaults to the record's name followed by 'Modal Responses'. |
None
|
Returns:
| Type | Description |
|---|---|
str
|
The name the result was added under, which is unique within the project — a clash gets a numbered suffix. |
Source code in src/visualdynamics/project.py
expand
¶
expand(source: Any, shapes: Any, *, records: Sequence[int] | None = None, name: str | None = None) -> str
Modal responses back through a shape set to physical
responses (Expand to Physical Responses) — u = Φq at every
DOF the set covers, linked into the set's group so the result
animates on the geometry. A pick of modes expands those modes'
contribution alone, and the name says which.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str or object
|
The modal time history, by name or as the object itself;
|
required |
shapes
|
str or object
|
The shape set it was transformed through, by name or as itself. |
required |
records
|
sequence of int
|
Which modal records to expand — the modes picked in the tree. All of them when omitted. |
None
|
name
|
str
|
What to call the result. Defaults to the record's name with 'Modal Responses' read as 'Physical Responses', and the modes carried in brackets when they are not all. |
None
|
Returns:
| Type | Description |
|---|---|
str
|
The name the result was added under, which is unique within the project — a clash gets a numbered suffix. |
Source code in src/visualdynamics/project.py
author_specification
¶
author_specification(source: Any, draft: Any, *, name: str | None = None, replace: bool = False) -> str
A specification written from a sheet (the Specification
reading's Make Specification) — autospectra at breakpoints,
every cross term from a stated coherence and phase, bands in
decibels — beside the object the sheet was opened on: a shape
set (at its modal coordinates, ready to expand through it), a
channel table (at its control channels), or a specification.
With replace, the sheet is written into the specification
it was opened from under its own name, so links and report
slots hold — or into several at once, from a sheet that spans
them. A sheet holding every channel of the specification
rewrites it in the sheet's own form, its breakpoints or its
lines becoming the object's; a sheet holding a picked subset
merges back at the specification's own lines with the other
channels untouched. A pair the sheet leaves unstated is absent
from the result — nothing is assumed for it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str, object, or list of str
|
The object the sheet was opened on, by name or as itself; the specifications' names when the sheet spans several. |
required |
draft
|
SpecificationDraft
|
What the author stated ( |
required |
name
|
str
|
What to call the result. Defaults to the source's name followed by 'Specification'. |
None
|
replace
|
bool
|
Write the sheet into |
False
|
Returns:
| Type | Description |
|---|---|
str
|
The name the result was added under, which is unique within the project — a clash gets a numbered suffix — or the source's own name when replaced (the first of them when several). |
Source code in src/visualdynamics/project.py
2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 | |
generate_rigid_body_modes
¶
The six rigid-body mode shapes of a geometry (Generate Rigid Body Mode Shapes) — three translations and three rotations about its reference point, as a shape set in the geometry's group.
The point, and the mass and inertia that mass-normalize the
set, are the geometry's own mass_properties, set in the
rigid-body view. With none set the centroid is adopted, unit
shapes about the middle of the model — a real answer, unlike
a whole-record truncation, and the one the virtual-point
transformation wants most often — and stored, so the
staleness fingerprint records what was actually used.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str or object
|
The geometry, by name or as the object itself; |
required |
name
|
str
|
What to call the result. Defaults to the geometry's name followed by 'Rigid Body Modes'. |
None
|
Returns:
| Type | Description |
|---|---|
str
|
The name the result was added under, which is unique within the project — a clash gets a numbered suffix. |
Source code in src/visualdynamics/project.py
merge_coincident_nodes
¶
Make a geometry's coincident nodes one node (Merge Coincident Nodes): elements renamed to the lowest id at each point, the rest removed. Plates connect only where they share nodes, so this is what ties planes meshed apart at the lines where they meet.
Refused while an object linked with the geometry names a node the merge would remove — its data would point at a node that is gone; merge first, then measure or solve.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str or object
|
The geometry, by name or as the object itself. |
required |
tolerance
|
float
|
How close two nodes must be to be one, in meters (as the geometry holds its coordinates). Defaults to a millionth of the geometry's size. |
None
|
Returns:
| Type | Description |
|---|---|
dict
|
'merged', the nodes removed, and 'into', the nodes they became. |
Source code in src/visualdynamics/project.py
new_geometry
¶
An empty geometry, to build a model in (the project's +):
planes added with add_plane, links in the app's add mode.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
What to call it. |
'Geometry'
|
unit
|
str
|
Its length unit. Coordinates are held in SI either way; this is the unit it remembers it was built in. |
'm'
|
Returns:
| Type | Description |
|---|---|
str
|
The name the result was added under, unique within the project — a clash gets a numbered suffix. |
Source code in src/visualdynamics/project.py
set_view
¶
set_view(source: Any, view: View | None = None) -> None
Set the view a geometry opens on in 3-D, in the app, the report and every exported figure.
The app's Set Default View captures the view on screen; from a script it is stated, a model built Y-up seen from the front and above, say. Everything drawn on the geometry — its shapes, an ODS, the DOF arrows — opens on it too, and Reset View returns to it. The nodes are not turned: only how the model is looked at (Brandon, 2026-09-27).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str or Geometry
|
The geometry, by name or as the object itself. |
required |
view
|
View
|
Where the eye is, seen from the model's center, and which way is up; None goes back to the default isometric (from +X+Y+Z, Z up). |
None
|
Returns:
| Type | Description |
|---|---|
None
|
|
Examples:
>>> from visualdynamics import View
>>> project.set_view('BARC', View(eye=(1, 1, -1), up=(0, 1, 0)))
Source code in src/visualdynamics/project.py
add_plane
¶
add_plane(source: Any, corner: Any, edge_a: Any, edge_b: Any, size: float, group: str = '', *, unit: str = 'm', tolerance: float | None = None) -> dict
Add a meshed rectangle of plates to a geometry (Add Plane): a
corner, two perpendicular edges and an element size, each edge
divided evenly into the whole number of elements nearest that
size (mesh.plane). Its nodes that fall on nodes already there
become them, so planes meeting along a line are tied there, and
the nodes already there keep their ids (mesh.join).
Planes given the same element group name are one element group — the five walls of a box, each named 'box', are the box, given its material once in the Element Groups table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str or object
|
The geometry, by name or as the object itself. |
required |
corner
|
array_like
|
One corner, (x, y, z). |
required |
edge_a
|
array_like
|
The two edges from that corner, as vectors; perpendicular. |
required |
edge_b
|
array_like
|
The two edges from that corner, as vectors; perpendicular. |
required |
size
|
float
|
The element size aimed at. |
required |
group
|
str
|
The element group the plates go in, by name: an existing element group of that name, or a new one. |
''
|
unit
|
str
|
The unit the lengths above are in. A geometry whose units are not defined takes them as given. |
'm'
|
tolerance
|
float
|
How close a node must be to one already there to be it, in meters. Defaults to a millionth of the size of the two together. |
None
|
Returns:
| Type | Description |
|---|---|
dict
|
'added', the nodes added; 'shared', the plane's nodes that fell on nodes already there; 'elements', the plates added; 'groups', the element group they went into. |
Source code in src/visualdynamics/project.py
tie_elements
¶
Tie a patch of a geometry's elements rigidly to the part under
it (Tie): each node of the patch linked by a rigid, massless link
to the nearest node of to — a bolted joint, from the elements
its washer covers (mesh.tie).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str or object
|
The geometry, by name or as the object itself. |
required |
elements
|
sequence of int
|
The patch, by element id. |
required |
to
|
str, int or sequence of int
|
An element group, by name or id, or a second patch, by element ids. |
required |
group
|
str
|
The element group the links go into, made rigid if new. Defaults to the geometry's first rigid element group, or a new one named 'ties'. |
None
|
Returns:
| Type | Description |
|---|---|
dict
|
'links', how many were added; 'shared', patch nodes the target already holds; 'group', where the links went. |
Source code in src/visualdynamics/project.py
merge_groups
¶
Merge a geometry's element groups into one (Merge Element Groups): the first
keeps its id, name and properties and the others' elements move
into it — refused unless they hold the same element types and
carry the same material and thickness or section
(Geometry.merge_refusal).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str or object
|
The geometry, by name or as the object itself. |
required |
groups
|
sequence of int
|
The element groups, by id; the first is the one kept. |
required |
Returns:
| Type | Description |
|---|---|
dict
|
'into', the element group kept; 'groups', how many merged into it; 'elements', how many elements moved. |
Source code in src/visualdynamics/project.py
add_block
¶
add_block(source: Any, corner: Any, edge_a: Any, edge_b: Any, edge_c: Any, size: float, group: str = '', *, unit: str = 'm', holes: Any = (), hole_group: str | None = None, tolerance: float | None = None) -> dict
Add a meshed box of solid bricks to a geometry (Add Block):
a corner, three perpendicular edges and an element size, each
edge divided evenly into the whole number of elements nearest
that size (mesh.block), with cylindrical holes cut the way a
structured mesh cuts them. Its nodes that fall on nodes already
there become them, so blocks meeting over a face are tied there
(mesh.join).
Blocks given the same element group name are one element group — the rails and uprights of a frame, each named 'frame', are the frame, given its material once in the Element Groups table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str or object
|
The geometry, by name or as the object itself. |
required |
corner
|
array_like
|
One corner, (x, y, z). |
required |
edge_a
|
array_like
|
The three edges from that corner, as vectors; perpendicular. |
required |
edge_b
|
array_like
|
The three edges from that corner, as vectors; perpendicular. |
required |
edge_c
|
array_like
|
The three edges from that corner, as vectors; perpendicular. |
required |
size
|
float
|
The element size aimed at. |
required |
group
|
str
|
The element group the bricks go in, by name: an existing group of that name, or a new one. |
''
|
unit
|
str
|
The unit the lengths above are in. A geometry whose units are not defined takes them as given. |
'm'
|
holes
|
sequence of tuple
|
Holes as |
()
|
hole_group
|
str
|
The element group the holes' bricks go to — an insert's — or None to leave them out. |
None
|
tolerance
|
float
|
How close a node must be to one already there to be it, in meters. Defaults to a millionth of the size of the two together. |
None
|
Returns:
| Type | Description |
|---|---|
dict
|
'added', the nodes added; 'shared', the box's nodes that fell on nodes already there; 'elements', the bricks added; 'groups', the element groups they went into. |
Source code in src/visualdynamics/project.py
2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 | |
solve_modes
¶
solve_modes(source: Any, *, maximum_frequency: float | None = None, num_modes: int | None = None, damping: float = 0.0, name: str | None = None, progress: Any = None) -> str
The normal modes of a geometry whose element groups carry their properties (Solve Modes): the finite element model built from the element groups, solved, and the shapes added in the geometry's group.
The geometry is the model: each element group a material and a thickness
or a section (fem.GroupProperties, set in the Element Groups table or
on geometry.group_properties), every quad a plate, every
triangle a triangle, every two-node line a beam
(fem.Model.from_geometry). The solution is free-free unless the
geometry says otherwise later; the six rigid-body modes come
back at exactly 0 Hz with the elastic ones after them.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str or object
|
The geometry, by name or as the object itself. |
required |
maximum_frequency
|
float
|
Solve for every mode up to this frequency, in Hz. |
None
|
num_modes
|
int
|
Or for this many modes, rigid ones included. |
None
|
damping
|
float
|
The fraction of critical damping every mode is given; a finite element model has none of its own. |
0.0
|
name
|
str
|
What to call the result. Defaults to the geometry's name with ' Modes' after it. |
None
|
progress
|
callable
|
Told |
None
|
Returns:
| Type | Description |
|---|---|
str
|
The name the shape set was added under. |
Source code in src/visualdynamics/project.py
fit_modes
¶
fit_modes(source: str, *, bounds: tuple[float, float] | None = None, limit: int = 30, name: str | None = None, at: Sequence[tuple[float, float]] | None = None, refine: int = 0) -> str
Fit a modal model to an FRF set (the fitting screen).
The screen's loop, scripted: confirm the suggestion, take the
next, limit times. The session's own suggestion logic is the
whole judgment — a confirmed peak is spoken for unless the
shape standing there is somebody else's — so the loop adds no
second opinion. It used to: a proximity guard here vetoed any
suggestion within 1 Hz of a confirmed mode, which was the same
ridge-trap bandaid the session has since outgrown, and it
silently skipped the repeated pair's second tooth that
suggest had deliberately offered. On the screen the person
stops the loop; scripted, limit is that judgment, and the
plate demo's own cap is the worked example of choosing it.
On the screen the equivalent of bounds is the zoom — what is
on the plot is what gets searched. A script has no plot, so it
says so here.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str
|
The FRF set to fit. |
required |
bounds
|
tuple of float
|
(low, high) frequency limits to fit within. Defaults to the whole band. |
None
|
limit
|
int
|
The most modes to accept. |
30
|
name
|
str
|
What to call the shape set. |
None
|
at
|
sequence of tuple
|
Explicit (frequency, damping) picks — each optionally
(frequency, damping, description) — confirmed in the
order given — the residual is peeled sequentially, so the
order is part of the fit. This is how an interactive
session replays: the fitting screen journals its confirms
as exactly this call. |
None
|
refine
|
int
|
Times to run the joint residue refinement after the confirms — the screen's Refine All, counted. |
0
|
Returns:
| Type | Description |
|---|---|
str
|
The name the result was added under, unique within the project — a clash gets a numbered suffix. |
Source code in src/visualdynamics/project.py
2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 | |
project_onto_basis
¶
project_onto_basis(source: str, *, onto: str | None = None, tolerance: float = 0.02, name: str | None = None) -> str
A shape set sampled at the Basis set's DOFs (Project onto
Basis DOFs): nearest node within tolerance of the basis
model's extent, the displacement there dotted with each basis
DOF's direction. Returns the new set's name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str
|
The shape set to project. |
required |
onto
|
str
|
The basis to project onto. Defaults to the project's basis. |
None
|
tolerance
|
float
|
The residual a fit may leave. |
0.02
|
name
|
str
|
What to call the result. |
None
|
Returns:
| Type | Description |
|---|---|
str
|
The name the result was added under, unique within the project — a clash gets a numbered suffix. |
Source code in src/visualdynamics/project.py
match_modes
¶
match_modes(first: str, second: str, *, pairs: Iterable[tuple[int, int]] | None = None, macs: Iterable[float] | None = None, threshold: float = 0.7, name: str = 'Matched Modes') -> str
Commit matched mode pairs (the comparison screen's +).
With pairs those pairs exactly; otherwise each of first's
modes takes its best partner in second when the MAC clears
threshold. Comparing across geometries goes through the
projection first, as the screen does.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
first
|
str
|
One shape set, by name. |
required |
second
|
str
|
The other shape set, by name. |
required |
pairs
|
iterable of tuple of int
|
Explicit (first, second) index pairs, overriding the automatic matching. |
None
|
macs
|
iterable of float
|
MAC values for those pairs. |
None
|
threshold
|
float
|
The lowest MAC an automatic pairing may have. |
0.7
|
name
|
str
|
What to call the result. |
'Matched Modes'
|
Returns:
| Type | Description |
|---|---|
str
|
The name the result was added under, unique within the project — a clash gets a numbered suffix. |
Source code in src/visualdynamics/project.py
2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 | |
comparison_mac
¶
The MAC between two shape sets as the comparison screen shows it: across geometries the second set is projected onto the first's DOFs, because matching DOF names across geometries would trust them to mean the same directions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
first
|
str
|
One shape set, by name. |
required |
second
|
str
|
The other shape set, by name. |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
The MAC matrix, first's shapes down the rows and second's across the columns. |
Source code in src/visualdynamics/project.py
plot_mac
¶
The MAC picture the comparison screen draws: first
against itself, or against second — projected across
geometries exactly as comparison_mac does it, which a
shape set's own plot_mac cannot, since it compares by DOF
name and knows no geometry.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
first
|
str or ShapeSet
|
One shape set, by name or as the object. |
required |
second
|
str or ShapeSet
|
The other. Absent, the auto-MAC. |
None
|
**kwargs
|
Any
|
Passed to the drawing: |
{}
|
Returns:
| Type | Description |
|---|---|
object
|
Whatever the drawing returns — a window, an image. |
Source code in src/visualdynamics/project.py
merge
¶
Combine compatible objects into one (Merge).
Same concrete type only, and each kind has its own rule about what may join: geometries need disjoint node ids, shape sets the same DOF cover, data arrays an identical abscissa. The merged object replaces its parts.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*names
|
str
|
The objects to act on, by name. |
()
|
name
|
str
|
What to call the merged object. |
None
|
Returns:
| Type | Description |
|---|---|
str
|
The name the result was added under, unique within the project — a clash gets a numbered suffix. |
Source code in src/visualdynamics/project.py
export
¶
Write an object to a foreign format, chosen by suffix —
every registered writer, .unv, .exo, .npz, .bdf,
.afu/.ati/.ash, .xlsx, .3mf, .stl, .vdreport and
the rest of io.exporters() (Export).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
The object to write. |
required |
path
|
str or PathLike
|
Where to write it. The format follows the extension. |
required |
unit_system
|
UnitSystem
|
Units to write in. Defaults to the project's own. |
None
|
**kwargs
|
Any
|
Passed through to the exporter. |
{}
|
Returns:
| Type | Description |
|---|---|
str
|
The path written. |
Source code in src/visualdynamics/project.py
generate_report
¶
generate_report(template: str = 'modal', name: str = 'Report', marking: str | None = None, marking_color: str | None = None) -> str
Build a report from a starter template, bound symbolically to this project's structure (Generate Report).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
template
|
str
|
Which starter to build: 'modal', 'random', 'shock',
'transient', 'sine', 'sysid' or 'empty' (a random-and-sine
project makes 'random' and 'sine', one report each) — or a saved
template, by the name it was saved under in the templates
folder or by the path of a |
'modal'
|
name
|
str
|
What to call the report object. |
'Report'
|
marking
|
str
|
The banner across the top and bottom of every page — 'UNCLASSIFIED' unless said. A batch stamps its own (Brandon, 2026-10-02). |
None
|
marking_color
|
str
|
'ink' or 'red'. |
None
|
Returns:
| Type | Description |
|---|---|
str
|
The name the result was added under, which is unique within the project — a clash gets a numbered suffix. |
Source code in src/visualdynamics/project.py
work_up
¶
Every missing object the project's type expects, computed from what is loaded, and the report last (the tree bar's Automatic; Brandon, 2026-09-30).
The skeleton's empty slots in their own order, each made from the objects already there the way the workflow guide makes it — PSDs from the time data, the octave bands from the PSDs and the specification, the coherence from the time data; the FRFs and a fitted shape set for a modal test; the levels for a sine sweep; the filtered record, its SRS and the motion chain for a shock; the two streams' PSDs on shared frames and the H1 plant for a system identification — and the typed report once they are in. A slot the loaded data cannot fill (a geometry, the photographs, a specification) is left gray; a computation the data refuses (no drive channel for a coherence) is skipped, and the rest still happen. Nothing already present is remade: pressed twice, the second press adds nothing.
One journal line, project.work_up(), the way merge is one
line: the verbs it calls are the ones the guide teaches, and a
script wanting them one at a time calls them.
Returns:
| Type | Description |
|---|---|
list of str
|
The names added, in the order made; empty when the skeleton was already full. |
Raises:
| Type | Description |
|---|---|
ValueError
|
When the project has no type — there is no skeleton to fill. |
Source code in src/visualdynamics/project.py
2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 | |
export_report
¶
Write a report as one self-contained HTML file (Export).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
The report object to render. |
required |
path
|
str or PathLike
|
Where to write the self-contained HTML file. |
required |
unit_system
|
UnitSystem
|
Units to render in. Defaults to the project's own. |
None
|
Returns:
| Type | Description |
|---|---|
str
|
The path written. |
Source code in src/visualdynamics/project.py
table
¶
(headers, rows) for an object that reads as a table.
The instrumentation of a channel table, the identified parameters of a shape set — the same rows the report prints, so a script and a report cannot disagree about what is in one.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str or object
|
The object to tabulate. |
required |
Returns:
| Type | Description |
|---|---|
tuple of (list of str, list of list of str)
|
The column headings and the rows, both as text. |
Source code in src/visualdynamics/project.py
plot
¶
Plot an object the way the GUI plots it: data as curves, a geometry as its scene, a shape set as its auto-MAC.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
The object to draw. |
required |
**kwargs
|
Any
|
Passed through to the object's own plot method. |
{}
|
Returns:
| Type | Description |
|---|---|
object
|
Whatever the underlying plot call returns. |
Source code in src/visualdynamics/project.py
animate
¶
A mode shape — or a complex spectrum's operating deflection — moving on the geometry it answers to.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
The shape set to animate. |
required |
mode
|
int
|
Which mode, by index. |
0
|
**kwargs
|
Any
|
Passed through to the scene. |
{}
|
Returns:
| Type | Description |
|---|---|
object
|
The plotter the animation is running in. |
Source code in src/visualdynamics/project.py
name_of
¶
The name an object goes by here; a name passes through.
Verbs take either, so project.compute_psds('Time History')
and project.compute_psds(project.basis.time_history) are the
same call.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target
|
str or object
|
A name, or an object the project holds. |
required |
Returns:
| Type | Description |
|---|---|
str
|
The name it is stored under. |
Source code in src/visualdynamics/project.py
extract_sine
¶
Each specification tone's level, read out of a recording (Extract Sine Levels) — one object per tone, because each tone sweeps its own frequencies on its own clock.
The specification is found in the project when not named — the one SineSweepSpecification there is — and each result is linked to the recording it was read from.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str or object
|
The sine level set or run to read. |
required |
specification
|
str or object
|
The sweep specification to extract against. Defaults to the project's own, when it holds exactly one. |
None
|
progress
|
callable
|
Told |
None
|
Returns:
| Type | Description |
|---|---|
list of str
|
The names of the levels added, one per tone. |
Source code in src/visualdynamics/project.py
stale
¶
{derived name: why} for everything whose source's settings have moved since it was computed.
A missing source, or a derivation this bookkeeping predates, answers nothing — absence of evidence is not staleness.
Source code in src/visualdynamics/project.py
refresh
¶
Recompute a derived object in place, under its own name.
The links, the report's bindings and the grids all key on the name, so replacing the value under it is what keeps every reference honest. Anything derived from this object goes stale by content, which is the cascade — refreshed one badge at a time, or all at once, but always by a person.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str or object
|
The derived object to recompute, in place and under its own name. |
required |
Returns:
| Type | Description |
|---|---|
str
|
The name refreshed. |
Source code in src/visualdynamics/project.py
refresh_stale
¶
Refresh everything stale, sources before their dependents, until nothing is — the project row's one click.
Source code in src/visualdynamics/project.py
save
¶
Write the whole project to one file: .vdyn, .mat for
the same layout in MATLAB's container, or .h5 for the
Engineering Sciences Common Data Format.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str or PathLike
|
Where to write the file. A |
required |
**options
|
Any
|
Passed to a foreign writer: an ESCDF file's |
{}
|
Returns:
| Type | Description |
|---|---|
str
|
The path written. |
Source code in src/visualdynamics/project.py
open
classmethod
¶
open(path: str | PathLike) -> Project
Read a project back, from .vdyn, .mat or an ESCDF .h5
(.hdf5, .escdf).
Source code in src/visualdynamics/project.py
journal_as
¶
Record a stretch of front-end work as one replaying line.
The GUI imports a file by building the objects itself and
adding them one by one; journaled verb by verb, that stretch
is a pile of not-replayable comments — when the honest record
is the single import_file call a script would make. Inside
the stretch every verb stays quiet, exactly as verbs nested in
verbs do; the line lands only when the stretch succeeds.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
line
|
str or None
|
The line that replays the stretch — None to record nothing at all. |
required |
Returns:
| Type | Description |
|---|---|
None
|
|
Source code in src/visualdynamics/project.py
record_setting
¶
A settings write, journaled the way a script would make it.
The front ends' funnel: the GUI stores analysis settings by assignment — a dragged averaging span, a filter corner, the shock windows — and those writes are session acts as much as any verb. A repeated write to the same slot replaces its own last line, so a session of nudging settles to the one assignment that stands rather than a line per keystroke.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target
|
str or object
|
The object written to, by name or as itself. |
required |
attribute
|
str
|
Which settings attribute was stored. |
required |
value
|
Any
|
What was stored; its repr must rebuild it, which every settings dataclass here guarantees. |
required |
Returns:
| Type | Description |
|---|---|
None
|
|
Source code in src/visualdynamics/project.py
record_call
¶
A method call on an object, journaled as a script makes it.
The front ends' funnel for object verbs that are not Project verbs — a line added to a geometry, a photo renamed — each an act of the session the console must speak (Brandon, 2026-08-30: adding a line said nothing).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target
|
str or object
|
The object acted on, by name or as itself. |
required |
method
|
str
|
The method a script would call. |
required |
*args
|
Any
|
The call's arguments; their reprs must rebuild them. |
()
|
**kwargs
|
Any
|
Keyword arguments, same rule. |
{}
|
Returns:
| Type | Description |
|---|---|
None
|
|
Source code in src/visualdynamics/project.py
session_script
¶
This sitting's acts as a runnable Python script.
The journal joined under its imports: every verb that ran and every setting stored — clicked in the GUI or called from a script — recorded as the line that reproduces it, so a session worked up by hand can be replayed, adapted, or kept. Reads and refusals are absent on purpose: the script is what happened to the project, and a verb that raised changed nothing.
Returns:
| Type | Description |
|---|---|
str
|
A Python script; running it rebuilds this session's project from the same inputs. |
Source code in src/visualdynamics/project.py
Functions:¶
type_rank
¶
describe
¶
A few words about what an object holds — how big it is, not what it is called. Empty categories are left out rather than reported as zero.
Source code in src/visualdynamics/project.py
remap_links
¶
remap_links(object_groups: Iterable[ObjectGroup], mapping: dict[str, str], roles_taken: Iterable[str] = ()) -> list[ObjectGroup]
Object groups translated through a {old name: new name} mapping.
Importing a project into one that already holds objects renames what clashes; its groups have to follow, or the structure the file carried is lost. A role already spoken for stays with the group that has it — the Basis is the project's, not the file's.
Source code in src/visualdynamics/project.py
retarget
¶
Point an object's references at renamed objects.
Objects that name others — matched modes name their two shape sets, a report's blocks name what they draw from — go stale the moment a name changes under them, and a stale binding is an unbound figure or a bracket that vanished.
Source code in src/visualdynamics/project.py
random_vibration_run
¶
random_vibration_run(run: str | PathLike, per_octave: int | None = None, *, last: float | None = None, geometry: str | PathLike | None = None, length_unit: str | None = None, photos: Any = None) -> Project
A Rattlesnake random vibration run, worked up into a project.
project = visualdynamics.random_vibration_run('run.nc4')
project = visualdynamics.random_vibration_run(
'run.nc4', last=100.0, geometry='article.stp', length_unit='mm',
photos='setup_photos/')
Every step the window would take on the way from a controller file to a finished project, in the order it takes them: import the run, average PSDs from the control time histories, band those onto proportional bands — and the specification onto the same bands, limits and all, since the report reads the banded measurement against the banded requirement (Brandon, 2026-09-18) — and measure how much of each response the drives account for. The run says it is a random vibration test, so the project comes back declared as one.
The averaging is the Detect answer: the controller's own frame length, window and overlap from the file, with the start and the count worked out from the record — where the run is at level and how many frames that stretch holds — so a script, an import and a click on Detect reach the same numbers (Brandon, 2026-09-19).
The rest is what the window's tree asks for after the run is in,
given here so a script never has to open it (Brandon,
2026-09-19): the article's geometry, with its length unit
declared when the file does not carry one; the setup photographs,
as a folder of png or jpeg files, one file, or a list of files in
the order they should appear; and, for a run too long to hold,
last — the seconds before the end to import, the same window the
import dialog's Last field sets, and a run shorter than that is
taken whole. The geometry and the photographs are linked into the
run's own group, which is what lets the report read them against
the channel table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
run
|
str or PathLike
|
The controller's |
required |
per_octave
|
int
|
Bands per octave for the banded PSD and specification; the project's default (a sixth) when omitted. |
None
|
last
|
float
|
Import only the last |
None
|
geometry
|
str or PathLike
|
A geometry file to import and link to the run. |
None
|
length_unit
|
str
|
The geometry's length unit, for a file that does not say. |
None
|
photos
|
str, os.PathLike or sequence of them
|
A folder of photographs, one photograph, or several. |
None
|
Returns:
| Type | Description |
|---|---|
Project
|
The worked-up project, declared Random Vibration. |
Source code in src/visualdynamics/project.py
4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 | |
work_up_system_id
¶
work_up_system_id(project: Project) -> tuple[str | None, str]
An imported system identification, worked up in place.
The seam between reading a file and doing the work, so the work can be exercised on a recording built by hand: no file a test can commit holds the two streams a system identification is made of, and a rule that is never exercised is a rule that is not tested (2026-09-22).
Returns the two stream names, the ambient one None where the recording holds only the driven stream.
Source code in src/visualdynamics/project.py
system_id_run
¶
system_id_run(run: str | PathLike, *, last: float | None = None, geometry: str | PathLike | None = None, length_unit: str | None = None, photos: Any = None) -> Project
A Rattlesnake system identification, worked up into a project.
project = visualdynamics.system_id_run('sysid.nc4')
The steps the window would take, in its order: import the recording, name the two streams for what they are, measure the plant from the driven one by H1 — the controller's own estimator, the excitation being known and the noise on the response — take the multiple coherence, and average a density from each stream on the same frames, since the signal-to-noise is a ratio of densities and a ratio only exists on shared lines. The project comes back declared a System ID.
last, geometry, length_unit and photos are
random_vibration_run's, and mean the same things.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
run
|
str or PathLike
|
The controller's |
required |
last
|
float
|
Import only the last |
None
|
geometry
|
str or PathLike
|
A geometry file to import and link to the run. |
None
|
length_unit
|
str
|
The geometry's length unit, for a file that does not say. |
None
|
photos
|
str, os.PathLike or sequence of them
|
A folder of photographs, one photograph, or several in order. |
None
|
Returns:
| Type | Description |
|---|---|
Project
|
The worked-up project, declared System ID. |
Source code in src/visualdynamics/project.py
mixed_run
¶
mixed_run(run: str | PathLike, per_octave: int | None = None, *, last: float | None = None, geometry: str | PathLike | None = None, length_unit: str | None = None, photos: Any = None) -> Project
A Rattlesnake random run with a sine sweep under it, worked up into a project: both halves.
project = visualdynamics.mixed_run('run.nc4')
Everything random_vibration_run does — the run imported, the PSDs
averaged and banded, the specification banded, the coherence
measured, the geometry and photographs brought in — and then the
sine half: each tone's level extracted from the same recording
against the sweep specification the file carried. The run says it
is both, so the project comes back declared Random and Sine. A run
with no sine specification is refused by name rather than worked
up as half of what was asked for; random_vibration_run is the
call for it. The keywords are random_vibration_run's.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
run
|
str or PathLike
|
The controller's |
required |
per_octave
|
int
|
Bands per octave for the banded PSD and specification; the project's default (a sixth) when omitted. |
None
|
last
|
float
|
Import only the last |
None
|
geometry
|
str or PathLike
|
A geometry file to import and link to the run. |
None
|
length_unit
|
str
|
The geometry's length unit, for a file that does not say. |
None
|
photos
|
str, os.PathLike or sequence of them
|
A folder of photographs, one photograph, or several in order. |
None
|
Returns:
| Type | Description |
|---|---|
Project
|
The worked-up project, declared Random and Sine. |
Source code in src/visualdynamics/project.py
sine_run
¶
sine_run(run: str | PathLike, *, last: float | None = None, geometry: str | PathLike | None = None, length_unit: str | None = None, photos: Any = None) -> Project
A Rattlesnake sine sweep run, worked up into a project.
project = visualdynamics.sine_run('sweep.nc4')
The run imported, each tone's level extracted from the control
channels against the sweep specification the file carried, and the
geometry and photographs brought in and linked, as
random_vibration_run brings them. The run says it is a sine
sweep, so the project comes back declared as one. A run with no
sine sweep specification is refused by name; random_vibration_run
or mixed_run is the call for it. The keywords are
random_vibration_run's, less per_octave, which a sweep has no
use for.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
run
|
str or PathLike
|
The controller's |
required |
last
|
float
|
Import only the last |
None
|
geometry
|
str or PathLike
|
A geometry file to import and link to the run. |
None
|
length_unit
|
str
|
The geometry's length unit, for a file that does not say. |
None
|
photos
|
str, os.PathLike or sequence of them
|
A folder of photographs, one photograph, or several in order. |
None
|
Returns:
| Type | Description |
|---|---|
Project
|
The worked-up project, declared Sine Sweep. |
Source code in src/visualdynamics/project.py
4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 | |
sine_report
¶
sine_report(run: Any = ASK, path: str | PathLike | None = None, *, last: float | None = None, geometry: Any = None, photos: Any = None, unit_system: Any = None, marking: str | None = None) -> Any
A Rattlesnake sine sweep run in, an HTML report out.
visualdynamics.sine_report('sweep.nc4', 'report.html')
visualdynamics.sine_report() # ask for both
visualdynamics.sine_report('sweep.nc4', 'reports/')
random_vibration_report's twin for a sweep: the same asking when
the run is left out, the same batch and folder rules, the workup
of sine_run, and the Sine Sweep report written as one
self-contained HTML file. Returns the path written, or the list of
them when several runs were chosen (Brandon, 2026-10-02: the batch
tool stopped at a run that was sine alone). marking is the banner
across every page of the report, 'UNCLASSIFIED' unless said — on
every one-call report since 2026-10-02, when a batch wanted its own
heading.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
run
|
str, os.PathLike, sequence of them, or `visualdynamics.ASK`
|
The controller's |
ASK
|
path
|
(str, PathLike or None)
|
The file to write, or a folder (one that exists, or a name ending in a separator) the report lands in under the run's own name. Beside the run when omitted. Several runs need a folder or None. |
None
|
last
|
float
|
Import only the last |
None
|
geometry
|
str, os.PathLike, `visualdynamics.ASK` or None
|
A geometry file to import and link to the run. Asked for once for
the whole batch when the run was asked for, Cancel meaning none;
|
None
|
photos
|
str, os.PathLike or sequence of them
|
A folder of photographs, one photograph, or several in order. |
None
|
unit_system
|
UnitSystem
|
The units the report is written in; the package default, 'in-slinch-lbf-s (g)', when omitted. |
None
|
marking
|
str
|
The banner across the top and bottom of every page; 'UNCLASSIFIED' when omitted. |
None
|
Returns:
| Type | Description |
|---|---|
str or list of str
|
The path written; a list of them when several runs were chosen or a list of runs was given. |
Source code in src/visualdynamics/project.py
report_kind
¶
Which one-call reports a Rattlesnake run gets: 'random', 'mixed',
'sine' or 'sysid', from the project type the file declares — 'mixed'
being a random run with a sweep under it, which gets a random report
and a sine report (run_report) — and
'sysid' for a streamed save with a system ID's shape, two streams,
a quiet one then a loud one, where the window asks and this
decides (Brandon, 2026-10-02: a batch defaults to the likeliest
reading). A run of a type with no one-call report, a modal or a
transient run, is refused by name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
run
|
str or PathLike
|
The controller's |
required |
Returns:
| Type | Description |
|---|---|
str
|
'random', 'mixed', 'sine' or 'sysid'. |
Source code in src/visualdynamics/project.py
run_report
¶
run_report(run: Any = ASK, path: str | PathLike | None = None, *, last: Any = None, kinds: Any = None, geometry: Any = None, photos: Any = None, per_octave: int | None = None, unit_system: Any = None, marking: str | None = None, progress: Any = None) -> Any
A Rattlesnake run in, the report its type calls for out.
visualdynamics.run_report('run.nc4', 'report.html')
visualdynamics.run_report() # ask for the runs and the geometry
visualdynamics.run_report('run.nc4', 'reports/')
visualdynamics.run_report(['a.nc4', 'b.nc4'], 'reports/',
geometry='article.stp')
The one-call report that reads the run's own type (report_kind)
and writes that report: a random run gets random_vibration_report's,
a sweep sine_report's, a system identification
system_id_report's, and a random-and-sine run both a random and a
sine report, as <name>_random.html and <name>_sine.html — the
random reading only the last last seconds when they are given, the
sine always the whole run, since a sweep cut short loses tones (the
combined report retired 2026-10-05). A streamed save
that looks like a system ID, two streams quiet then loud, is taken
as one rather than asked about. Named for what it takes, since
visualdynamics.report is the rendering package. The asking, the batch and the
folder are the same rule the typed functions share, and a batch
may mix kinds. The keywords are the union of theirs; per_octave
reaches the random halves alone.
Every run's kind is read before any report is written, so a batch
holding a run with no one-call report is refused whole rather than
stopping partway through. kinds overrides the reading run by run —
a restarted random run the system-ID guess took for one, a file
that declares no type (Reports from Runs' per-run choice,
2026-10-06) — and last may be given run by run too. progress(done, total) is told after
each run, the hook the window's bar and Cancel ride on (Reports
from Runs, 2026-10-04); a run is the smallest step, so a cancel
lands between runs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
run
|
str, os.PathLike, sequence of them, or `visualdynamics.ASK`
|
The controller's |
ASK
|
path
|
(str, PathLike or None)
|
The file to write, or a folder (one that exists, or a name ending in a separator) the report lands in under the run's own name. Beside the run when omitted. Several runs need a folder or None. |
None
|
last
|
float or Mapping
|
Import only the last |
None
|
kinds
|
Mapping
|
The report each run gets, by path: 'random', 'mixed' (a random
and a sine report), 'sine' or 'sysid'. A run it leaves out gets
the report its file declares ( |
None
|
geometry
|
str, os.PathLike, `visualdynamics.ASK` or None
|
A geometry file to import and link to the run. Asked for once for
the whole batch when the run was asked for, Cancel meaning none;
|
None
|
photos
|
str, os.PathLike or sequence of them
|
A folder of photographs, one photograph, or several in order. |
None
|
per_octave
|
int
|
Bands per octave for the banded PSD and specification; the project's default (a sixth) when omitted. The random halves alone read it. |
None
|
unit_system
|
UnitSystem
|
The units the report is written in; the package default, 'in-slinch-lbf-s (g)', when omitted. |
None
|
marking
|
str
|
The banner across the top and bottom of every page; 'UNCLASSIFIED' when omitted. |
None
|
progress
|
callable
|
Called as |
None
|
Returns:
| Type | Description |
|---|---|
str or list of str
|
The path written; a list of them when several runs were chosen, a list of runs was given, or a run was random and sine (two reports). |
Source code in src/visualdynamics/project.py
4795 4796 4797 4798 4799 4800 4801 4802 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876 4877 4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892 4893 4894 4895 4896 4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 4919 4920 4921 4922 4923 4924 4925 4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 | |
system_id_report
¶
system_id_report(run: Any = ASK, path: str | PathLike | None = None, *, last: float | None = None, geometry: Any = None, photos: Any = None, unit_system: Any = None, marking: str | None = None) -> Any
A Rattlesnake system identification in, an HTML report out.
visualdynamics.system_id_report('sysid.nc4', 'sysid.html')
visualdynamics.system_id_report() # ask for both
system_id_run followed by the System ID report: the measured
plant's FRFs, the coherence map, and the signal-to-noise of the
measurement, line by line and per channel — where it falls to
zero decibels, the plant is the room.
Everything about being asked, writing a batch and taking a folder
is random_vibration_report's, and means the same things: left
out, the run is asked for and as many may be chosen as there are
reports wanted; the geometry is asked for once for the whole
batch when the run was asked for, Cancel meaning none;
visualdynamics.ASK forces either question; and path may be the
file to write or the folder to write into.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
run
|
str, os.PathLike, sequence of them, or `visualdynamics.ASK`
|
The controller's |
ASK
|
path
|
(str, PathLike or None)
|
The file to write, or a folder (one that exists, or a name ending in a separator) the report lands in under the run's own name. Beside the run when omitted. Several runs need a folder or None. |
None
|
last
|
float
|
Import only the last |
None
|
geometry
|
str, os.PathLike, `visualdynamics.ASK` or None
|
A geometry file to import and link to the run. Asked for once for
the whole batch when the run was asked for, Cancel meaning none;
|
None
|
photos
|
str, os.PathLike or sequence of them
|
A folder of photographs, one photograph, or several in order. |
None
|
unit_system
|
UnitSystem
|
The units the report is written in; the package default, 'in-slinch-lbf-s (g)', when omitted. |
None
|
marking
|
str
|
The banner across the top and bottom of every page; 'UNCLASSIFIED' when omitted. |
None
|
Returns:
| Type | Description |
|---|---|
str or list of str
|
The path written; a list of them when several runs were chosen or a list of runs was given. |
Source code in src/visualdynamics/project.py
4944 4945 4946 4947 4948 4949 4950 4951 4952 4953 4954 4955 4956 4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 | |
random_vibration_report
¶
random_vibration_report(run: Any = ASK, path: str | PathLike | None = None, *, last: float | None = None, geometry: Any = None, photos: Any = None, per_octave: int | None = None, unit_system: Any = None, marking: str | None = None) -> Any
A Rattlesnake random vibration run in, an HTML report out.
visualdynamics.random_vibration_report('run.nc4', 'report.html')
visualdynamics.random_vibration_report(
'run.nc4', 'report.html', last=100.0,
geometry='article.stp', photos='setup_photos/')
visualdynamics.random_vibration_report() # ask for both
visualdynamics.random_vibration_report('run.nc4', 'reports/')
Left out, it asks. Called with no run, it opens a file dialog
and takes as many runs as are chosen, writing one report each and
returning the list; a run chosen that way is asked about its
geometry too, where Cancel means none. visualdynamics.ASK in
either place forces the question, so a script with a run in hand
can still be asked for the geometry. A run given without a
geometry keyword means no geometry, as it always has, and
nothing opens.
A folder is a folder. path may be the file to write, or a
folder the report lands in under the run's own name with an
.html extension. Without a path it lands beside the run. Several
runs need a folder or no path, never one file name.
The whole workflow in one call, with nothing to click (Brandon,
2026-09-19): import the run — or only its last last seconds, a
shorter run taken whole — detect the averaging, compute the PSDs,
band them and the specification onto octave bands, compute the
multiple coherence, bring in the geometry and the photographs when
they are given, generate the Random Vibration report and write it
as one self-contained HTML file. Returns the path written, or the
list of them when several runs were chosen. The keywords are
random_vibration_run's, plus unit_system for the units the
report is written in.
Everything it does is random_vibration_run followed by
generate_report and export_report; reach for those instead when
the project is wanted afterwards — to write the test summary, or to
save it as .vdyn.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
run
|
str, os.PathLike, sequence of them, or `visualdynamics.ASK`
|
The controller's |
ASK
|
path
|
(str, PathLike or None)
|
The file to write, or a folder (one that exists, or a name ending in a separator) the report lands in under the run's own name. Beside the run when omitted. Several runs need a folder or None. |
None
|
last
|
float
|
Import only the last |
None
|
geometry
|
str, os.PathLike, `visualdynamics.ASK` or None
|
A geometry file to import and link to the run. Asked for once for
the whole batch when the run was asked for, Cancel meaning none;
|
None
|
photos
|
str, os.PathLike or sequence of them
|
A folder of photographs, one photograph, or several in order. |
None
|
per_octave
|
int
|
Bands per octave for the banded PSD and specification; the project's default (a sixth) when omitted. |
None
|
unit_system
|
UnitSystem
|
The units the report is written in; the package default, 'in-slinch-lbf-s (g)', when omitted. |
None
|
marking
|
str
|
The banner across the top and bottom of every page; 'UNCLASSIFIED' when omitted. |
None
|
Returns:
| Type | Description |
|---|---|
str or list of str
|
The path written; a list of them when several runs were chosen or a list of runs was given. |
Source code in src/visualdynamics/project.py
5007 5008 5009 5010 5011 5012 5013 5014 5015 5016 5017 5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031 5032 5033 5034 5035 5036 5037 5038 5039 5040 5041 5042 5043 5044 5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 | |