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 link 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 |
Link 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. |
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,
links: Iterable[LinkGroup] | 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 link 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 |
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. |
role_group |
The link group carrying a role, or None. |
place |
Put one object into the named group, making it if need be. |
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 link 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 |
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 |
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 .vdyn file. |
open |
Read a .vdyn project back. |
journal_as |
Record a stretch of front-end work as one replaying line. |
record_setting |
A settings write, journalled the way a script would make it. |
record_call |
A method call on an object, journalled 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: |
groups |
list[Selection]
|
Every link group, the Basis first, each reached by type. |
other |
Selection
|
The one link 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.
other
property
¶
other: Selection
The one link 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 link 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 link 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 link 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.
Link 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 labelled 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 mislabelled is mislabelled 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, normalised 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
|
Returns:
| Type | Description |
|---|---|
list of str
|
The group's members after linking. |
Source code in src/visualdynamics/project.py
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
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 link 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 link 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 grey 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
role_group
¶
The link 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 |
|---|---|
LinkGroup 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
991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 | |
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 labelled. |
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 link 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 LinkGroup
|
Link 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.
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
1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 | |
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-normalise 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
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 judgement — 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 judgement, 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
1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 | |
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
2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 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 | |
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
¶
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' — 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'
|
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
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
|
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 .vdyn file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str or PathLike
|
Where to write the |
required |
Returns:
| Type | Description |
|---|---|
str
|
The path written. |
Source code in src/visualdynamics/project.py
open
classmethod
¶
open(path: str | PathLike) -> Project
Read a .vdyn project back.
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; journalled 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, journalled 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, journalled as a script makes it.
The front ends' funnel for object verbs that are not Project verbs — a traceline added to a geometry, a photo renamed — each an act of the session the console must speak (Brandon, 2026-08-30: adding a traceline 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(
links: Iterable[LinkGroup],
mapping: dict[str, str],
roles_taken: Iterable[str] = (),
) -> list[LinkGroup]
Link 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
) -> Project
A Rattlesnake random vibration run, worked up into a project.
project = visualdynamics.random_vibration_run('run.nc4')
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 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 frames the spectra are averaged over are detected from the data itself when the file does not carry them, exactly as the bar's act does — so a script and a click reach the same numbers.
Source code in src/visualdynamics/project.py
random_vibration_report
¶
random_vibration_report(
run: str | PathLike,
path: str | PathLike | None = None,
*,
per_octave: int | None = None,
unit_system: Any = None,
) -> str
A Rattlesnake random vibration run in, an HTML report out.
visualdynamics.random_vibration_report('run.nc4')
The whole workflow in one call: import, PSDs, octave bands, multiple
coherence, the Random Vibration report, and the self-contained HTML.
Returns the path written, which defaults to the run's own name with
an .html extension.
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 add photographs or a geometry,
to write the test summary, or to save it as .vdyn.