visualdynamics.core.field_limit¶
field_limit
¶
Field data to specification statistics: a set of measured spectra turned into the limit a specification is written from, and the straight-line envelope written over that limit.
A random or shock specification derived from measurements starts as a set of spectra: one PSD or SRS per flight, per run, per event or per segment of a mission. Every one of them differs from the next, so the specification is written over a statistical limit of the set, a level at each frequency that the set's next member is not expected to exceed, and the limit is then covered by a few straight lines on log-log axes, which is the breakpoint table a laboratory receives. This module makes both steps reproducible, and says on each result what it rests on.
The set. gather reads the records onto common frequency lines:
the records of one PSD or SRS, or records from several, as long as
they can be read one way. Records of different quantities, or with
units declared on one side and not the other, are refused
(compliance.units_check), as are a PSD mixed with an SRS, two SRS at
different Q or of different kinds, a cross spectrum, and frequency
ranges that do not overlap. A NaN, or a line at zero or below, inside
the band read is refused with its record and frequency named, since a
level of zero has no logarithm and a dead channel is not a quiet
flight. Records on different lines are read onto the first record's
lines over the range they share, log-log between their own lines, and
the result says so.
The limits. The levels are taken in decibels, 10 log10 of a density for a PSD and 20 log10 of a peak for an SRS, so a level spread by a factor reads as a spread in decibels whatever its size. At each line, over the n records,
envelope: the largest of the records (the maximum envelope). It assumes nothing about how the levels are distributed. Whatever that distribution is, the largest of n independent records stands above a sharecoverageof the population with probability 1 - coverage**n, which the result reports beside it.distribution_free: the same largest level, taken as a limit at a statedcoverageandconfidence, which it can be only when 1 - coverage**n reaches the confidence. A set smaller than ln(1 - confidence) / ln(coverage) records is refused, with the count it needs.mean_k: the mean of the levels in decibels pluskstandard deviations of them, the standard deviation estimated from the records with n - 1 in its denominator.-
tolerance: the normal tolerance limit, the mean plus a factor times the standard deviation, the factor chosen so that the limit stands above a sharecoverageof the population with probabilityconfidence, on the assumption that the levels in decibels are normally distributed across the records (that the levels are log-normal). With the standard deviation estimated from n records the factor isk = t'(confidence; n - 1, z sqrt(n)) / sqrt(n)
where z is the standard normal quantile at coverage and t' the
quantile of the noncentral t distribution with n - 1 degrees of
freedom and noncentrality z sqrt(n). That is exact: the sample mean
is normal and the sample variance chi-square, and the event that the
limit covers the population's quantile is the event that a
noncentral t variable stays below k sqrt(n). Ten records at 95
percent coverage and 95 percent confidence give k = 2.911, three give
7.656, and at 50 percent confidence the factor falls toward z =
1.645 as the set grows. Fewer than two records leave no standard
deviation to estimate and are refused, unless one is stated.
A standard deviation can instead be stated, sigma_db, when the set
is too small to estimate one. It is then taken as known, and the
tolerance factor is z + z_c / sqrt(n), with z_c the normal quantile
at confidence: one record with 3 dB stated gives a limit 1.645 x 3 =
4.9 dB above it at 95 percent coverage and 50 percent confidence, and
(2.326 + 1.282) x 3 = 10.8 dB at 99 percent and 90 percent. The result
says the scatter was stated and not measured.
The envelope written over the limit. fit_envelope covers the
limit with straight lines on log-log axes, by stated rules. The lines
are at or above the limit at every line. The breakpoints are at lines
of the limit and their levels are free: for a given set of breakpoint
frequencies the levels are those of the cover that stands least above
the limit on average, in decibels over log frequency, which is a
linear program. The fit starts with one line across the band. While
the lines stand more than max_overshoot_db above the limit at some
line, a breakpoint is added in the span with the most area between its
line and the limit, at the line that span stands highest above, and
the levels are fitted again. It stops there, or at max_breakpoints,
in which case the allowance is not met and the result says by how much
and where. Once the allowance is met, any breakpoint whose removal
still meets it is removed. The area the envelope adds over the limit
(a PSD's mean square, read log-log between lines for both) is reported
in decibels; two straight runs that agree at neighboring lines agree
between them, so a cover at every line is a cover everywhere. A
narrowband limit is ragged from line to line, and lines that stay
within a few decibels of every valley need a breakpoint at almost
every other line; the limit of spectra banded first (Psd.to_octave)
is the one a few lines can follow.
A cover always raises the rms. Where the rms is to be kept instead
(rms='conserve', a PSD only, since an SRS has no area), the covering
lines are lowered by the decibels they added, so the envelope's area
equals the limit's; the lines where it then falls below the limit are
counted and the deepest named.
The margin a document asks for is added when the specification is
written (EnvelopeFit.specification), in decibels on the same
convention, as its own step and shown as such.
References
The normal distribution, the sampling distributions of a sample's mean and variance and the Student t are standard results of statistics; the tolerance factor follows from them, as above, and the limits are those a test document names for summarizing a set of measured spectra.
- Bendat, J. S., & Piersol, A. G. (2010). Random Data: Analysis and Measurement Procedures, 4th ed. Wiley. The normal distribution, and the sampling distributions of a sample mean and variance (normal and chi-square) and of the Student t, on which the tolerance factor rests.
- MIL-STD-810H with Change 1 (2022), Environmental Engineering Considerations and Laboratory Tests, Method 514.8 (Vibration), Annex F, Appendix B. The upper limits a set of measured spectra is summarized by: the normal tolerance limit, the envelope, the distribution-free tolerance limit and the empirical tolerance limit.
Classes:
| Name | Description |
|---|---|
FieldSet |
Records read onto common frequency lines, ready to summarize. |
FieldLimit |
A statistical limit over a set of spectra, and what it rests on. |
EnvelopeFit |
A limit covered by straight lines on log-log axes. |
Functions:
| Name | Description |
|---|---|
gather |
Read a set of PSDs or SRSs onto common lines, refusing what |
tolerance_factor |
The one-sided normal tolerance factor: how many standard |
records_needed |
The fewest records whose largest stands above |
field_limit |
The statistical limit over a set, at every line. |
cover |
Straight lines over a curve: at or above it at every point, and, |
fit_envelope |
Cover a limit with straight lines on log-log axes, by the rules |
derive |
The whole chain from a set of spectra: |
settings_of |
The arguments |
Classes¶
FieldSet
dataclass
¶
FieldSet(frequencies: ndarray, levels: ndarray, labels: list[str], dofs: list[str], kind: str, dim: str, q: float | None = None, srs_kind: str | None = None, notes: list[str] = list())
Records read onto common frequency lines, ready to summarize.
Attributes:
frequencies: The lines, in Hz, increasing and positive.
levels: One row per record, the level at each line in SI (a
density for a PSD, a peak for an SRS), every value finite
and positive.
labels: How each record is named in a message: its object, its
record number and its DOF.
dofs: The DOF each record was measured at.
kind: 'psd' or 'srs'.
dim: The records' dimension, as ordinate_dim spells it.
q: The SRS's amplification, or None for a PSD.
srs_kind: The SRS's kind ('maximax', 'positive', 'negative'), or
None for a PSD.
notes: What was done to read the records one way: lines read
from another grid, a 0 Hz line left out, raw units.
Attributes:
| Name | Type | Description |
|---|---|---|
count |
int
|
How many records the set holds. |
db_scale |
float
|
10 for a PSD and 20 for an SRS: the decibels of a factor of |
levels_db |
ndarray
|
The levels in decibels re one SI unit, on the record's own |
FieldLimit
dataclass
¶
FieldLimit(frequencies: ndarray, levels: ndarray, statistic: str, fieldset: FieldSet, coverage: float, confidence: float | None, factor: float | None, sigma_db: float | None, mean_db: ndarray, scatter_db: ndarray | None, notes: list[str] = list())
A statistical limit over a set of spectra, and what it rests on.
Attributes:
frequencies: The lines, in Hz.
levels: The limit at each line, in SI.
statistic: Which limit (STATISTICS).
fieldset: The set it was taken over.
coverage: The share of the population the limit is to stand
above (for envelope, the share its distribution-free
confidence is read at).
confidence: The probability it does, for distribution_free
and tolerance; for envelope, 1 - coverage**n, read off
the set; None for mean_k.
factor: The standard deviations above the mean, for mean_k
and tolerance; None otherwise.
sigma_db: The standard deviation in dB when it was stated
rather than estimated; None otherwise.
mean_db: The mean of the levels in dB at each line.
scatter_db: The standard deviation of the levels in dB at each
line, estimated from the records (n - 1 in its
denominator); None for a single record.
notes: The set's notes, and any the limit adds.
Methods:
| Name | Description |
|---|---|
describe |
One line: the limit, its count and its factor. |
Attributes:
| Name | Type | Description |
|---|---|---|
count |
int
|
How many records the limit was taken over. |
kind |
str
|
'psd' or 'srs'. |
levels_db |
ndarray
|
The limit in decibels re one SI unit. |
rests_on |
str
|
In words: how many records the limit was taken over, and the |
Attributes¶
rests_on
property
¶
In words: how many records the limit was taken over, and the assumption it needs.
Methods:¶
describe
¶
One line: the limit, its count and its factor.
Source code in src/visualdynamics/core/field_limit.py
EnvelopeFit
dataclass
¶
EnvelopeFit(frequencies: ndarray, levels: ndarray, limit: FieldLimit, max_overshoot_db: float, overshoot_db: float, rule: str, limit_area: float, envelope_area: float, added_db: float, below: int, deepest_db: float, max_breakpoints: int | None = MAX_BREAKPOINTS, met: bool = True, notes: list[str] = list())
A limit covered by straight lines on log-log axes.
Attributes:
frequencies: The breakpoints, in Hz.
levels: The envelope's level at each breakpoint, in SI: at or
above the limit, and lowered by added_db under 'conserve'.
limit: The limit covered.
max_overshoot_db: The allowance it was fitted with.
overshoot_db: The most the envelope stands above the limit at
any line, in dB.
rule: 'exceed' or 'conserve' (RMS_RULES).
max_breakpoints: The most breakpoints it was allowed, or None.
met: Whether the envelope is within the allowance at every
line; False when the breakpoints allowed ran out first.
limit_area: The area under the limit over its lines, read
log-log between them (a PSD's mean square in SI); NaN for an
SRS.
envelope_area: The same for the covering lines, before any
lowering; NaN for an SRS.
added_db: The area the cover adds over the limit, in dB
(10 log10 of the ratio); NaN for an SRS.
below: Under 'conserve', how many of the limit's lines the
lowered envelope falls below; 0 under 'exceed'.
deepest_db: Under 'conserve', the most it falls below the limit
at any line, in dB (a positive number); 0 otherwise.
notes: The limit's notes, and any the fit adds.
Methods:
| Name | Description |
|---|---|
at |
The envelope's level at any frequencies inside its band, |
describe |
One line: the breakpoints, the overshoot and the area added. |
dof |
The DOF a specification from this envelope is written at: |
margined |
The breakpoint levels with |
specification |
The specification this envelope writes, with |
Attributes:
| Name | Type | Description |
|---|---|---|
breakpoints |
int
|
How many breakpoints the envelope has. |
limit_rms |
float
|
The rms under the limit, the square root of its area; NaN for |
envelope_rms |
float
|
The rms under the envelope as it stands (lowered under |
Attributes¶
limit_rms
property
¶
The rms under the limit, the square root of its area; NaN for an SRS.
envelope_rms
property
¶
The rms under the envelope as it stands (lowered under 'conserve'); NaN for an SRS.
Methods:¶
at
¶
The envelope's level at any frequencies inside its band, straight between breakpoints on log-log axes; NaN outside.
Source code in src/visualdynamics/core/field_limit.py
describe
¶
One line: the breakpoints, the overshoot and the area added.
Source code in src/visualdynamics/core/field_limit.py
dof
¶
The DOF a specification from this envelope is written at:
dof when given, or the set's one DOF.
Raises:
| Type | Description |
|---|---|
ValueError
|
When the set spans several DOFs and none is named. |
Source code in src/visualdynamics/core/field_limit.py
margined
¶
The breakpoint levels with margin_db added, on the set's
decibel convention (10 log10 for a PSD, 20 log10 for an SRS).
Source code in src/visualdynamics/core/field_limit.py
specification
¶
specification(margin_db: float = 0.0, dof: str | None = None, comment: str = '') -> Specification | ShockSpecification
The specification this envelope writes, with margin_db
added: a Specification at the breakpoints for a PSD, through
core.author.SpecificationDraft (with that sheet's starter
bands), or a ShockSpecification on the SRS's own spacing,
Q and kind for an SRS, with no bands.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
margin_db
|
float
|
Decibels added to every breakpoint. |
0
|
dof
|
str
|
The DOF to write it at; the set's one DOF when omitted. |
None
|
comment
|
str
|
For the records; a line saying how it was derived when omitted. |
''
|
Returns:
| Type | Description |
|---|---|
Specification or ShockSpecification
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
For undeclared units, a set spanning several DOFs with none named, or a margin that is not finite. |
Source code in src/visualdynamics/core/field_limit.py
Functions:¶
gather
¶
gather(spectra: Any, records: Sequence[int] | None = None, *, band: tuple[float, float] | None = None) -> FieldSet
Read a set of PSDs or SRSs onto common lines, refusing what cannot be read one way.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
spectra
|
Psd, Srs, or sequence
|
One PSD or SRS (its records are the set), or a sequence of
them (every record of each), or a sequence of
|
required |
records
|
sequence of int
|
With a single object, the records that make the set; every record when omitted. |
None
|
band
|
(float, float)
|
The frequencies to read, in Hz; the range every record covers when omitted. A band reaching past that range is refused. |
None
|
Returns:
| Type | Description |
|---|---|
FieldSet
|
The records' levels on the first record's lines inside the band, with notes on what was done to read them one way. |
Raises:
| Type | Description |
|---|---|
TypeError
|
For an object that is not a plain PSD or SRS (a specification is a requirement, not a measurement). |
ValueError
|
For an empty set; a PSD mixed with an SRS; SRSs at different Q or of different kinds; a cross spectrum; records of different quantities, or with units declared on one side only; ranges with no frequency in common, or a band outside the common range; fewer than two lines in the band; or a NaN, or a level at or below zero, at a line inside it, named by record and frequency. |
Source code in src/visualdynamics/core/field_limit.py
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 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 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 | |
tolerance_factor
¶
tolerance_factor(count: int, coverage: float = COVERAGE, confidence: float = CONFIDENCE, *, sigma_known: bool = False) -> float
The one-sided normal tolerance factor: how many standard
deviations above the mean a limit stands so that it is above a
share coverage of a normal population with probability
confidence, from count samples.
With the standard deviation estimated from the samples it is the noncentral t quantile over sqrt(count) (see the module's text); with it known it is z + z_c / sqrt(count).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
count
|
int
|
The number of samples (records). |
required |
coverage
|
float
|
Each strictly between 0 and 1. |
COVERAGE
|
confidence
|
float
|
Each strictly between 0 and 1. |
COVERAGE
|
sigma_known
|
bool
|
Whether the standard deviation is known rather than estimated. |
False
|
Returns:
| Type | Description |
|---|---|
float
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
For a coverage or confidence outside (0, 1), fewer than one sample, or fewer than two with the standard deviation to be estimated. |
Source code in src/visualdynamics/core/field_limit.py
records_needed
¶
The fewest records whose largest stands above coverage of the
population with probability at least confidence, whatever the
distribution: the smallest n with 1 - coverage**n >= confidence.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
coverage
|
float
|
Each strictly between 0 and 1. |
COVERAGE
|
confidence
|
float
|
Each strictly between 0 and 1. |
COVERAGE
|
Returns:
| Type | Description |
|---|---|
int
|
|
Source code in src/visualdynamics/core/field_limit.py
field_limit
¶
field_limit(fieldset: FieldSet, statistic: str = 'tolerance', *, coverage: float = COVERAGE, confidence: float = CONFIDENCE, k: float | None = None, sigma_db: float | None = None) -> FieldLimit
The statistical limit over a set, at every line.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fieldset
|
FieldSet
|
The set ( |
required |
statistic
|
str
|
One of |
'tolerance'
|
coverage
|
float
|
The share of the population the limit stands above. |
`COVERAGE`
|
confidence
|
float
|
The probability it does, for 'distribution_free' and 'tolerance'. |
`CONFIDENCE`
|
k
|
float
|
The number of standard deviations, for 'mean_k' (where it is required) and nowhere else. |
None
|
sigma_db
|
float
|
A standard deviation in dB to take as known instead of estimating one, for 'mean_k' and 'tolerance'. It is what lets a single record give either. |
None
|
Returns:
| Type | Description |
|---|---|
FieldLimit
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
For an unknown statistic; a coverage or confidence outside
(0, 1); |
Source code in src/visualdynamics/core/field_limit.py
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 728 729 730 731 732 733 734 735 | |
cover
¶
cover(x: ArrayLike, y: ArrayLike, max_overshoot: float, max_points: int | None = None) -> tuple[list[int], ndarray]
Straight lines over a curve: at or above it at every point, and,
where max_points allows, no more than max_overshoot above it at
any.
x and y are the axes the lines are straight on (log frequency
and decibels for a spectrum). The breakpoints are at points of x
and their levels are free: for a given set of breakpoints the
levels are those of the cover that stands least above the curve on
average over x (_least_cover). It starts with one line from the
first point to the last. While some point is more than the
allowance under the lines, a breakpoint is added in the span with
the most area between its line and the curve, at the point that
span stands highest above, and the levels are fitted again; it
stops at max_points, the allowance then not met. When the
allowance is met, any breakpoint whose removal still meets it is
removed, in passes from the low end until a pass removes none.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
array - like
|
The points, |
required |
y
|
array - like
|
The points, |
required |
max_overshoot
|
float
|
The allowance, in the units of |
required |
max_points
|
int
|
The most breakpoints, two or more; no limit when omitted. |
None
|
Returns:
| Type | Description |
|---|---|
(list of int, numpy.ndarray)
|
The breakpoints as indices into |
Source code in src/visualdynamics/core/field_limit.py
788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 | |
fit_envelope
¶
fit_envelope(limit: FieldLimit, max_overshoot_db: float = OVERSHOOT_DB, rms: str = 'exceed', *, max_breakpoints: int | None = MAX_BREAKPOINTS, progress: Callable[[int, int], None] | None = None) -> EnvelopeFit
Cover a limit with straight lines on log-log axes, by the rules in the module's text.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
limit
|
FieldLimit
|
The limit ( |
required |
max_overshoot_db
|
float
|
The most the lines may stand above the limit at any line, in dB on the set's convention. |
`OVERSHOOT_DB`
|
rms
|
str
|
'exceed', the cover itself, or 'conserve', the cover lowered to the limit's area (a PSD only). |
'exceed'
|
max_breakpoints
|
int or None
|
The most breakpoints, two or more; None for no limit. Where it
stops the fit, the allowance is not met, and the result says so
( |
`MAX_BREAKPOINTS`
|
progress
|
callable
|
Called with |
None
|
Returns:
| Type | Description |
|---|---|
EnvelopeFit
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
For an allowance that is negative or not finite, fewer than two breakpoints allowed, an unknown rule, or 'conserve' asked of an SRS. |
Source code in src/visualdynamics/core/field_limit.py
1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 | |
derive
¶
derive(spectra: Any, records: Sequence[int] | None = None, *, statistic: str = 'tolerance', coverage: float = COVERAGE, confidence: float = CONFIDENCE, k: float | None = None, sigma_db: float | None = None, band: tuple[float, float] | None = None, max_overshoot_db: float = OVERSHOOT_DB, rms: str = 'exceed', max_breakpoints: int | None = MAX_BREAKPOINTS, progress: Callable[[int, int], None] | None = None) -> EnvelopeFit
The whole chain from a set of spectra: gather, field_limit
and fit_envelope, with the arguments each takes.
fit = derive(flights, statistic='tolerance', coverage=0.95,
confidence=0.5)
spec = fit.specification(margin_db=3.0)
Returns:
| Type | Description |
|---|---|
EnvelopeFit
|
The envelope; its |
Source code in src/visualdynamics/core/field_limit.py
settings_of
¶
settings_of(fit: EnvelopeFit) -> dict[str, Any]
The arguments derive was called with, as far as the result
records them: what a script needs to make it again.