Skip to content
This documentation describes version 4.0.0, which is not released yet. The current version on PyPI is 3.3.0 and does not carry everything described here.

noise_control.silencer_in_situ

What a silencer does where it was installed (ISO 11820:1996).

phonometry.noise_control.silencer_measurement is the laboratory measurement a catalogue figure comes from: a qualified rig, a substitution duct, a stated uncertainty. This is the other one. The silencer is in the plant, the plant is running, and the question is what the thing is doing there.

Clause 1.1 puts the relationship between the two in one sentence: results obtained here cannot be compared with performance data obtained from laboratory measurements to ISO 7235. The reason is in the Introduction: what is measured in situ carries the flanking transmission, the regenerated flow noise and the operating conditions with it, and the standard treats all three as properties of the silencer in that installation rather than as errors to be removed. A number from this module and a number from a data sheet are two different quantities, and this library keeps them in two different modules so that they cannot be added by accident.

Transmission loss compares the sound power reaching the silencer with the sound power leaving it, Equation (4). Insertion loss compares the plant without the silencer with the plant with it, Equation (8), and it is the only choice for a blowdown silencer, which does not exist as a duct element to measure through.

Neither is a bare subtraction of levels. Each starts as a sound pressure level difference, Equation (1) or (3), and becomes a loss by adding the area ratio of the two measurement surfaces and the difference of the two field corrections:

Which areas those are is not a matter of taste. Figure 1 enumerates twenty installations, sixteen for transmission and four for insertion, by what stands on each side of the silencer, and clause 9.1.3 gives each of them its own rule for and : a measurement surface in the duct, a quarter or a half of the silencer intake, a quarter of the room absorption, or a surface enveloping the open end. installation_case is that figure and those rules as data, so that a measurement says which case it is and the areas follow.

Clause 4 corrects for background noise from a printed table, not from the usual logarithmic subtraction, and the two do not agree: at a margin of 5 dB the table takes off 2 dB where the formula takes off 1,7, and at 8 dB it takes off 1 where the formula takes off 0,7. silencer_background_correction_db implements the table as printed, and says so.

Where the extraneous sound can be measured on its own, 9.1.1 and 9.1.2 offer the energy route of Equations (17) and (18) instead, and cap it: the maximum correction is 3 dB. A measurement that needs more than that does not yield the quantity at all, and the clause says what may be stated instead, which is an inequality. extraneous_corrected_mean_level_db returns whether the cap was reached so that a capped value cannot be reported as a determination.

Clause 9.1.5 is the one prohibition worth reading twice: converting one-third-octave data to octave data is permissible for measured sound pressure levels only, but not for level differences. octave_levels_from_third_octave_db does the permitted conversion, and the docstring names the function in this library that does the forbidden one for a different standard.

Auto-generated from the source docstrings by scripts/generate_api_docs.py (make api-docs). Do not edit by hand.

Constant (tuple).

DOWNSTREAM_DISTANCE_COEFFICIENTS = (12.0, 10.0)
extraneous_corrected_mean_level_db(
levels_db: ArrayLike,
extraneous_levels_db: ArrayLike,
) -> tuple[float, bool]

The mean level with the extraneous sound taken off, Equations (17) and (18).

The energy route 9.1.1 and 9.1.2 offer instead of Table 1, for the case where the sources the silencer works on can be switched off and the extraneous sound measured at the same positions: corrections are made “using table 1 or the relationship” (9.1.1, printed folio 10, PDF page 18 of BS EN ISO 11820:1997).

The clause caps it: the maximum correction is 3 dB. Past that the quantity is not determined, and what may be stated instead is the inequality of clause 4. Clause 4.1 (folio 4, PDF page 12) ties that to a correction of 3 dB that “is not sufficient”, and Table 1 (folio 5, PDF page 13) prints where that starts: a margin under 3 dB is invalid, and a margin of 3 dB takes off 3 dB. The subtraction reaches 3,0206 dB at that same margin, which is the printed 3 dB unrounded, so the cap is judged on the margin of the two energy means:

That is the same condition as a correction of at most dB, and it keeps the two routes of the standard in agreement at their shared boundary. A margin that floating point leaves a few parts in under 3 dB counts as 3 dB.

