The objects¶
A project holds objects, and every object is a plain Python thing with arrays in it. The window is a view onto them; a script holds the same objects and calls the same methods. This page is the map: what kinds there are, what each one stores, where it comes from, and what can be done with it. The API reference has the signatures — each heading below links to its page.
Two rules hold across all of them:
- Values are stored in SI once their units are known. A record
whose units were never declared keeps the file's raw numbers and
says so (
ordinate_dim == 'unknown'); nothing is ever scaled by a guess. Units has the whole story. - A project reaches an object by its kind —
project.frf,project.time_histories— as Projects explains, andproject.verbs(obj)lists the processing verbs that apply to any object, each with its one-line reading, andproject.selection_verbs(*names)the ones a selection of several can take together. Those lists and the acts on the window's bar read one table, so they cannot disagree.
Geometry — core.geometry¶
Kind geometry. Nodes, coordinate systems, elements and element groups, held
as flat arrays: node_id, node_xyz (meters), the placement and
measurement system of each node, cs_matrix (three direction rows
and an origin per system), one connectivity array per element, and
the element group each element belongs to with its name. nodes,
coordinate_systems, elements and groups are row views onto
those arrays — what a script usually reaches; writing through a row
writes the array.
There are no tracelines. A line drawn through nodes is an element
group of two-node beam elements with no properties — add_beams
chains one through the nodes, drawn_lines reads them back as the runs
they were — and the same group becomes structure the moment it is given
a section. An element group holds one element family (beams,
triangles, quads, tetras, wedges, hexes; element_family names a
type code's), which is what exodus has always meant by an element block; a source
that mixes families is split on arrival.
Comes from universal files, Exodus, Nastran and Femap decks, STEP and
IGES, 3MF and STL meshes, sdynpy arrays and .vdyn files. Does:
plot and plot_dofs; add_node, add_element, add_beams,
add_block and their delete_* and renumber_* counterparts;
missing_dofs against a data object; extent; validate; save.
A geometry also carries mass_properties — the reference point its
rigid-body modes pivot on and, when the set is to be mass-normalized,
the mass and inertia tensor about it
(core.rigid.MassProperties).
They ride the geometry the way averaging rides a time history: set in
the rigid-body view (the toggle on the 3-D view's bar, offered to one
selected geometry), saved with it, and suggest_mass_properties seeds
the centroid with no mass. Generate Rigid Body Mode Shapes
(project.generate_rigid_body_modes) makes the six-mode shape set in
the geometry's group, previewed on the model as the point is set.
A geometry also carries its default view — how it opens in 3-D
(View): the direction from
the model to the eye and which way is up, no distance or zoom, since
every view fits the model. It is how the app opens the geometry and
everything drawn on it (shapes, an ODS, the DOF arrows), where Reset
View on the 3-D view's bar returns, and how the report's scenes and
the exported figures (plot, plot_dofs, the animations) are drawn.
In the app, turn the model the way you want it and use Set Default
View on the geometry's bar; from a script,
from visualdynamics import View
project.set_view('BARC', View(eye=(1, 1, -1), up=(0, 1, 0))) # built y-up
None goes back to the default, from +X+Y+Z with Z up. Only how the
model is looked at changes; its nodes are not turned. The view is
saved with the geometry.
A geometry is also a finite element model once its element groups say what
they are made of. The pencil on an element group's row in the tree opens the
Element Groups table on it, and its property columns take, per
element group, a material (name, E, ν, ρ) and either a thickness — for an element group
of quads or triangles — or a section and an orientation vector for an
element group of beams. A section is built from its shape: round tube, rod,
rectangle, rectangular tube, I-beam, channel or angle, picked in the
Shape column, whose Dimensions cell then says what it needs (D=?, t=?
for a round tube) and computes A, Iy, Iz and the torsion constant J
from what is typed; custom takes the four numbers typed instead.
Every shape puts its width or flanges along the section's local y and
its depth along local z, and the orientation vector names local y — so
for an I-beam or a channel it points across the flanges, and Iy is the
strong axis. An angle's leg axes are not principal, so its Iy and Iz are
its principal moments and its Dimensions cell says where to point the
orientation (45° from the long leg for an equal angle). A channel's and
an angle's shear center is off the centroid, and the twisting that
causes is not modeled; a rolled channel's tapered flanges put its
weak-axis Iz under the uniform-flange value given here. An element group of
two-node lines may instead be made rigid (massless), the last entry
of the Material list: each line is then a rigid link, its second node
moving exactly as its first does (translated by the first's rotation
about it), adding no mass — what a bolt joining two plates whose
mid-surfaces do not meet is. Such an element group needs no section, and its
modulus, density and ratio read blank. In the app a link is a beam
element: edit the geometry's Elements, switch on add mode (+), choose
the beam and, in the Element group drop-down beside it, New element group; click
the two nodes of each link — every link picked joins that new element group —
and give the element group rigid (massless) in the Element Groups table. The drop-down
lists the element groups of the family being added to and a new one, and opens
on the element group the editing began from, so a beam never lands among
plates. A bolted joint is quicker as a patch: select the elements under
the washer (click one, Shift-click the rest) and press Tie on the
bar — every node of the patch is linked to the nearest node of the element group
picked from its menu, or of a second patch picked next, and the links
go into the geometry's rigid element group, made the first time
(project.tie_elements, mesh.tie). Plates themselves connect only
where they share nodes: a structure built from planes is tied along the
lines where its planes meet. + New Geometry on the project row's
bar starts an empty geometry, and Add Plane on a geometry's bar meshes
one rectangle at its mid-thickness — a center, widths with one left at
zero to name the plane it lies in, and an element size, in the display
unit, typed in a pane beside the 3-D view and drawn there as it is
typed, turned by angles about X, Y and Z or by the rings around the
preview, a degree at a time, and slid onto the grid by its arrows —
into the element group named, its nodes that fall on nodes
already there becoming them (project.new_geometry, project.add_plane; in
mesh, mesh.plane, mesh.join
and mesh.assemble). Add Block is the same pane with all three widths: a box
meshed into eight-node solid bricks, for a part that is neither a beam
nor a plate (project.add_block, mesh.block, which also cuts
cylindrical holes). Merge Coincident Nodes on a geometry's bar ties
any geometry the same way after the fact, the tolerance asked in the
display unit; project.merge_coincident_nodes from a script. All of it is
shown and typed in the
current display unit system (psi and in⁴ in the inch system, MPa and mm⁴
in millimeters), the unit in the column's header, and held in SI; the
table follows the unit menu while it is open. The Material
cell is a drop-down over a small library — 6061-T6, 7075-T6, 2024-T3,
1018, A36 and 4130 steel, 304, 316 and 17-4 PH stainless, Ti-6Al-4V,
AZ31B magnesium, C26000 brass, C11000 copper, Inconel 718, acrylic,
polycarbonate, ABS and nylon 6/6 — and picking one fills the row with
the handbook's typical room-temperature values (fem.material(name)
in a script; fem.MATERIAL_LIBRARY carries each entry's note). They
are typical values, not the part's: with a certification in hand, type
its numbers over them, and any name typed into the cell is accepted as
a name. An element group of point elements takes a mass alone, typed in the
Mass column, and every element in it puts that mass at its node: a
bolt, a sensor or a fitting too small to mesh
(fem.GroupProperties(mass=...) in a script). A Nastran deck's plain
CONM2 cards arrive this way, one element group per distinct mass, and a deck
written back carries the masses on its CONM2 cards.
A group of two-node lines can be springs instead of beams: type a
stiffness by direction in its Stiffness cell (Kz=1e5, Kry=2e3, in the
display units, along and about the global axes), and each line becomes a
spring between its two nodes in each direction given, the nodes free to
coincide, as a joint between two parts meshed to the same point is. A
group of points given a stiffness is springs from each node to ground,
and a group of points given ground (fixed) from the Material list
holds each node in all six directions, which is a support, or the far
end of a spring line to ground. The Ground column names the directions
held instead (X, Y, Z pins a node and leaves it free to turn; all
is all six). A Nastran deck's CELAS springs and SPC supports arrive as
such groups, and a deck written back carries them as CELAS2 and SPC1
cards. Nodes in one place draw as one: hovering there captions every id
(104, 200 (2 here)), and Shift-clicking the spot again while adding an
element takes the one not picked yet, so a spring between two
coincident nodes is two clicks. A point mass hung on spring lines needs
no beam at its node. The Points family is always in the tree, so the
first point can be clicked into place (fem.GroupProperties(stiffness=
(...)) and GroupProperties(ground=True) in a script, or
Model.add_spring and Model.add_ground on a model directly). The properties land on
geometry.group_properties as
fem.GroupProperties and ride
the native file. Solve Modes (project.solve_modes) then builds the
model from the element groups (fem.Model.from_geometry) and adds its normal
modes in the geometry's group — free-free, the six rigid-body modes at
0 Hz first — asking for the highest frequency wanted and the damping to
give every mode. An element group with no properties, or the wrong kind for its
elements, is refused by name.
Photos — core.photos¶
Kind photos. Setup photographs as they arrived — names, formats
and the encoded images — never re-encoded, because a report embeds
them and a second JPEG generation gains nothing. Does: add_file,
rename, delete_photos, plot.
Channel table — core.channel_table¶
Kind channel_table. One frame, a row per channel and a fixed,
typed column set. Comes from a controller run or a spreadsheet. Does:
controls, sensitivities, ranges, dof_strings, units_for,
set_cell, rename_dof, delete_channels, save. The channel
table has its rules.
The data arrays — core.data¶
Every measured or computed curve is a DataArray subclass, and one
object holds many records sharing one abscissa: abscissa (the x
axis — seconds or hertz), ordinate shaped (records, samples), and
one entry per record of response_dof, reference_dof where the
type has a reference, block (which repeat), ordinate_dim and
ordinate_unit (what and in which SI unit), reference_unit for a
ratio, dimension_hint (what a file claimed without saying its
scale) and comment. Uneven and unsorted abscissas are allowed at
the door; anything that needs an even step asks at the point of use.
What every data array does: plot, plot_waterfall, save_plot,
save; define_units and undefine_units; delete_records and
rename_dof; display_ordinate and display_abscissa in a chosen
unit system; num_records and record_label.
The rows of a data object's grid in the project tree are its
coordinates, and the columns of a matrix are coordinates too. A
channel assigned to the wrong point at the instrument is corrected
there: double-click the row or column label, type the coordinate,
and the channel takes it — that channel, the coordinate and the
quantity the row or column is, wherever it appears in the object: a
CPSD's accelerometer is on both sides of its cross terms and moves as
one sensor, while an FRF's drive-point accelerometer (a row) and load
cell (a column) are two channels, and moving one leaves the other to
be moved explicitly. Everything derived from the object follows,
because a PSD computed from a mislabeled channel is mislabeled the
same way (project.rename_dof; without a quantity it moves every
channel at the point). A rename that would give two records one
coordinate and one quantity is refused: a load cell and an
accelerometer share a point, two accelerometers do not. A channel
table's row coordinate is its node and direction: type over the
row in the grid and the two cells change, type a node or a direction
in the table view and the row's coordinate follows. A channel table
imported beside the data it describes is linked with it — one file,
one group — and is otherwise its own object: a coordinate corrected
on the time history is not corrected on the table, or the reverse.
| kind | class | what it is | what it adds |
|---|---|---|---|
time_history |
TimeHistory |
the record as acquired, against time; carries its averaging, shocks and roles readings so every derivation reads one description |
compute_spectra, compute_psds, compute_cpsds, compute_frfs, compute_multiple_coherence, compute_srs, integrate, differentiate, filter, truncate, split_into_frames, sample_rate, drive_dofs |
transient_specification |
TransientSpecification |
a target waveform: what a transient test was controlled to, sample by sample | everything a time history does |
spectrum |
Spectrum |
the complex average of a record's frames — amplitude and phase per line | animate |
psd |
Psd |
the power average: real autospectra, complex cross spectra; bin_widths when the bins are not even |
area, to_octave, bin_bounds, principal_shapes, animate |
specification |
Specification |
a PSD with its band: the target and the warn and abort limits either side, as arrays beside the ordinate | limit, has_limits |
frf |
Frf |
response per unit reference, complex, one reference per record | plot_cmif, animate; the input to a modal fit |
coherence |
Coherence, MultipleCoherence |
how much of a response one reference explains, or all of them together; bounded 0 to 1 | plot_map |
srs |
Srs |
a shock response spectrum: the peak an oscillator of each natural frequency reached, laid out in octaves | damping |
shock_specification |
ShockSpecification |
an SRS with its band, conventionally +6 dB and −3 dB | limit, has_limits |
Sine — core.sine¶
Two kinds that are not data arrays, because each tone sweeps its own
frequencies on its own clock and different abscissas cannot share
one. sine_sweep_specification (SineSweepSpecification) is what a
sine test was controlled to: the tones, each with its breakpoints,
sweep law, bands and start time, over the control DOFs. sine_levels
(SineLevelSet) is one extraction — the per-tone levels a joint
Vold-Kalman solve read out of a recording, grouped the way the
specification groups its tones. Each level carries the noise floor beside every reading (floor) and where the tone sat under it (below_floor, the reading then reported at the floor); the set carries the smoothing it was read with (cycles), and a level the sweep clock was corrected on says by how much (drift_hz).
Shapes — core.shapes¶
Kind shapes. Mode shapes over a shared set of DOFs: frequency and
damping per mode, shape_matrix shaped (modes, dofs) with
coordinate naming each column, modal_mass, modal_damping and
mass_unit where a source carried them, a description per mode, and
unscaled when the fit had no drive point to pin the scale. A fitted
set is also the record of its fit, which is what Edit Fit reopens.
A geometry's rigid-body set (three translations, three rotations
about its reference point, frequency exactly zero) is unscaled
too unless mass and inertia were given, in which case it is
mass-normalized about the inertia's principal axes.
Any set carries data through itself: project.transform fits a
record's motions to the modes (q = Φ⁺u) and projects its forces
(Φᵀf), one record per mode and quantity at the modal coordinates
M1 … Mn — the letter and the mode's index, visibly not a node, the
same for every set; the object's provenance says which set — and
project.expand carries modal responses back to every DOF the set
covers. Every kind of data goes the same two ways in the form its
kind takes: a time history or spectrum as rows; a CPSD or a
specification as the matrix, S_qq = Φ⁺ S_uu Φ⁺ᴴ, which needs every
cross term between the shared channels — autospectra alone are refused
rather than completed with a guess, so compute the CPSDs, or import
the specification with the cross terms the controller wrote — and its
tolerance bands carried exactly when every channel wears the same one;
an FRF on its response rows and, when the set covers the drives, its
reference columns too. A shock response spectrum or a coherence does
not transform (a maximum, a ratio): transform the time history and
compute it again. A specification can also be written, on a
sheet that opens three ways: Specification on the table bar of a
lone shape set starts one at its modal coordinates — every shape, or
the shapes picked in the tree, so a virtual point's target is its
three translations and the rotations, left out, contribute nothing
when it is expanded; the same button
on a lone channel table starts one at its control channels; Edit
on the plot bar of a specification opens the specification itself
— all of its channels, or the ones whose records are picked in the
tree, and several specifications at once open as one sheet, a
column per channel under its specification's name, cross terms
within each. The sheet sits beside the plot — breakpoints and a
level per channel, every pair's coherence and phase (a pair left
unstated is absent; independent is a statement too), the a box that scales the selected levels by decibels. While the sheet is open the plot is the flat
one — every editing gesture lives there, so the 3-D stage stands
down until the sheet closes. The warning and abort bands are not on
the sheet at all: they are on the plot, as the shaded zones, and while the sheet is open each edge of each band
carries a drag handle: one over the whole range while the channel is
Uniform, and with Uniform off one per linear section of the
requirement — every segment between breakpoints, or every run of one
power law in the interpolated form — each moved on its own. Drag
one up or down — it snaps to quarter decibels, and its label says
which — and, when you let go, that edge moves to that value on every
channel the sheet holds in that frequency range: the whole
specification, or the channels picked in the tree. A specification
with no limits opens wearing the defaults, ±3 dB warning and ±6 dB
abort, there on the plot to drag; the sheet says they are not on the
object yet, and the first edit writes them. Two constraints
sit in the sheet's head beside the form buttons and apply to every
channel of the specification the same way: Symmetric keeps the
band above at minus the band below, Uniform keeps every segment at
one band. Both are on to begin with, and a specification opens with
whatever its own bands say. Both grids are the application's ordinary tables: a column
header selects a channel, a row header a breakpoint, Cmd-click adds
cells, and copy, paste, Delete to clear, Batch Edit and the fill
handle work as they do everywhere else; a frequency typed between
two others re-sorts the breakpoints. There is no button: every
edit lands on the specification as it is made, so switching to
another object never leaves an edit behind. Opened on a shape set
or a channel table, the sheet first makes the specification beside
it and edits that. A sheet holding every channel rewrites the
object in the sheet's own form — fold a controller's target to its
breakpoints and the object is the breakpoints; a sheet holding a
picked subset of channels merges back at the object's own lines
with the other channels untouched, so the name, links and place in
every report hold (project.author_specification with a
core.author.SpecificationDraft;
replace=True, and a list of names for a sheet spanning several).
A pair left unstated is absent from the specification, not
assumed; a cross term that varies with frequency opens unstated,
and the sheet says so. A sheet has two forms, chosen by the pair of buttons
at its top and applied to the whole specification whichever
channels the sheet is open on: Breakpoints, the few points a requirement is written
from, and Interpolated, the same requirement read onto evenly
spaced frequency lines as a controller writes its target. A
controller's target opens as lines and folds to its breakpoints
exactly, since its lines were read from them, and editing is done on
the few rows; a specification with no power-law structure keeps every
line and says so. The spacing the lines are read onto is the
specification's own, else a time history's averaging (sample rate
over frame length), else typed into the box beside the buttons.
Nothing is guessed. Written at modal coordinates and expanded through
the set, it is an exact control-channel target with every cross
term. Save As offers a specification as Rattlesnake random
specification (.npz) — the target file the controller's Random
environment loads before a test: the frequency lines, the whole
cross-spectral matrix at each, the four bands, and the node and
direction of every channel so the controller puts the matrix in its
own channel order. Most random tests run on autospectra alone, and
the controller's form of that is a matrix with zeros off the
diagonal, so a pair the specification does not hold is written as a
zero and a held mask records that it was a placeholder rather
than a statement of independence. The values go out in the
coherent unit system on display, which the file cannot record and
the status line names. The same file imports again, held cross
terms kept and placeholders left out; a controller's or sdynpy's
own target file imports too, an off-diagonal all zero or NaN read
as its placeholder for autospectra alone. The expansion
goes back — every mode for the motion of the structure, or the modes
picked in the tree for their contribution alone, u = φₖqₖ, the
result named for them (records= in a script). Both land in the
group with the set and the record — a modal object answers to the
shape set it came through rather than to a geometry, and fits where a
set has every mode it names — and both carry the record's averaging
frames and shock windows, which land on the same instants. The unit rule is
[q] = [u]/[Φ]:
unit rigid shapes give the virtual point's rotations in rad/s² and its
moments in lbf·in, mass-normalized shapes give the half-power mass
units (modal_acceleration, in/s²·slinch½), and a set with no mass
unit gives responses to declare. In the window, select the record and
the set together and press Transform to Modal Responses on the
bar (or Expand to Physical Responses, for a modal record); it
makes the object at once — a transform has no settings — and the
status line gives the account: DOFs shared, left out, the residual
the fit leaves, kept on the object as its transform_report
(core.transform).
Does: plot, animate, plot_mac, auto_mac, synthesize_frf,
covers, delete_modes, save.
Matched modes — core.matches¶
Kind matches. Pairs committed on a cross-MAC between two shape
sets: first and second name the sets, pairs the modes, macs
the value each pair had when it was committed. Does: add,
delete_matches.
Report — core.report¶
Kind report. An ordered list of blocks, each a plain dict bound to
project objects symbolically by default (@basis:Frf) or by a literal
name: text, plot, scene, table, photo, bars, the
matched-modes pairs and their overlay. Does: add, remove, move,
figures, unbound. Reports explains the bindings.
What can be done with what¶
The processing verbs live on the project, so a result is added, named and linked to its source in one call:
| on | verbs |
|---|---|
| a time history | filter_data, truncate_data, detect_shocks, compute_spectra, compute_psds, compute_cpsds, compute_srs; compute_frfs and compute_multiple_coherence when it has drive channels; extract_sine when the project holds a sine sweep specification; integrate and differentiate when the quantity allows; transform through a shape set whose DOFs it is measured on, and expand back when it holds that set's modal responses |
| a geometry | generate_rigid_body_modes; add_plane, a meshed rectangle of plates tied to what is there; add_block, a meshed box of solid bricks, likewise; tie_elements, a patch of elements tied rigidly to a block or a second patch; merge_coincident_nodes once it has elements; merge_groups, blocks of one element type and one material and thickness made one; solve_modes once its blocks carry their properties (the Blocks table: a material and a thickness or a section per block, a material alone for a block of solids), which builds the finite element model and solves it |
| the project itself | new_geometry, an empty geometry to build in; generate_report |
| a PSD, CPSD or specification | compute_octave — a specification's warning and abort limits band with it |
| an FRF | fit_modes |
| two shape sets | project_onto_basis, match_modes |
| any object with a sibling of its type | merge |
Every object also answers to the whole-project verbs — add,
remove, rename, duplicate, link, unlink, place,
set_basis, export, save, generate_report, export_report,
refresh — and project.verbs() with no argument lists all of the
processing verbs with their readings. The API page for
project has every signature.