visualdynamics.core.runs¶
runs
¶
Runs: which run of a test a time history is, and what its record shows.
A test is a dozen runs on three axes: a low level and a step-up, the
full-level run, a signature before and after, and the same again on
the next axis. A time history is one of those runs, and a report of the
test has to say which. A record does not say it by itself, so a time
history carries a run identity (RunIdentity): the test axis, the run
number, the level against full in decibels, the date and time the run
started, and a free comment.
Each field comes from somewhere, and says where (RunIdentity.sources):
- the name of the file the record was imported from, and a Rattlesnake
run's environment names (
read_name):Z_run07_-6dB_2026-10-11T111105is axis Z, run 7, -6 dB, started 11:11:05 on 11 October 2026; - what a person entered (
entered), which stands over anything read; - the level the comparison with a specification already detects
(
visualdynamics.report.runsreads it throughcompliance.read_scale_db), when nothing else gives the level.
What cannot be read one way is refused, never guessed: a name with two
axes in it, a level written without a sign ('6dB' is 6 dB below full
or above it), two readings of the level that disagree. A refused field
is left unread, with the reason (RunIdentity.refused), until a
person enters it.
What a record shows about its run is read from the running RMS of its
control channels (read_levels): how long the run was at level, and
the interruptions in it, a stretch mid-run where the running RMS fell
well below the run's level (to zero, when the controller stopped). An
interruption is listed for the author to explain; nothing here says
why it happened. order puts runs in the order they were run, or
refuses when the identities cannot say it.
Every number here is read off the record as it stands. The outcome of a run and the explanation of an interruption are the author's, or a run log's.
Classes:
| Name | Description |
|---|---|
RunIdentity |
Which run of a test a time history is. |
Interruption |
A stretch mid-run where the running RMS fell below the run's level. |
RecordReading |
What a record shows about its run ( |
Functions:
| Name | Description |
|---|---|
level_text |
'0 dB' for full level, otherwise the signed level: '-6 dB', |
parse_start |
|
read_name |
The run identity a file's name, or several names, give. |
entered |
Entered fields, checked and put in the form a run identity holds. |
order |
The records in the order their runs were run. |
check_settings |
Refuse, with |
running_mean_square |
The mean square of |
read_levels |
How long a continuous run was at level, and where it was interrupted. |
Classes¶
RunIdentity
dataclass
¶
RunIdentity(axis: str = '', run: str = '', level_db: float | None = None, start: str = '', comment: str = '', sources: tuple[tuple[str, str], ...] = (), refused: tuple[tuple[str, str], ...] = ())
Which run of a test a time history is.
axis is the test axis ('X', 'Y', 'Z', or a lab's own name for
it), run the run's identifier ('7'), level_db the level against
full in decibels (0 for full level, -6 for a quarter of the power),
start the date, or the date and time, the run started, in ISO
8601 ('2026-10-11T11:11:05'), and comment a free note. An empty
field (None for the level) is not known.
sources says where each known field came from: a file name, an
environment name, an entry, or a comparison. refused names each
field that was read two ways, or read in a way that does not settle
it, with the reason; such a field stays unknown until it is entered.
Methods:
| Name | Description |
|---|---|
value |
The value of field |
source |
Where field |
reason |
Why field |
label |
'run 7, Z axis, -6 dB, 2026-10-11 11:11:05', with the parts |
when |
The start as a date and time (midnight for a date alone), or |
to_json |
The known fields, where each came from, and what was refused |
from_json |
The identity |
with_values |
A copy with |
Attributes:
| Name | Type | Description |
|---|---|---|
known |
bool
|
Whether anything identifies the run: an axis, a run, a level |
Attributes¶
known
property
¶
Whether anything identifies the run: an axis, a run, a level or a start (a comment alone does not).
Methods:¶
value
¶
source
¶
reason
¶
label
¶
'run 7, Z axis, -6 dB, 2026-10-11 11:11:05', with the parts that are not known left out; '' when none is known.
Source code in src/visualdynamics/core/runs.py
when
¶
The start as a date and time (midnight for a date alone), or None when it is not known.
to_json
¶
The known fields, where each came from, and what was refused and why, for the project file. Empty when nothing is known or refused.
Source code in src/visualdynamics/core/runs.py
from_json
classmethod
¶
from_json(data: Mapping[str, Any], record: str = '') -> RunIdentity
The identity to_json wrote. Raises ValueError or
TypeError, naming record, when a stored field is not one a run
identity can hold.
Source code in src/visualdynamics/core/runs.py
with_values
¶
with_values(values: Mapping[str, Any], where: str) -> RunIdentity
A copy with values set, each from where. A value of None
(or '' for a text field) clears the field and its source. A
field set here is no longer refused.
Source code in src/visualdynamics/core/runs.py
Interruption
dataclass
¶
A stretch mid-run where the running RMS fell below the run's level.
start_s is where it began on the record's time axis, seconds
how long it lasted, both measured where the running mean square
crossed half the run's level, and depth_db how far below the
level it reached at its lowest, or None when it reached zero.
RecordReading
dataclass
¶
RecordReading(start_s: float, seconds: float, at_level_s: float | None = None, interruptions: tuple[Interruption, ...] = (), channels: tuple[str, ...] = (), left_out: tuple[str, ...] = (), not_read: str = '', settings: Mapping[str, float] = dict())
What a record shows about its run (read_levels).
start_s and seconds are where the record starts on its own time
axis and how long it is. at_level_s is how long the running RMS
was within 3 dB of the run's level, None when nothing was read.
interruptions are the interruptions found. channels are the
channels the level was read on, and left_out each channel left
out with why. not_read says why nothing was read, '' when the
record was read.
Functions:¶
level_text
¶
'0 dB' for full level, otherwise the signed level: '-6 dB', '+3 dB', '-4.5 dB'.
parse_start
¶
text as a date and time, a date alone read as its midnight.
ISO 8601, as datetime.fromisoformat reads it ('2026-10-11',
'2026-10-11T11:11', '2026-10-11 11:11:05'). A time with a zone is
turned to UTC and the zone dropped, so starts compare. Raises
ValueError for anything else.
Source code in src/visualdynamics/core/runs.py
read_name
¶
The run identity a file's name, or several names, give.
Each name is read for an axis (a lone X, Y or Z, or 'Xaxis', 'axis-Z'), a run ('run7', 'run_07', 'R7'), a level ('-6dB', 'm6dB', 'minus6dB', '+3dB', '0dB', 'full', 'FL'), and a start (a date as '2026-10-11' or '20261011', with a time as 'T111105', '_1111' or '-11-11-05' after it). A directory and an extension are left off.
A field read the same way twice is read once. Refused, with the reason, and so not read: a field read two different ways, in one name or across the names, and a level written without a sign other than 0 dB ('6dB' could be below full or above it).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*names
|
str
|
The names to read, such as a file's name and a Rattlesnake run's environment names. |
()
|
Returns:
| Type | Description |
|---|---|
tuple of (dict, dict)
|
The fields read, by name ('axis', 'run', 'level_db', 'start'), and the fields refused, each with its reason. |
Source code in src/visualdynamics/core/runs.py
entered
¶
Entered fields, checked and put in the form a run identity holds.
An empty string (or None) clears a field, which then reads as None
in the result. axis, run and comment are text, and a run may
be given as a whole number; axis and run are one line, and an
axis named by one letter is upper-cased. level_db is a finite
number of decibels. start is ISO 8601 text, a datetime or a
date, and is kept as ISO text.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
record
|
str
|
The time history the fields are for, named in a refusal. |
required |
**fields
|
Any
|
The fields, by name: 'axis', 'run', 'level_db', 'start', 'comment'. |
{}
|
Returns:
| Type | Description |
|---|---|
dict
|
The fields given, each checked, None where cleared. |
Raises:
| Type | Description |
|---|---|
ValueError
|
For a field that is not a run identity's, a level that is not a
finite number, a start that is not a date, an axis or run on
more than one line; the message names |
TypeError
|
For a value of the wrong type. |
Source code in src/visualdynamics/core/runs.py
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 443 444 445 446 447 448 449 450 | |
order
¶
order(identities: Sequence[tuple[str, RunIdentity]]) -> list[str]
The records in the order their runs were run.
By start when every run has one, a tie broken by run number. When some do not, by run number, if every run has one. Otherwise the order cannot be read from the identities, and is refused; a lone run needs no order.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
identities
|
sequence of (str, RunIdentity)
|
Each record's name and its run identity. |
required |
Returns:
| Type | Description |
|---|---|
list of str
|
The record names, first run first. |
Raises:
| Type | Description |
|---|---|
ValueError
|
Naming the records that have neither a start nor a run number. |
Source code in src/visualdynamics/core/runs.py
check_settings
¶
Refuse, with ValueError, settings read_levels cannot read with:
anything not a finite number, a window that is not positive, a drop
not beyond the 3 dB that bounds the time at level, and a negative
least duration.
Source code in src/visualdynamics/core/runs.py
running_mean_square
¶
The mean square of values (channels by samples, each channel's
own mean taken off first), averaged over the channels and over
width samples at every sample: entry k covers samples k to
k + width - 1. Length samples - width + 1.
Source code in src/visualdynamics/core/runs.py
read_levels
¶
read_levels(values: ndarray, rate: float, labels: Sequence[str] | None = None, start_s: float = 0.0, window_s: float = WINDOW_S, drop_db: float = DROP_DB, min_s: float = MIN_S) -> RecordReading
How long a continuous run was at level, and where it was interrupted.
The control channels' running mean square, each channel's mean
taken off and the channels averaged, is taken over window_s at
every sample. The run's level is the median of the running mean
square over the stretches within 3 dB of its loudest, so a ramp-up,
the lower steps of a step-up, a lead-in and a tail do not pull it
down. The run is at level where the running mean square is at least
half the level (within 3 dB). Where the record starts or stops
sharply, from or to silence, that half-way point falls on the edge
itself, whatever the window, so the time at level is read to a
sample; an edge between two levels that are not silence is placed
within half a window of where it is.
An interruption is a stretch out of level between the first and the
last instant at level, at least min_s long, that reaches at least
drop_db below the level. The lead-in before the run reaches level
and the ramp-down after are not interruptions. A shallower dip, or
a shorter one, counts against the time at level and is not listed.
The window sets what is seen: a dip shorter than about half the window is smoothed away, and a record dominated by a tone at a few hertz ripples the running RMS by about 1/(4 pi f window) of its mean square. Broadband random at the default window scatters by a few percent.
A channel holding a NaN sample, and a channel that is constant
(all zero included), is left out with the reason. When none is
left, or the record is shorter than the window, nothing is read
and not_read says why.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
values
|
ndarray
|
The channels the level is read on, channels by samples. |
required |
rate
|
float
|
Samples per second. |
required |
labels
|
sequence of str
|
Each channel's name, for |
None
|
start_s
|
float
|
The time of the first sample on the record's own axis. |
0.0
|
window_s
|
float
|
The running-RMS window in seconds, how far below the level an interruption reaches, and how long it lasts at least. |
WINDOW_S
|
drop_db
|
float
|
The running-RMS window in seconds, how far below the level an interruption reaches, and how long it lasts at least. |
WINDOW_S
|
min_s
|
float
|
The running-RMS window in seconds, how far below the level an interruption reaches, and how long it lasts at least. |
WINDOW_S
|
Returns:
| Type | Description |
|---|---|
RecordReading
|
The time at level, the interruptions, and what was read on. |
Raises:
| Type | Description |
|---|---|
ValueError
|
For a window that is not positive, a drop not beyond 3 dB, a
negative |
Source code in src/visualdynamics/core/runs.py
586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 | |