visualdynamics.io.matlab¶
matlab
¶
MATLAB .mat files: the project file in MATLAB's container.
A .mat written here has the .vdyn schema and names
(docs/vdyn-format.md), so a MATLAB user gets load('modal.mat') and
structs with the right shapes and complex numbers assembled, rather
than HDF5 datasets to read one at a time with h5read (which a
.vdyn already allows). Everything a project holds has a MATLAB form,
so the reader reads its own files back completely.
The layout is implemented once, and not in this module. The writer
runs the native .vdyn writer into an HDF5 file held in memory and
converts that tree into .mat variables; the reader rebuilds an
in-memory HDF5 tree from the .mat and passes it to the native
loader. A field added to the schema is carried with no change here,
and the two file types cannot drift apart, because neither this module
nor native.py knows two layouts (principle 9). It costs one
in-memory HDF5 round trip per file, which is fast.
MATLAB holds three things differently from HDF5, and the same rules convert them in both directions:
- Ragged arrays, the
<name>_flat/<name>_offsetspairs (element connectivity), are one cell array<name>with one row vector per entry, which is how MATLAB holds a ragged list. - Numbered groups, the project's
objects/0000, 0001, …and a sine specification'stones/0, 1, …, are a cell array of structs in the same order (p.objects{3}.name); each object struct keeps thenameandkindits group's attributes held. - Vectors are columns. A one-dimensional dataset is written N×1,
MATLAB's own vector orientation, so it reads back one-dimensional;
a two-dimensional array (
ordinate, records × samples) stays as it is even with one row. MATLAB has no one-dimensional array, so a matrix with one column (a sine tone's amplitude over one channel) would read back as a vector. A struct holding one has amatrix_fieldscellstr naming it, written only in that case and read if present.
HDF5 attributes and datasets both become plain fields
(frf.function_type beside frf.ordinate), because a MATLAB struct
has only one kind of member and a hidden marker would be clutter. On
the way back the native loader asks for each field as the kind it
expects, so the rebuild writes every field as a dataset and, where it
is small enough, as an attribute too; the loader reads the one it
wants. Strings are char arrays, lists of strings are cellstr, and
'' (an undeclared unit) is MATLAB's empty char.
Values are in the display units, and every unit field names the unit
its values are in. A .mat exported in a display system holds each
record in that system's unit for its quantity, labeled with it: g
values with an ordinate_unit of 'g', (in/s**2)/lbf with an FRF
in those units, a geometry's coordinates in inches with length_unit
'in', shapes in slinch. The file names the system in a
unit_system variable, and the reader converts each object back to SI
from its own labels, so on reading the declared unit is the one it was
written in. Some values stay SI in every system, because the project
file defines them as SI: material and section properties, and the
abscissa, which is seconds or hertz. A record whose units were never
declared is written as it is, marked '', and is read back
undeclared. Without a display system (Project.save to .mat, or a
script that names none) the file holds exactly what the project file
holds, in SI, and a file with no unit_system variable is read as SI.
The conversion is the core's own, run on a copy: each object is
labeled with the display unit and undefine_units converts its values
to it, the inverse of how a declaration converts a file's numbers to
SI; the reader clears the labels and declares them again
(_in_display, _from_display).
The stamp visualdynamics_schema is written as a variable, and the
reader follows the .vdyn rule: a newer stamp is refused, and an
older one always loads. A .mat with no stamp is accepted in one case
only, a single struct in the documented layout of one object (a data
array with abscissa, ordinate and data_class or function_type,
a geometry with node_xyz, a shape set with shape_matrix, a channel
table with column_order), which is a user's own struct built to that
layout. Any other .mat is refused with a ValueError naming the
variables it holds, because reading an arbitrary workspace would be
guessing.
.mat version 5 (what save -v7 writes, and what scipy reads and
writes) holds at most 4 GB per variable. A record larger than that is
refused before anything is written, with the array named; version 7.3
files are HDF5 and are not read or written here yet.
Functions:
| Name | Description |
|---|---|
handles |
Whether |
sniff |
Whether |
save |
Write an object, or a whole project, as a |
load |
Read a |
Functions:¶
handles
¶
Whether obj can be written as a .mat: any object the project
file holds, or a whole project.
Source code in src/visualdynamics/io/matlab.py
sniff
¶
Whether path ends in .mat, in any case (.MAT too).
The suffix is the only check. Whether the file was written here is
decided by load, which raises a ValueError saying what is wrong;
that is more useful than a message that no importer recognized the
file.
Source code in src/visualdynamics/io/matlab.py
save
¶
Write an object, or a whole project, as a .mat file.
With a unit_system, the values are in that system's units and
each unit field names them; with None, the values are SI, exactly
as the project file holds them (see the module docstring). .mat
is added to path when it does not end in it. Raises ValueError,
before anything is written, when an array is larger than the 4 GB
a version 5 file allows per variable.
Source code in src/visualdynamics/io/matlab.py
load
¶
Read a .mat written here, or a single struct built to the
documented layout of one object.
Returns the object the file holds, or a Project for a whole
project. Values written in a display system are converted
back to SI. Raises ValueError for a version 7.3 file, for a schema
stamp newer than this version reads, and for a file that has no
visualdynamics_schema stamp and is not a single struct in the
documented layout.