The second return value says whether the cap was reached, so that a capped number cannot be written down as a determination, and a capped measurement also emits a SilencerInSituWarning. The corrected level itself is returned either way.

Parameters

NameDescription
levels_dbThe levels with everything running, in decibels.
extraneous_levels_dbThe extraneous levels at the same positions, in decibels.

Returns: The corrected mean level in decibels, and whether the correction reached the cap, which is a margin under 3 dB.

Raises

ExceptionWhen
ValueErrorFor inputs that do not match point for point, or an extraneous level at or above the level it is subtracted from.
flow_velocity_m_s(
velocity_pressure_pa: ArrayLike,
density_kg_m3: float,
) -> NDArray[np.float64]

The flow velocity a velocity pressure stands for, Equation (28).

.

Parameters

NameDescription
velocity_pressure_pa, in pascals.
density_kg_m3, in kilograms per cubic metre.

Returns: , in metres per second.

Raises

ExceptionWhen
ValueErrorFor a non-positive density or a negative velocity pressure.
gas_density_kg_m3(
*,
temperature_c: float,
molar_mass_kg_kmol: float | None = None,
ambient_pressure_pa: float = 100000.0,
) -> float

The density of the gas, Equation (29).

with the universal gas constant, printed as 8 314,4 N m per kmol K, and the molar mass. The standard adds that N m per kg K for air, and that is what is used when no molar mass is given.

phonometry.noise_control.normal_air_density computes the same quantity for ISO 7235, from a gauge pressure and with that standard’s own two constants; this one takes the absolute ambient pressure the way Equation (29) prints it and admits any gas through its molar mass.

Parameters

NameDescription
temperature_c, in degrees Celsius.
molar_mass_kg_kmol, in kilograms per kilomole. Omit it for air.
ambient_pressure_pa, in pascals.

Returns: , in kilograms per cubic metre.

Raises

ExceptionWhen
ValueErrorFor a temperature that is not finite or at or below the absolute zero the equation uses, or a non-positive pressure or molar mass.
in_situ_insertion_loss(
levels_without_db: ArrayLike,
levels_with_db: ArrayLike,
*,
area_without_m2: ArrayLike,
area_with_m2: ArrayLike,
frequencies: ArrayLike | None = None,
field_correction_difference_db: ArrayLike = 0.0,
case: int | None = None,
) -> SilencerInSituResult

The insertion loss of a silencer in place, Equation (21).

The same shape as Equation (19) with the two runs in place of the two sides. NOTE 5 of 3.4 says that in most cases the two areas are equal and the two field corrections nearly so, and then both terms fall out and the loss is the level difference; the arguments are still explicit, because “in most cases” is not “always” and cases 17 and 19 of Figure 1 are where it fails.

A blowdown silencer can only be measured this way: there is no duct to measure through.

As in Equation (19), the terms are band quantities (3.4, printed folio 3, PDF page 11). Where the receiver side is a diffuse room, case 18 of Figure 1, both areas are a quarter of the room absorption, Equations (10) and (12), and move band by band with the reverberation time of each run: pass the two arrays reverberant_surface_area_m2 returns. Each area and the field correction may be one value, applied to every band, or one value per band.

Parameters

NameDescription
levels_without_db per band, in decibels.
levels_with_db per band, in decibels.
area_without_m2, one value or one per band, in square metres.
area_with_m2, one value or one per band, in square metres.
frequenciesNominal band centres, in hertz.
field_correction_difference_db, one value or one per band, in decibels.
caseThe installation of Figure 1, 17 to 20, carried into the result.

Returns: The loss, as a SilencerInSituResult.

Raises

ExceptionWhen
ValueErrorFor spectra that do not match, an area or a field correction that is neither one value nor one per band, a non-positive area or band centre, a field correction that is not finite, or a case that is not an insertion one.
in_situ_transmission_loss(
source_levels_db: ArrayLike,
receiver_levels_db: ArrayLike,
*,
source_area_m2: ArrayLike,
receiver_area_m2: ArrayLike,
frequencies: ArrayLike | None = None,
field_correction_difference_db: ArrayLike = 0.0,
case: int | None = None,
) -> SilencerInSituResult

