visualdynamics.core.geometry¶
geometry
¶
Test/model geometry: nodes, coordinate systems, tracelines, elements.
Node coordinates are stored in SI meters once their units are known, resolved
into the global cartesian frame. A geometry imported from a source that does
not declare units (sdynpy .npz, exodus, a UNV without a 164) keeps the file's
raw coordinates and reports length_unit is None until define_units() is
called. Element types use the UFF dataset 2412 FE descriptor codes as the
interchange vocabulary.
Classes:
| Name | Description |
|---|---|
Geometry |
Nodes, coordinate systems, tracelines, elements and blocks. |
Functions:
| Name | Description |
|---|---|
placed |
One point written in a frame, as global cartesian. |
written_in |
The inverse of |
to_global |
Node coordinates, each written in the frame it is placed in, as |
to_local |
The inverse of |
reconcile_type |
The descriptor that matches how many nodes an element really |
face_corners |
How many corners a face element is drawn with: three for a |
Classes¶
Geometry
¶
Geometry(node_id: Ids, node_xyz: ArrayLike, node_def_cs: ArrayLike | None = None, node_disp_cs: ArrayLike | None = None, node_color: ArrayLike | None = None, cs_id: Ids | None = None, cs_name: Sequence[str] | None = None, cs_type: ArrayLike | None = None, cs_matrix: ArrayLike | None = None, traceline_id: Ids | None = None, traceline_color: ArrayLike | None = None, traceline_desc: Sequence[str] | None = None, traceline_conn: Sequence[ArrayLike] | None = None, elem_id: Ids | None = None, elem_type: ArrayLike | None = None, elem_color: ArrayLike | None = None, elem_conn: Sequence[ArrayLike] | None = None, elem_block: Ids | None = None, block_id: Ids | None = None, block_name: Sequence[str] | None = None, length_unit: str | None = None)
Nodes, coordinate systems, tracelines, elements and blocks.
Parameters are array-likes; connectivity lists contain one integer array of node ids per traceline/element. All coordinates in SI meters.
The arrays below are the storage; nodes, coordinate_systems,
tracelines, elements and blocks are views onto them, which
is how the tree lists a geometry and how a script should usually
reach one. A view is not a copy — writing through a row writes here.
Attributes:
node_id: Every node's id. Unique, because connectivity and
placement refer to nodes by id.
node_xyz: (nodes, 3) coordinates, in meters once
length_unit is declared and the file's raw numbers before
that.
node_def_cs: The coordinate system each node is placed in.
node_disp_cs: The system each node is measured in — the frame a
shape's values at that node are expressed in.
node_color: Palette index per node.
cs_id: Coordinate system ids. Unique, for the same reason as
nodes.
cs_name: A name per system, often empty.
cs_type: 0 cartesian, 1 cylindrical, 2 spherical (CS_TYPES).
cs_matrix: (systems, 4, 3) — three direction rows then the
origin, so cs_matrix[i, 3] is where system i sits.
traceline_id: Ids of the display polylines. Not unique: a UNV
trace line that lifts the pen arrives as several runs under
one id, and deleting that id removes all of them.
traceline_color: Palette index per line.
traceline_desc: Free text per line.
traceline_conn: One array of node ids per line, in drawing order.
elem_id: Element ids. Labels — nothing refers to them.
elem_type: UFF dataset 2412 descriptor code per element
(ELEMENT_TYPES names them and says how each is drawn).
elem_color: Palette index per element.
elem_conn: One array of node ids per element.
elem_block: Which block each element belongs to, by block id.
block_id: The declared blocks. Unique, since elements name them.
block_name: A name per block — 'wing', 'arm front left'. This
is where a mesh records that a region is a different part
from its neighbor, and fem.Model.from_geometry reads a
member's section from it.
length_unit: What the coordinates are in, or None while that
has not been declared — in which case they are the file's own
numbers and nothing has been scaled.
Methods:
| Name | Description |
|---|---|
validate |
Check the geometry hangs together — every element's nodes |
node_index |
Positions of the given node ids in the node arrays. |
contains_nodes |
Boolean array: which of |
missing_dofs |
The DOF strings whose node this geometry does not define. |
suggest_mass_properties |
The centroid of the nodes as the reference point, and no |
define_units |
Declare what the coordinates are in, converting them to SI. |
undefine_units |
Take the declaration back, restoring the file's raw coordinates. |
dof_direction |
A DOF's direction in global coordinates, through the frame |
add_node |
Append a node at |
add_coordinate_system |
Append a coordinate system. Returns its id. |
add_traceline |
Append a traceline through the given nodes. Returns its index. |
block_of |
The name of the block an element belongs to, or ''. |
elements_in |
The ids of the elements in a block, named or numbered. |
add_block |
Declare an element block. Returns its id. |
add_element |
Append an element. Type defaults to whatever fits the node count: |
renumber_node |
Give a node a new id, carrying its tracelines and elements over. |
renumber_block |
Give a block a new id, carrying its elements over. |
renumber_coordinate_system |
Give a coordinate system a new id, repointing the nodes using it. |
delete_nodes |
Remove nodes, and anything that referenced them. |
delete_coordinate_systems |
Remove coordinate systems, reassigning any node that used them. |
delete_tracelines |
Remove tracelines by id. Ids that are not there are ignored. |
delete_blocks |
Remove blocks, moving anything in them into the first one left. |
delete_elements |
Remove elements by id. Ids that are not there are ignored. |
save |
Write the geometry to a file of its own. |
plot |
Draw the geometry: nodes, elements and tracelines. |
plot_dofs |
This geometry with labeled arrows at every DOF |
Attributes:
| Name | Type | Description |
|---|---|---|
num_nodes |
int
|
How many nodes the geometry defines. |
nodes |
EntityView
|
Every node: |
coordinate_systems |
EntityView
|
Every coordinate system: |
tracelines |
EntityView
|
Every traceline: |
elements |
EntityView
|
Every element: |
blocks |
EntityView
|
Every element block: |
extent |
tuple[ndarray, ndarray]
|
(min_xyz, max_xyz), in meters once units are defined. |
units_defined |
bool
|
Whether the geometry knows what its coordinates mean. |
Source code in src/visualdynamics/core/geometry.py
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 | |
Attributes¶
coordinate_systems
property
¶
coordinate_systems: EntityView
Every coordinate system: ids, names, types, matrices.
blocks
property
¶
blocks: EntityView
Every element block: ids and names.
A block groups elements rather than holding them — which elements
are in one is read off elem_block (elements_in), so moving an
element between blocks is an edit to the element.
extent
property
¶
(min_xyz, max_xyz), in meters once units are defined.
units_defined
property
¶
Whether the geometry knows what its coordinates mean.
False until define_units names the length unit.
Methods:¶
validate
¶
Check the geometry hangs together — every element's nodes present, every identifier unique — and report what does not.
Source code in src/visualdynamics/core/geometry.py
node_index
¶
Positions of the given node ids in the node arrays.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
node_ids
|
int or sequence of int
|
The identifiers, one or many. |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Each identifier's row in the node arrays. |
Source code in src/visualdynamics/core/geometry.py
contains_nodes
¶
Boolean array: which of node_ids this geometry defines.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
node_ids
|
int or sequence of int
|
The identifiers, one or many. |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
A boolean per identifier: whether the geometry has it. |
Source code in src/visualdynamics/core/geometry.py
missing_dofs
¶
The DOF strings whose node this geometry does not define.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dofs
|
sequence of str
|
The degrees of freedom to check, such as '101Z+'. |
required |
Returns:
| Type | Description |
|---|---|
list of str
|
Those the geometry has no node for — what makes a data object incompatible with it. |
Source code in src/visualdynamics/core/geometry.py
suggest_mass_properties
¶
suggest_mass_properties() -> MassProperties
The centroid of the nodes as the reference point, and no mass — unit rigid-body shapes about the middle of the model.
The seed the rigid-body pane opens with and what
generate_rigid_body_modes adopts when nothing was set: unlike
a whole-record truncation, this is a real answer, and the one
the virtual-point transformation wants most often.
Returns:
| Type | Description |
|---|---|
MassProperties
|
The centroid, unscaled. |
Source code in src/visualdynamics/core/geometry.py
define_units
¶
define_units(length_unit: str) -> Geometry
Declare what the coordinates are in, converting them to SI.
Re-declaring reinterprets the original file values rather than scaling twice, so a wrong guess can simply be corrected.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
length_unit
|
str
|
The unit the coordinates are in, such as 'm' or 'in'. |
required |
Returns:
| Type | Description |
|---|---|
Geometry
|
Self, converted to SI in place. |
Source code in src/visualdynamics/core/geometry.py
undefine_units
¶
undefine_units() -> Geometry
Take the declaration back, restoring the file's raw coordinates.
Source code in src/visualdynamics/core/geometry.py
dof_direction
¶
A DOF's direction in global coordinates, through the frame its node is measured in.
'101X+' at a node whose node_disp_cs is rotated is not global
X. The report's grid of control channels reads which global
axis a channel is nearest and how far off it sits (Brandon,
2026-09-19), and this is where that is answered. A rotational
DOF points along its axis, as the DOF arrows draw it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dof
|
str
|
The DOF, such as '101X+' or '1313RZ-'. |
required |
Returns:
| Type | Description |
|---|---|
ndarray or None
|
A unit vector, or None when the DOF names no node this geometry has, no axis (a node number alone), or a node measured in a cylindrical or spherical frame — whose local axes turn with the node's position, a reading this does not attempt rather than half-answer. |
Source code in src/visualdynamics/core/geometry.py
add_node
¶
add_node(xyz: ArrayLike, node_id: int | None = None, color: int = 1, def_cs: int | None = None, disp_cs: int | None = None) -> int
Append a node at xyz (SI). Returns its id.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
xyz
|
array_like
|
The node's coordinates. |
required |
node_id
|
int
|
Its identifier. The next free one when omitted. |
None
|
color
|
int
|
Its display color index. |
1
|
def_cs
|
int
|
The coordinate system the position is given in. |
None
|
disp_cs
|
int
|
The coordinate system displacements are measured in. |
None
|
Returns:
| Type | Description |
|---|---|
int
|
The node's identifier. |
Source code in src/visualdynamics/core/geometry.py
add_coordinate_system
¶
add_coordinate_system(origin: ArrayLike = (0.0, 0.0, 0.0), rotation: ArrayLike | None = None, cs_id: int | None = None, name: str = '', cs_type: int = 0) -> int
Append a coordinate system. Returns its id.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
origin
|
array_like
|
The system's origin. |
(0, 0, 0)
|
rotation
|
array_like
|
A 3x3 rotation matrix. Identity when omitted. |
None
|
cs_id
|
int
|
Its identifier. The next free one when omitted. |
None
|
name
|
str
|
What to call it. |
''
|
cs_type
|
int
|
0 cartesian, 1 cylindrical, 2 spherical. |
0
|
Returns:
| Type | Description |
|---|---|
int
|
The coordinate system's identifier. |
Source code in src/visualdynamics/core/geometry.py
add_traceline
¶
Append a traceline through the given nodes. Returns its index.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
node_ids
|
int or sequence of int
|
The nodes the line passes through, in order. |
required |
color
|
int
|
Its display color index. |
1
|
description
|
str
|
A label for it. |
''
|
Returns:
| Type | Description |
|---|---|
int
|
The traceline's identifier. |
Source code in src/visualdynamics/core/geometry.py
block_of
¶
The name of the block an element belongs to, or ''.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
elem_id
|
int
|
Which element. |
required |
Returns:
| Type | Description |
|---|---|
str
|
The name of the block it belongs to. |
Source code in src/visualdynamics/core/geometry.py
elements_in
¶
The ids of the elements in a block, named or numbered.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
block
|
int or str
|
A block, by identifier or by name. |
required |
Returns:
| Type | Description |
|---|---|
list of int
|
The identifiers of the elements it holds. |
Source code in src/visualdynamics/core/geometry.py
add_block
¶
Declare an element block. Returns its id.
A block with nothing in it is legitimate — exodus files carry empty ones, and a block has to exist before an element can be put in it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
What to call the block. |
''
|
block_id
|
int
|
Its identifier. The next free one when omitted. |
None
|
Returns:
| Type | Description |
|---|---|
int
|
The block's identifier. |
Source code in src/visualdynamics/core/geometry.py
add_element
¶
add_element(node_ids: Ids, elem_type: int | None = None, color: int = 1, block: int | None = None) -> int
Append an element. Type defaults to whatever fits the node count: 2 nodes a beam, 3 a triangle, 4 a quadrilateral. Returns its index.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
node_ids
|
int or sequence of int
|
The nodes the element connects, in order. |
required |
elem_type
|
int
|
The element type code. Inferred from the node count when omitted. |
None
|
color
|
int
|
Its display color index. |
1
|
block
|
int
|
Which block it belongs to. |
None
|
Returns:
| Type | Description |
|---|---|
int
|
The element's identifier. |
Source code in src/visualdynamics/core/geometry.py
renumber_node
¶
Give a node a new id, carrying its tracelines and elements over.
Connectivity names nodes by id, so a rename that left it alone would orphan every line and face touching the node.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
row
|
int
|
Which node, by row. |
required |
node_id
|
int
|
Its new identifier. |
required |
Returns:
| Type | Description |
|---|---|
None
|
|
Source code in src/visualdynamics/core/geometry.py
renumber_block
¶
Give a block a new id, carrying its elements over.
An element names its block by id, so a renumber that left them
alone would put every element of the block in a block that is no
longer there — which validate refuses, after the damage.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
row
|
int
|
Which block, by row. |
required |
block_id
|
int
|
Its new identifier. |
required |
Returns:
| Type | Description |
|---|---|
None
|
|
Source code in src/visualdynamics/core/geometry.py
renumber_coordinate_system
¶
Give a coordinate system a new id, repointing the nodes using it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
row
|
int
|
Which system, by row. |
required |
cs_id
|
int
|
Its new identifier. |
required |
Returns:
| Type | Description |
|---|---|
None
|
|
Source code in src/visualdynamics/core/geometry.py
delete_nodes
¶
Remove nodes, and anything that referenced them.
A traceline or element naming a deleted node cannot survive, so it goes too. Returns what was removed, for reporting.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
node_ids
|
int or sequence of int
|
The identifiers, one or many. |
required |
Returns:
| Type | Description |
|---|---|
dict of str to int
|
How many of each kind were removed, including the dependents that went with them. |
Source code in src/visualdynamics/core/geometry.py
delete_coordinate_systems
¶
Remove coordinate systems, reassigning any node that used them.
The last coordinate system is never removed — nodes must reference something.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cs_ids
|
int or sequence of int
|
The identifiers, one or many. |
required |
Returns:
| Type | Description |
|---|---|
dict of str to int
|
How many of each kind were removed, including the dependents that went with them. |
Source code in src/visualdynamics/core/geometry.py
delete_tracelines
¶
Remove tracelines by id. Ids that are not there are ignored.
One id can name several polylines — a UNV trace line that lifts the pen arrives split into its drawn runs, all still that one trace line — and deleting it removes all of them, which is what deleting that trace line means.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
traceline_ids
|
int or sequence of int
|
The identifiers, one or many. |
required |
Returns:
| Type | Description |
|---|---|
dict of str to int
|
How many of each kind were removed, including the dependents that went with them. |
Source code in src/visualdynamics/core/geometry.py
delete_blocks
¶
Remove blocks, moving anything in them into the first one left.
Deleting the grouping must not delete what was grouped — an element is a piece of the mesh and a block is a label on it — so the elements move rather than go, the way a node whose coordinate system is deleted is reassigned. The last block cannot go while any element names one; with no elements at all there is nothing to hold and the geometry may have none.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
block_ids
|
int or sequence of int
|
The identifiers, one or many. |
required |
Returns:
| Type | Description |
|---|---|
dict of str to int
|
How many of each kind were removed, including the dependents that went with them. |
Source code in src/visualdynamics/core/geometry.py
delete_elements
¶
Remove elements by id. Ids that are not there are ignored.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
elem_ids
|
int or sequence of int
|
The identifiers, one or many. |
required |
Returns:
| Type | Description |
|---|---|
dict of str to int
|
How many of each kind were removed, including the dependents that went with them. |
Source code in src/visualdynamics/core/geometry.py
save
¶
Write the geometry to a file of its own.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str or PathLike
|
Where to write it. |
required |
Returns:
| Type | Description |
|---|---|
None
|
|
Source code in src/visualdynamics/core/geometry.py
plot
¶
plot(unit_system: UnitSystem | None = None, **kwargs: Any) -> Any
Draw the geometry: nodes, elements and tracelines.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
unit_system
|
UnitSystem
|
Units to draw in. |
None
|
**kwargs
|
Any
|
Passed through to the scene. |
{}
|
Returns:
| Type | Description |
|---|---|
object
|
The plot widget or plotter. |
Source code in src/visualdynamics/core/geometry.py
plot_dofs
¶
plot_dofs(source: Any, quantity: str, unit_system: UnitSystem | None = None, **kwargs: Any) -> Any
This geometry with labeled arrows at every DOF source
measures as quantity — the GUI's DOF arrows.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
DataArray
|
The object whose degrees of freedom are drawn. |
required |
quantity
|
str
|
Which quantity's DOFs to show, such as 'acceleration'. |
required |
unit_system
|
UnitSystem
|
Units to draw in. |
None
|
**kwargs
|
Any
|
Passed through to the scene. |
{}
|
Returns:
| Type | Description |
|---|---|
object
|
The plotter the scene is in. |
Source code in src/visualdynamics/core/geometry.py
Functions:¶
placed
¶
One point written in a frame, as global cartesian.
kind is the frame's CS_TYPES code and matrix its (4, 3)
rows — three directions then the origin. A cylindrical frame
writes (r, theta, z) and a spherical one (r, theta from +z, phi),
both with the angles in degrees, which is what the universal
file, a Nastran CORD2C/S and sdynpy all mean by them.
Source code in src/visualdynamics/core/geometry.py
written_in
¶
The inverse of placed: a global cartesian point as the frame
would write it, so a file states its nodes the way it stated them
before.
Source code in src/visualdynamics/core/geometry.py
to_global
¶
to_global(node_xyz: ArrayLike, node_def_cs: ArrayLike, cs_id: Ids, cs_type: ArrayLike, cs_matrix: ArrayLike) -> ndarray
Node coordinates, each written in the frame it is placed in, as
global cartesian — what Geometry stores.
A file states a node's position in whatever frame it was drawn in,
and the frame is the node's def_cs. Reading those numbers as if
they were global puts the node somewhere else entirely, which is
what a geometry with local placement frames looked like (Brandon,
2026-09-20). A node whose frame the file does not define, or which
names the global frame, is taken as written.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
node_xyz
|
array - like
|
|
required |
node_def_cs
|
array - like
|
The frame each node is placed in. |
required |
cs_id
|
array - like
|
The frames themselves, as |
required |
cs_type
|
array - like
|
The frames themselves, as |
required |
cs_matrix
|
array - like
|
The frames themselves, as |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
|
Source code in src/visualdynamics/core/geometry.py
to_local
¶
to_local(node_xyz: ArrayLike, node_def_cs: ArrayLike, cs_id: Ids, cs_type: ArrayLike, cs_matrix: ArrayLike) -> ndarray
The inverse of to_global, for a writer that states each node
in the frame the geometry says it is placed in.
Source code in src/visualdynamics/core/geometry.py
reconcile_type
¶
The descriptor that matches how many nodes an element really has, when the two disagree.
A file states both, and they can contradict each other: one wrote a four-node element as descriptor 41, a plane-stress triangle, and it drew as a triangle with the fourth node dropped (Brandon, 2026-09-20). The count is the element; the descriptor is a label on it. So a descriptor whose count is wrong is exchanged for the one in its own family that carries that many nodes — 41 with four nodes is 44, the plane-stress quadrilateral, keeping the family the file chose and mending only what it got wrong.
2412 numbers a family in one decade, triangles before quadrilaterals, which is what makes the sibling findable. When no sibling fits, the descriptor stands: an element nobody can name is better carried as written than renamed to a guess.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
code
|
int
|
The descriptor the file gave. |
required |
node_count
|
int
|
How many nodes the file actually listed for it. |
required |
Returns:
| Type | Description |
|---|---|
int
|
The descriptor to use. |
Source code in src/visualdynamics/core/geometry.py
face_corners
¶
How many corners a face element is drawn with: three for a triangle, four for a quadrilateral, whichever family its descriptor names and however many nodes it carries.
Read off the name in ELEMENT_TYPES rather than from the node
count, which cannot tell a nine-node cubic triangle from a
nine-node quadrilateral (Brandon, 2026-09-20).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
code
|
int
|
The element's descriptor. |
required |
Returns:
| Type | Description |
|---|---|
int
|
3 or 4. |