The transmission loss of a silencer in place, Equation (19).

The level difference of Equation (1), the ratio of the two measurement areas, and the difference of the two field corrections. installation_case says which areas the installation calls for; temperature_field_correction_db is the correction difference two temperatures make.

Every term is a band quantity (3.3, printed folio 3, PDF page 11). A measurement surface in a duct is one area for all bands, but where a side is a room with a diffuse field its area is a quarter of the absorption, , and moves with the reverberation time from band to band: pass the array reverberant_surface_area_m2 returns. Each area and the field correction may be one value, applied to every band, or one value per band.

Parameters

NameDescription
source_levels_db per band, in decibels.
receiver_levels_db per band, in decibels.
source_area_m2, one value or one per band, in square metres.
receiver_area_m2, one value or one per band, in square metres.
frequenciesNominal band centres, in hertz.
field_correction_difference_db, one value or one per band, in decibels.
caseThe installation of Figure 1, 1 to 16, carried into the result.

Returns: The loss, as a SilencerInSituResult.

Raises

ExceptionWhen
ValueErrorFor spectra that do not match, an area or a field correction that is neither one value nor one per band, a non-positive area or band centre, a field correction that is not finite, or a case that is not a transmission one.
insertion_level_difference_db(
levels_without_db: ArrayLike,
levels_with_db: ArrayLike,
) -> NDArray[np.float64]

The level difference the silencer made, Equation (3).

, the level before the silencer was installed less the level after. Here II is without and I is with, which is again the opposite of the reading order, so the arguments say which run they are.

Parameters

NameDescription
levels_without_db per band, in decibels.
levels_with_db per band, in decibels.

Returns: per band, in decibels.

Raises

ExceptionWhen
ValueErrorFor spectra that do not match band for band.
installation_case(number: int) -> InstallationCase

One installation of Figure 1, with the area rules clause 9 gives it.

The figure is a matrix: the source side may be a duct, a room with a diffuse field, a room with a non-diffuse field or an open space, and so may the receiver side, which is sixteen transmission cases. The four insertion cases are keyed by the receiver side alone, because the source side is not part of what is measured.

Parameters

NameDescription
numberThe case number Figure 1 prints, 1 to 20.

Returns: The case, as an InstallationCase.

Raises

ExceptionWhen
ValueErrorFor a number outside the figure.

Constant (mapping).

InstallationCase(
number: int,
source_side: str,
receiver_side: str,
quantity: str,
source_area_rule: str,
receiver_area_rule: str,
)

One of the twenty installations of Figure 1, with its area rules.

Attributes

NameDescription
numberThe case number Figure 1 prints, 1 to 20.
source_sideWhat stands on the source side: "duct", "diffuse_room", "non_diffuse_room", "open_space", or "any" for the four insertion cases, whose source side is not part of the case.
receiver_sideThe same for the receiver side.
quantity"transmission" for cases 1 to 16, "insertion" for 17 to 20.
source_area_ruleHow 9.1.3 or 9.1.4 says to read , the source side of a transmission case, or , the run without the silencer of an insertion one, in the clause’s own words.
receiver_area_ruleThe same for or .

Constant (float).

ISO11820_AIR_GAS_CONSTANT = 287.0

Constant (float).

ISO11820_AMBIENT_PRESSURE_PA = 100000.0

Constant (mapping).

ISO11820_BACKGROUND_CORRECTIONS_DB = {3: 3.0, 4: 2.0, 5: 2.0, 6: 1.0, 7: 1.0, 8: 1.0, 9: 0.5, 10: 0.5}

Constant (float).

ISO11820_GAS_CONSTANT = 8314.4

Constant (float).

ISO11820_MINIMUM_BACKGROUND_MARGIN_DB = 3.0

Constant (float).

ISO11820_NEGLIGIBLE_BACKGROUND_MARGIN_DB = 10.0

Constant (tuple).

ISO11820_OCTAVE_BAND_EXTENDED_RANGE_HZ = (31.5, 8000.0)

Constant (tuple).

ISO11820_OCTAVE_BAND_RANGE_HZ = (63.0, 4000.0)

Constant (float).

ISO11820_SOUND_SPEED_M_S = 340.0

ISO11820_THIRD_OCTAVE_BAND_EXTENDED_RANGE_HZ

Section titled “ISO11820_THIRD_OCTAVE_BAND_EXTENDED_RANGE_HZ”

Constant (tuple).

ISO11820_THIRD_OCTAVE_BAND_EXTENDED_RANGE_HZ = (25.0, 10000.0)

Constant (tuple).

ISO11820_THIRD_OCTAVE_BAND_RANGE_HZ = (50.0, 5000.0)

Constant (float).

MAXIMUM_EXTRANEOUS_CORRECTION_DB = 3.0
mean_sound_pressure_level_db(levels_db: ArrayLike) -> float

The mean level over the measuring points, Equation (2).

An energy mean, which is what every clause of the standard means by “mean sound pressure level”.

Parameters

NameDescription
levels_dbThe levels at the measuring points, in decibels.

Returns: , in decibels.

Raises

ExceptionWhen
ValueErrorFor an empty or non-finite set of levels.
measurement_distance_downstream_m(
downstream_area_m2: float,
free_area_m2: float,
) -> float

How far downstream the measurement surface stands, Equation (16).

, with the free cross-sectional area of the silencer, which NOTE 18 warns is not the same thing as its total intake cross-section.

The expression can return zero or less for a silencer whose free area is a large fraction of the duct it sits in. 8.3.1 has its own escape for that, which is agreement between the parties on the distances, and the case is reported rather than returned as a distance nobody can stand at.

Parameters

NameDescription
downstream_area_m2, in square metres.
free_area_m2, in square metres.

Returns: , in metres.

Raises

ExceptionWhen
ValueErrorFor a non-positive area.
measurement_distance_upstream_m(upstream_area_m2: float) -> float

How far upstream the measurement surface stands, Equation (15).

, which is one and a half equivalent diameters of the upstream measurement cross-section.

Parameters

NameDescription
upstream_area_m2, in square metres.

Returns: , in metres.

Raises

ExceptionWhen
ValueErrorFor a non-positive area.
octave_levels_from_third_octave_db(
levels_db: ArrayLike,
) -> NDArray[np.float64]

One-third-octave levels folded into octaves, 9.1.5.

The energy sum of each consecutive group of three. Clause 9.1.5 permits this conversion for measured sound pressure levels only, but not for level differences, and that sentence is the whole of the clause.

The prohibition is worth stating twice, because this library does perform the forbidden operation for a different standard: phonometry.noise_control.octave_insertion_loss folds a one-third-octave insertion loss into octaves, which is Equation (2) of ISO 11691 and is exactly what ISO 11820 forbids. The two are not interchangeable: one is a fold of a measured level, the other a fold of a difference, and ISO 11820 wants the levels folded on each side and the difference taken afterwards.

Parameters

NameDescription
levels_dbOne-third-octave levels, in decibels, a multiple of three bands in ascending order.

Returns: The octave levels, in decibels.

Raises

ExceptionWhen
ValueErrorFor a count that is not a multiple of three.
reverberant_surface_area_m2(
volume_m3: float,
reverberation_time_s: ArrayLike,
*,
speed_of_sound: float = 340.0,
) -> NDArray[np.float64]

A quarter of the room absorption, as an area, Equations (6), (10) and (12).

The three equations are the same expression written three times, for the receiver room of the transmission measurement and for the room with and without the silencer of the insertion one. It is a quarter of the Sabine equivalent absorption area, which is what turns a reverberant level into a sound power.

The standard prints m/s “at room temperature”; that value is the default here and a measurement at another temperature should say so.

Parameters

NameDescription
volume_m3The room volume, in cubic metres.
reverberation_time_sThe reverberation time per band, in seconds.
speed_of_soundThe speed of sound, in metres per second.

Returns: The area, in square metres.

Raises

ExceptionWhen
ValueErrorFor a non-positive volume, time or speed.

Constant (float).

SABINE_AREA_COEFFICIENT = 13.815510557964275
silencer_background_correction_db(
level_difference_db: ArrayLike,
) -> NDArray[np.float64]

The background correction of Table 1, in decibels to subtract.

The table is printed stepped and integer-indexed, and it is not the logarithmic subtraction that most emission standards use: at a margin of 5 dB it takes off 2 dB where the formula takes off 1,7, and at 8 dB it takes off 1 where the formula takes off 0,7. It is implemented as printed.

The table gives no row between its integers. A difference that falls between two rows is read at the lower one, which is the larger correction and therefore the lower source level; the standard does not decide this, and the choice is stated here rather than smoothed away. Above ISO11820_NEGLIGIBLE_BACKGROUND_MARGIN_DB there is no row to read at all: 10 dB is the last one the table prints, and anything over it takes nothing off, so 10,5 dB is corrected by zero rather than by the 0,5 dB of the 10 dB row.

Parameters

NameDescription
level_difference_dbThe difference between the level measured with the source running and the background level alone, in decibels.

Returns: The correction to subtract, in decibels.

Raises

ExceptionWhen
ValueErrorFor a margin under ISO11820_MINIMUM_BACKGROUND_MARGIN_DB, which Table 1 calls invalid.
silencer_flow_velocity_m_s(
upstream_mean_velocity_m_s: float,
*,
upstream_area_m2: float,
free_area_m2: float,
) -> float

The mean velocity inside the silencer, Equation (31).

, the upstream mean velocity scaled by how much the silencer narrows the passage. It is the velocity the regenerated noise of the installation answers to, which is why 9.2 asks for it rather than for the duct velocity.

Parameters

NameDescription
upstream_mean_velocity_m_s, in metres per second, the arithmetic mean of Equation (30).
upstream_area_m2, in square metres.
free_area_m2, the free cross-section, in square metres.

Returns: , in metres per second.

Raises

ExceptionWhen
ValueErrorFor a non-positive area.
SilencerInSituResult(
frequencies: NDArray[np.float64] | None,
level_difference_db: NDArray[np.float64],
area_term_db: NDArray[np.float64],
field_correction_difference_db: NDArray[np.float64],
loss_db: NDArray[np.float64],
quantity: str,
case: InstallationCase | None,
)

A silencer measured where it stands, ISO 11820 Equation (19) or (21).

Attributes

NameDescription
frequenciesNominal band centres, in hertz, or None.
level_difference_db or , the sound pressure level difference the loss is built on, per band.
area_term_db or per band, in decibels. Always one value per band, even where both areas were given as single values, because the area of a diffuse room moves with the reverberation time from band to band.
field_correction_difference_db or per band, in decibels, on the same shape.
loss_db or per band, in decibels.
quantity"transmission" or "insertion".
caseThe installation of Figure 1 the measurement was made in, or None where the caller did not name one.
SilencerInSituResult.plot(
ax: Axes | None = None,
*,
language: str = 'en',
**kwargs: Any,
) -> Axes

Draw the level difference and the loss it becomes.

Requires matplotlib (pip install phonometry[plot]).

Parameters

NameDescription
axExisting axes, or None to create a figure.
languageLabel language, "en" (default) or "es".
kwargsForwarded to phonometry._plot.noise_control.plot_silencer_in_situ.

Returns: The Axes.

property

The symbol clause 11 reports this as, "D_ts" or "D_is".

The measurement is outside a condition ISO 11820 states.

sound_power_level_db(
mean_level_db: ArrayLike,
*,
area_m2: ArrayLike,
field_correction_db: ArrayLike = 0.0,
) -> NDArray[np.float64]

A mean level read as a sound power, Equations (5), (7), (9) and (11).

The four equations differ only in which side of the silencer and which run they belong to. is whichever area the case calls for, from installation_case, and the field correction of Annex A, which NOTE 4 expects to stay under TYPICAL_FIELD_CORRECTION_LIMIT_DB in absolute value once the areas have been chosen as 3.3 and 3.4 define them.

Parameters

NameDescription
mean_level_db per band, in decibels.
area_m2, in square metres.
field_correction_db, in decibels.

Returns: per band, in decibels.

Raises

ExceptionWhen
ValueErrorFor a non-positive area or mismatched shapes.
static_pressure_difference_pa(
total_pressure_loss_pa: float,
*,
volume_flow_m3_s: float,
density_kg_m3: float,
upstream_area_m2: float,
downstream_area_m2: float,
) -> float

The static pressure difference behind a change of area, Equation (14).

For a silencer whose inlet and outlet areas differ, where the gas temperature does not vary markedly. With equal areas the bracket vanishes and the two pressure differences are the same number, which is what 3.5 says in words.

Parameters

NameDescription
total_pressure_loss_pa, in pascals.
volume_flow_m3_s, in cubic metres per second.
density_kg_m3, in kilograms per cubic metre.
upstream_area_m2, in square metres.
downstream_area_m2, in square metres.

Returns: , in pascals.

Raises

ExceptionWhen
ValueErrorFor a value that is not finite, or a non-positive density or area.
temperature_field_correction_db(
*,
receiver_temperature_c: float,
source_temperature_c: float,
) -> float

The field correction difference two temperatures make, Equations (20) and (22).

Unless Annex A gives a reason to say otherwise, the field corrections account for markedly different temperatures on the two sides and for nothing else. The standard explains the term by the speed of sound alone, and that is where the ratio comes out upside down: the factor from squared pressure to power is the characteristic impedance, and at one ambient pressure the standard’s own Equation (29) makes the density fall as while rises as , so falls as and the correction rises with temperature. The printed form is returned unchanged, because a reader holding ISO 11820 has to find the standard’s own number; the defect is registered in docs/ERRATA.md under “ISO 11820:1996, Equations (20) and (22)”.

The same expression is Equation (22) with the two runs of an insertion measurement in place of the two sides: there is the temperature with the silencer and without, and the correction it returns is . Pass the with-silencer temperature as the receiver one and the without-silencer temperature as the source one, which is the ordering the two equations share.

The standard writes 273 rather than 273,15, and that is what is used.

Parameters

NameDescription
receiver_temperature_c on the receiver side, or with the silencer, in degrees Celsius.
source_temperature_c on the source side, or without the silencer, in degrees Celsius.

Returns: or , in decibels.

Raises

ExceptionWhen
ValueErrorFor a temperature that is not finite or at or below the absolute zero the equation uses.
total_pressure_loss_pa(
upstream_total_pressure_pa: float,
downstream_total_pressure_pa: float,
) -> float

The total pressure loss of the silencer, Equation (13).

, the mean total pressure upstream less the mean total pressure downstream, each of them the arithmetic mean of Equations (23) and (25). Where the inlet and outlet areas are equal and neither temperature nor density changes much, this is also the static pressure difference.

Parameters

NameDescription
upstream_total_pressure_pa, in pascals, as a difference from the ambient pressure.
downstream_total_pressure_pa, in pascals, on the same basis.

Returns: , in pascals.

transmission_level_difference_db(
source_levels_db: ArrayLike,
receiver_levels_db: ArrayLike,
) -> NDArray[np.float64]

The level difference across the silencer, Equation (1).

, the mean level on the source side less the mean level on the receiver side. The arguments are named for the side rather than for the subscript, because 1 is the receiver and 2 the source, which is the opposite of the order most readers expect.

NOTE 2 of 3.1: this is not a result of its own but the step Equation (19) turns into a transmission loss.

Parameters

NameDescription
source_levels_db per band, in decibels.
receiver_levels_db per band, in decibels.

Returns: per band, in decibels.

Raises

ExceptionWhen
ValueErrorFor spectra that do not match band for band.

Constant (float).

TYPICAL_FIELD_CORRECTION_LIMIT_DB = 3.0

Constant (float).

UPSTREAM_DISTANCE_DIAMETERS = 1.5
velocity_pressure_pa(
total_pressure_pa: ArrayLike,
static_pressure_pa: ArrayLike,
) -> NDArray[np.float64]

The velocity pressure, Equation (27).

, the total pressure less the static pressure, both reported as differences from the ambient atmospheric pressure as 8.3.2 asks.

Parameters

NameDescription
total_pressure_pa, in pascals.
static_pressure_pa, in pascals.

Returns: , in pascals.

Raises

ExceptionWhen
ValueErrorFor inputs that do not match.

Constant (float).

VELOCITY_UNIFORMITY_TOLERANCE_PERCENT = 10.0