<!-- canonical: https://docs.jmrp.io/phonometry/reference/api/noise_control/silencers/ -->
Source: https://docs.jmrp.io/phonometry/reference/api/noise_control/silencers/

Reactive silencers by the four-pole (transmission-matrix) method.

A reactive silencer controls noise by *reflecting* it back to the source with
impedance discontinuities -- sudden area changes and side branches -- rather
than by dissipating it in absorptive material. The one-dimensional plane-wave
theory represents each acoustic element by a 2x2 **transfer (four-pole)
matrix** relating the sound pressure `p` and volume velocity `S u` at its
two ends, and a compound silencer is the ordered matrix product of its
elements (Bies, Hansen & Howard, *Engineering Noise Control* 5th ed., §8.8-8.9;
Munjal, *Acoustics of Ducts and Mufflers*).

**Transfer matrix** (Bies Eq. (8.133)), state vector `[p, S u]` with the
characteristic acoustic impedance $Z = \rho c / S$. The plane-wave
element for
a straight duct of length `L` and area `S` is (Bies Eq. (8.143), no flow)

$$
\begin{bmatrix} \cos(kL) & j (\rho c / S) \sin(kL) \\ j (S / \rho c) \sin(kL) & \cos(kL) \end{bmatrix}, \qquad k = \omega / c,
$$

and a **side branch** of acoustic impedance `Z_b` is the shunt element
(Bies Eq. (8.144))

$$
\begin{bmatrix} 1 & 0 \\ 1 / Z_\mathrm{b} & 1 \end{bmatrix}.
$$

**Transmission loss** from the compound matrix `T` (Munjal, *Acoustics of
Ducts and Mufflers* 2nd ed., Eq. (3.27), no flow; reduces to Bies Eq. (8.148)
for equal inlet/outlet areas):

$$
\mathrm{TL} = 10 \log_{10}\!\left[\frac{Z_n}{Z_1} \cdot \frac{1}{4} \left\lvert T_{11} + \frac{T_{12}}{Z_n} + Z_1 T_{21} + \frac{Z_1}{Z_n} T_{22} \right\rvert^2\right]
$$

with $Z_1 = \rho c / S_{\mathrm{in}}$ and
$Z_n = \rho c / S_{\mathrm{out}}$. A zero-length element
between unequal areas then reproduces the classic sudden-expansion result
$\mathrm{TL} = 10 \log_{10}[(1 + m)^2 / (4 m)]$ with
$m = S_{\mathrm{out}} / S_{\mathrm{in}}$, and the TL is
the same from either side, as reciprocity of a passive two-port requires.
Bies Eq. (8.141) prints this formula with impedance ratios on `T11` and
`T22` ($Z_{A1}/Z_{An}$ and $Z_{An}/Z_{A1}$) instead of the
overall $Z_n/Z_1$
prefactor; as printed it fails the sudden-expansion limit (see
`docs/ERRATA.md`). `TL` is the intrinsic attenuation for an anechoic
termination. The **insertion loss** for a source of internal impedance
`Z_s` radiating into a termination impedance `Z_r` is the extra
attenuation of inserting the silencer in place of a direct connection,

$$
\mathrm{IL} = 20 \log_{10} \left\lvert \frac{T_{11} Z_\mathrm{r} + T_{12} + Z_\mathrm{s} Z_\mathrm{r} T_{21} + Z_\mathrm{s} T_{22}} {Z_\mathrm{s} + Z_\mathrm{r}} \right\rvert,
$$

which is `0` when the silencer reduces to a through connection
($T = I$)
and, for equal inlet/outlet areas, equals the transmission loss for the
anechoic reference $Z_\mathrm{s} = Z_\mathrm{r} = \rho c / S$ (with unequal areas the
direct
connection contains the same area jump, so its mismatch loss cancels from
the insertion loss but not from the transmission loss).

**Simple expansion chamber.** A chamber of area `S_exp` and length `L`
between pipes of area `S_duct` has the closed-form transmission loss (Bies
Eq. (8.111)) with area ratio $m = S_{\mathrm{exp}} / S_{\mathrm{duct}}$

$$
\mathrm{TL} = 10 \log_{10}\!\left[1 + \frac{1}{4} \left(m - \frac{1}{m}\right)^2 \sin^2(kL)\right],
$$

peaking at $10 \log_{10}[1 + (1/4)(m - 1/m)^2]$ when
$kL = \pi/2, 3\pi/2, \ldots$ and
dropping to `0` at $kL = n \pi$ (no dissipation). The four-pole product
reproduces this exactly, and the machinery extends to side-branch (Helmholtz,
quarter-wave) and extended-tube resonators that the closed form cannot cover.

**Layouts of your own.** Anything the four named devices do not cover is built
by cascading elements directly. [`SilencerChain`](/phonometry/reference/api/noise_control/silencers/#silencerchain) does that through the
same [`duct_matrix`](/phonometry/reference/api/noise_control/silencers/#duct_matrix), [`shunt_matrix`](/phonometry/reference/api/noise_control/silencers/#shunt_matrix) and [`cascade`](/phonometry/reference/api/noise_control/silencers/#cascade) calls while
keeping the arguments each element was given, which is what lets a hand-built
chain be drawn ([`SilencerChain.plot_geometry`](/phonometry/reference/api/noise_control/silencers/#silencerchainplot_geometry)) and not only computed. The
drawing shows the ducts to scale and marks the branch points, because that is
the whole of what the elements declare: a duct element is handed a length and
an area, a shunt element only an impedance.

**Validity.** All of this is one-dimensional: it holds while the duct and the
chamber carry plane waves only, that is below the first higher-order-mode
cut-on frequency of the widest cross section
([`phonometry.noise_control.duct_modes`](/phonometry/reference/api/noise_control/duct-modes/)). Every result reports that
frequency as [`ReactiveSilencerResult.plane_wave_limit`](/phonometry/reference/api/noise_control/duct-modes/#plane_wave_limit) and raises a
[`PlaneWaveWarning`](/phonometry/reference/api/noise_control/duct-modes/#planewavewarning) when the
analysis grid reaches past it: the numbers are still returned, but above cut-on
they describe the plane-wave mode alone and a measurement will show the rest.

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

## cascade

```python
cascade(*matrices: _Complex) -> _Complex
```

Cascade element four-pole matrices from inlet to outlet.

The compound matrix is the ordered product `T1 @ T2 @ ... @ Tn` (the
state at the inlet equals the compound matrix times the state at the
outlet), broadcast over the frequency axis.

**Parameters**

| Name | Description |
| :--- | :--- |
| `matrices` | One or more `(n_freq, 2, 2)` arrays sharing `n_freq`. |

**Returns:** The compound `(n_freq, 2, 2)` array.

**Raises**

| Exception | When |
| :--- | :--- |
| ValueError | If a matrix is not a `(n_freq, 2, 2)` array or the matrices disagree on `n_freq`. |

## duct_matrix

```python
duct_matrix(
    frequencies: ArrayLike,
    length: float,
    area: float,
    *,
    speed_of_sound: float = 343.0,
    density: float = 1.206,
) -> _Complex
```

Four-pole matrix of a straight duct (Bies Eq. (8.143), no flow).

**Parameters**

| Name | Description |
| :--- | :--- |
| `frequencies` | Frequencies `f`, Hz (1-D array). |
| `length` | Duct length `L`, m. |
| `area` | Cross-sectional area `S`, m2. |
| `speed_of_sound` | Speed of sound `c`, m/s. |
| `density` | Air density `rho`, kg/m3. |

**Returns:** A `(n_freq, 2, 2)` complex transfer-matrix array.

## expansion_chamber

```python
expansion_chamber(
    frequencies: ArrayLike,
    length: float,
    chamber_area: float,
    pipe_area: float,
    *,
    speed_of_sound: float = 343.0,
    density: float = 1.206,
    source_impedance: ArrayLike | None = None,
    radiation_impedance: ArrayLike | None = None,
) -> ReactiveSilencerResult
```

Simple expansion-chamber silencer (Bies Eq. (8.111) / four-pole).

**Parameters**

| Name | Description |
| :--- | :--- |
| `frequencies` | Frequencies `f`, Hz (1-D array). |
| `length` | Chamber length `L`, m. |
| `chamber_area` | Chamber cross-sectional area `S_exp`, m2. |
| `pipe_area` | Inlet/outlet pipe area `S_duct`, m2. |
| `speed_of_sound` | Speed of sound `c`, m/s. |
| `density` | Air density `rho`, kg/m3. |
| `source_impedance` | Optional source impedance `Z_s` for the insertion loss, Pa s/m3. |
| `radiation_impedance` | Optional radiation impedance `Z_r` for the insertion loss, Pa s/m3. |

**Returns:** A [`ReactiveSilencerResult`](/phonometry/reference/api/noise_control/silencers/#reactivesilencerresult) (its `transmission_loss` equals the closed form $10 \log_{10}[1 + (1/4)(m - 1/m)^2 \sin^2(kL)]$).

## extended_tube_chamber

```python
extended_tube_chamber(
    frequencies: ArrayLike,
    length: float,
    chamber_area: float,
    pipe_area: float,
    *,
    inlet_extension: float = 0.0,
    outlet_extension: float = 0.0,
    speed_of_sound: float = 343.0,
    density: float = 1.206,
    source_impedance: ArrayLike | None = None,
    radiation_impedance: ArrayLike | None = None,
) -> ReactiveSilencerResult
```

Extended-inlet/outlet expansion chamber (Bies §8.9.7).

The inlet and outlet pipes extend a distance into the chamber, forming
annular quarter-wave side branches (of area
$S_{\mathrm{exp}} - S_{\mathrm{duct}}$ and lengths
equal to the extensions, Bies Eq. (8.156)) at the two junctions. Tuning the
extensions (classically $L/4$ and $L/2$) places quarter-wave
peaks that
fill the $kL = n \pi$ troughs of the plain expansion chamber. With
both extensions `0` the result reduces exactly to
[`expansion_chamber`](/phonometry/reference/api/noise_control/silencers/#expansion_chamber).

The junction where each extended pipe ends is where its three ducts meet,
so the straight chamber element cascaded between the two side branches is
the length left over,
$L_c = L - L_a - L_b$ (Bies Figure 8.19(a) and Example 8.2, where
$L = L_a + L_b + L_c$), and not the full chamber length. When the two
extensions meet ($L_a + L_b = L$) the straight element vanishes and
the two annular branches shunt the same plane, which is the well-defined
limit of the cascade; extensions that would overlap are rejected.

**Parameters**

| Name | Description |
| :--- | :--- |
| `frequencies` | Frequencies `f`, Hz (1-D array). |
| `length` | Overall chamber length `L`, m, extensions included. |
| `chamber_area` | Chamber cross-sectional area `S_exp`, m2. |
| `pipe_area` | Inlet/outlet pipe area `S_duct`, m2. |
| `inlet_extension` | Inlet pipe extension into the chamber `L_a`, m. |
| `outlet_extension` | Outlet pipe extension into the chamber `L_b`, m. |
| `speed_of_sound` | Speed of sound `c`, m/s. |
| `density` | Air density `rho`, kg/m3. |
| `source_impedance` | Optional source impedance `Z_s`, Pa s/m3. |
| `radiation_impedance` | Optional radiation impedance `Z_r`, Pa s/m3. |

**Returns:** A [`ReactiveSilencerResult`](/phonometry/reference/api/noise_control/silencers/#reactivesilencerresult).

## helmholtz_impedance

```python
helmholtz_impedance(
    frequencies: ArrayLike,
    neck_area: float,
    neck_length: float,
    cavity_volume: float,
    *,
    resistance: float = 0.0,
    speed_of_sound: float = 343.0,
    density: float = 1.206,
) -> _Complex
```

Acoustic impedance of a Helmholtz side branch (Bies Eq. (8.152)).

$Z = R + j(\rho \omega l_\mathrm{e} / S_{\mathrm{neck}} - \rho c^2 / (\omega V))$ with acoustic
mass $\rho l_\mathrm{e} / S_{\mathrm{neck}}$ and compliance
$V / (\rho c^2)$; the resonance
$f_0 = (c / 2 \pi) \sqrt{S_{\mathrm{neck}} / (l_\mathrm{e} V)}$
(Bies Eq. (8.46)) is where the
reactance vanishes, leaving $Z = R$: a lossless branch
(`resistance = 0`) shorts the duct there, and a resistive one presents
its resistance instead, which is what bounds the peak attenuation.

**Parameters**

| Name | Description |
| :--- | :--- |
| `frequencies` | Frequencies `f`, Hz (1-D array). |
| `neck_area` | Neck cross-sectional area `S_neck`, m2. |
| `neck_length` | Effective neck length `l_e` (with end corrections), m. |
| `cavity_volume` | Cavity volume `V`, m3. |
| `resistance` | Acoustic resistance `R`, Pa s/m3 (default 0, lossless). |
| `speed_of_sound` | Speed of sound `c`, m/s. |
| `density` | Air density `rho`, kg/m3. |

**Returns:** The complex branch impedance per frequency, Pa s/m3.

## helmholtz_resonator

```python
helmholtz_resonator(
    frequencies: ArrayLike,
    duct_area: float,
    neck_area: float,
    neck_length: float,
    cavity_volume: float,
    *,
    resistance: float = 0.0,
    speed_of_sound: float = 343.0,
    density: float = 1.206,
    source_impedance: ArrayLike | None = None,
    radiation_impedance: ArrayLike | None = None,
) -> ReactiveSilencerResult
```

Side-branch Helmholtz resonator on a duct (Bies Eqs. (8.144), (8.152)).

**Parameters**

| Name | Description |
| :--- | :--- |
| `frequencies` | Frequencies `f`, Hz (1-D array). |
| `duct_area` | Main-duct cross-sectional area `S_d`, m2. |
| `neck_area` | Resonator neck area `S_neck`, m2. |
| `neck_length` | Effective neck length `l_e`, m. |
| `cavity_volume` | Cavity volume `V`, m3. |
| `resistance` | Neck acoustic resistance `R`, Pa s/m3 (default 0). |
| `speed_of_sound` | Speed of sound `c`, m/s. |
| `density` | Air density `rho`, kg/m3. |
| `source_impedance` | Optional source impedance `Z_s`, Pa s/m3. |
| `radiation_impedance` | Optional radiation impedance `Z_r`, Pa s/m3. |

**Returns:** A [`ReactiveSilencerResult`](/phonometry/reference/api/noise_control/silencers/#reactivesilencerresult); `resonances` holds $f_0 = (c / 2 \pi) \sqrt{S_{\mathrm{neck}} / (l_\mathrm{e} V)}$.

## insertion_loss

```python
insertion_loss(
    transfer_matrix: _Complex,
    *,
    source_impedance: ArrayLike,
    radiation_impedance: ArrayLike,
) -> NDArray[np.float64]
```

Insertion loss of a four-pole element for given end impedances.

The attenuation from inserting the element in place of a direct (zero
length) connection between a source of internal impedance `Z_s` and a
radiation (termination) impedance `Z_r`:

$$
\mathrm{IL} = 20 \log_{10} \left\lvert \frac{T_{11} Z_\mathrm{r} + T_{12} + Z_\mathrm{s} Z_\mathrm{r} T_{21} + Z_\mathrm{s} T_{22}} {Z_\mathrm{s} + Z_\mathrm{r}} \right\rvert.
$$

**Parameters**

| Name | Description |
| :--- | :--- |
| `transfer_matrix` | A `(n_freq, 2, 2)` compound matrix. |
| `source_impedance` | Source internal acoustic impedance `Z_s`, Pa s/m3 (scalar or per-frequency, real or complex). |
| `radiation_impedance` | Termination/radiation acoustic impedance `Z_r`, Pa s/m3 (scalar or per-frequency). |

**Returns:** The insertion loss per frequency, dB.

**Raises**

| Exception | When |
| :--- | :--- |
| ValueError | If an impedance is neither a scalar nor one value per analysis frequency. |

## plot_silencer_geometry

```python
plot_silencer_geometry(
    kind: str,
    ax: Axes | None = None,
    *,
    length: float | None = None,
    chamber_area: float | None = None,
    pipe_area: float | None = None,
    inlet_extension: float = 0.0,
    outlet_extension: float = 0.0,
    duct_area: float | None = None,
    neck_area: float | None = None,
    neck_length: float | None = None,
    cavity_volume: float | None = None,
    branch_area: float | None = None,
    language: str = 'en',
) -> Axes
```

Draw a reactive silencer cross-section to scale.

Side cut through the duct axis with equivalent circular diameters
(`d = 2 sqrt(S / pi)`) for every cross-section area, matching the
parameters of the four `noise_control` silencer
constructors. A Helmholtz cavity is drawn as the cube of equal volume
with its volume annotated.

**Parameters**

| Name | Description |
| :--- | :--- |
| `kind` | One of `"expansion chamber"`, `"extended-tube chamber"`, `"Helmholtz resonator"`, `"quarter-wave resonator"` (the `ReactiveSilencerResult.kind` strings). |
| `ax` | Existing axes, or `None` to create a figure. |
| `length` | Chamber length or quarter-wave tube length, in metres. |
| `chamber_area` | Chamber cross-section, in m2 (chambers). |
| `pipe_area` | Inlet/outlet pipe cross-section, in m2 (chambers). |
| `inlet_extension` | Inlet tube extension into the chamber, in metres. |
| `outlet_extension` | Outlet tube extension, in metres. |
| `duct_area` | Main duct cross-section, in m2 (side branches). |
| `neck_area` | Neck cross-section, in m2 (Helmholtz). |
| `neck_length` | Neck length, in metres (Helmholtz). |
| `cavity_volume` | Cavity volume, in m3 (Helmholtz). |
| `branch_area` | Branch tube cross-section, in m2 (quarter-wave). |
| `language` | Label language, `"en"` (default) or `"es"`. |

**Returns:** The axes.

## quarter_wave_impedance

```python
quarter_wave_impedance(
    frequencies: ArrayLike,
    length: float,
    area: float,
    *,
    speed_of_sound: float = 343.0,
    density: float = 1.206,
) -> _Complex
```

Acoustic impedance of a closed quarter-wave side branch (Bies Eq. (8.146)).

$Z = -j (\rho c / S) \cot(k l_\mathrm{e})$; the reactance vanishes at
$l_\mathrm{e} = \lambda / 4$ ($f = c / 4 l_\mathrm{e}$), where the closed tube
presents a
pressure node and shorts the duct.

**Parameters**

| Name | Description |
| :--- | :--- |
| `frequencies` | Frequencies `f`, Hz (1-D array). |
| `length` | Effective tube length `l_e` (with end correction), m. |
| `area` | Tube cross-sectional area `S`, m2. |
| `speed_of_sound` | Speed of sound `c`, m/s. |
| `density` | Air density `rho`, kg/m3. |

**Returns:** The complex branch impedance per frequency, Pa s/m3.

## quarter_wave_resonator

```python
quarter_wave_resonator(
    frequencies: ArrayLike,
    duct_area: float,
    length: float,
    branch_area: float,
    *,
    speed_of_sound: float = 343.0,
    density: float = 1.206,
    source_impedance: ArrayLike | None = None,
    radiation_impedance: ArrayLike | None = None,
) -> ReactiveSilencerResult
```

Closed quarter-wave side-branch tube on a duct (Bies Eqs. (8.144), (8.146)).

**Parameters**

| Name | Description |
| :--- | :--- |
| `frequencies` | Frequencies `f`, Hz (1-D array). |
| `duct_area` | Main-duct cross-sectional area `S_d`, m2. |
| `length` | Effective branch length `l_e`, m. |
| `branch_area` | Branch tube area `S`, m2. |
| `speed_of_sound` | Speed of sound `c`, m/s. |
| `density` | Air density `rho`, kg/m3. |
| `source_impedance` | Optional source impedance `Z_s`, Pa s/m3. |
| `radiation_impedance` | Optional radiation impedance `Z_r`, Pa s/m3. |

**Returns:** A [`ReactiveSilencerResult`](/phonometry/reference/api/noise_control/silencers/#reactivesilencerresult); `resonances` holds the odd multiples of $f = c / (4 l_\mathrm{e})$ within the frequency range.

## ReactiveSilencerResult

```python
ReactiveSilencerResult(
    frequencies: np.ndarray,
    transmission_loss: np.ndarray,
    insertion_loss: np.ndarray | None,
    transfer_matrix: np.ndarray,
    kind: str,
    resonances: np.ndarray | None = None,
    geometry: dict[str, float] | None = None,
    plane_wave_limit: float | None = None,
    chain: SilencerChain | None = None,
)
```

Transmission and insertion loss of a reactive silencer over frequency.

**Attributes**

| Name | Description |
| :--- | :--- |
| `frequencies` | Frequencies `f`, Hz. |
| `transmission_loss` | Transmission loss per frequency, dB. |
| `insertion_loss` | Insertion loss per frequency, dB, or `None` when no source/radiation impedance was supplied. |
| `transfer_matrix` | The compound `(n_freq, 2, 2)` four-pole matrix. |
| `kind` | A short label of the device (e.g. `"expansion chamber"`). |
| `resonances` | Notable resonance frequencies, Hz (e.g. the resonator tuning frequency), or `None`. |
| `geometry` | The defining geometry the constructor was called with (keys matching its keyword names, e.g. `length`/`chamber_area`/ `pipe_area` for a chamber), retained so `plot_geometry` can draw the device; appended after the original fields and `None` for hand-built results that were not assembled by a [`SilencerChain`](/phonometry/reference/api/noise_control/silencers/#silencerchain). |
| `plane_wave_limit` | The first higher-order-mode cut-on frequency of the widest cross section of the device, Hz (Norton & Karczub Eq. 7.6, [`phonometry.noise_control.duct_modes.plane_wave_limit`](/phonometry/reference/api/noise_control/duct-modes/#plane_wave_limit)). The four-pole algebra of this module is one-dimensional and is valid below it; above it several modes propagate at once and the result describes the plane-wave mode only, which is why a [`PlaneWaveWarning`](/phonometry/reference/api/noise_control/duct-modes/#planewavewarning) is raised when the analysis reaches past it. `None` for hand-built results that do not retain their geometry. |
| `chain` | The [`SilencerChain`](/phonometry/reference/api/noise_control/silencers/#silencerchain) that assembled this result, for a result built by [`SilencerChain.result`](/phonometry/reference/api/noise_control/silencers/#silencerchainresult), and `None` otherwise. A chain is a list of four-pole elements rather than a named device, so it carries its geometry element by element instead of in `geometry`; `plot_geometry` draws whichever of the two is present. |

### ReactiveSilencerResult.plot()

```python
ReactiveSilencerResult.plot(
    ax: Axes | None = None,
    *,
    language: str = 'en',
    **kwargs: Any,
) -> Axes
```

Plot the transmission (and insertion) loss against frequency.

Requires matplotlib (`pip install phonometry[plot]`); returns the
`Axes`.

### ReactiveSilencerResult.plot_geometry()

```python
ReactiveSilencerResult.plot_geometry(
    ax: Axes | None = None,
    *,
    language: str = 'en',
) -> Axes
```

Draw the silencer cross-section to scale (dimensioned side cut).

A named device is drawn from its `geometry`; a result assembled by a
[`SilencerChain`](/phonometry/reference/api/noise_control/silencers/#silencerchain) is drawn from that `chain`, duct by duct.

Requires matplotlib (`pip install phonometry[plot]`); returns the
`Axes`.

**Parameters**

| Name | Description |
| :--- | :--- |
| `ax` | Existing axes, or `None` to create a figure. |
| `language` | Label language, `"en"` (default) or `"es"`. |

**Returns:** The axes.

**Raises**

| Exception | When |
| :--- | :--- |
| ValueError | If the result retains neither its `geometry` nor the `chain` that built it. |

### ReactiveSilencerResult.report()

```python
ReactiveSilencerResult.report(
    path: str,
    *,
    metadata: ReportMetadata | None = None,
    engine: str = 'reportlab',
    verbose: bool = False,
    language: str = 'en',
) -> str
```

Render a reactive-silencer transmission-loss fiche to `path`.

Writes a one-page silencer-performance sheet: the method-basis line
naming the plane-wave four-pole (transfer-matrix) method (Munjal,
Acoustics of Ducts and Mufflers 2nd ed., Eq. (3.27); Bies, Hansen &
Howard, Engineering Noise Control 5th ed., sections 8.8-8.9), an
optional metadata header (client, device, test environment,
instrumentation, climate, date), a per-band table (nominal frequency,
the transmission loss `TL` and, when computed, the insertion loss
`IL`) beside the `TL` (and `IL`) curves, the boxed mean
transmission loss over the analysis bands with the peak transmission
loss and the device kind, an optional verdict row against a declared
minimum, and a method-basis strip stating the four-pole
transmission-loss relation.

**Parameters**

| Name | Description |
| :--- | :--- |
| `path` | Destination path of the PDF file. |
| `metadata` | Optional [`ReportMetadata`](/phonometry/reference/api/building/insulation/#reportmetadata) supplying the header (`client`, `specimen` the device, `test_room` the test environment, `instrumentation`, `temperature_c`, `relative_humidity_percent`, `pressure`, `test_date`), the footer identity (`laboratory`, `operator`, `report_id`, `notes`) and, via `requirement`, a declared minimum mean transmission loss (more transmission loss is better). |
| `engine` | Rendering back end; only `"reportlab"` is supported. |
| `verbose` | Accepted for signature symmetry with the other fiches; the silencer table already shows the insertion loss when it was computed. |
| `language` | Fiche language: `"en"` (default) or `"es"`. |

**Returns:** The written `path` as a `str`.

**Raises**

| Exception | When |
| :--- | :--- |
| ValueError | If `engine` is not `"reportlab"` or `language` is unknown. |
| ImportError | If reportlab (or, for the figure, matplotlib) is not installed (`pip install phonometry[report]`). |

## shunt_matrix

```python
shunt_matrix(branch_impedance: ArrayLike) -> _Complex
```

Four-pole matrix of a side branch of impedance `Z_b` (Bies Eq. (8.144)).

**Parameters**

| Name | Description |
| :--- | :--- |
| `branch_impedance` | Acoustic impedance `Z_b` of the branch, Pa s/m3 (1-D complex array over frequency). |

**Returns:** A `(n_freq, 2, 2)` complex transfer-matrix array.

## SilencerChain

```python
SilencerChain(
    frequencies: ArrayLike,
    *,
    speed_of_sound: float = 343.0,
    density: float = 1.206,
)
```

A chain of four-pole elements that remembers the geometry it was given.

[`duct_matrix`](/phonometry/reference/api/noise_control/silencers/#duct_matrix), [`shunt_matrix`](/phonometry/reference/api/noise_control/silencers/#shunt_matrix) and [`cascade`](/phonometry/reference/api/noise_control/silencers/#cascade) build any
silencer layout the named devices do not cover, but they return bare
matrices: the compound matrix of a hand-built chain is a stack of complex
numbers, and nothing in it recalls that the first element was a 300 mm run
of 200 mm duct. This class calls the same three functions and keeps the
arguments, so the chain can be drawn (`plot_geometry`) as well as
evaluated (`result`), and the drawing cannot drift from the model
because one call produces both.

Elements are added in order from inlet to outlet, and each adder returns
the chain so the calls read as the device does:

```text
chain = (
    SilencerChain(frequencies)
    .duct(0.30, 0.0314)
    .shunt(quarter_wave_impedance(frequencies, 0.686, 0.0079))
    .duct(0.60, 0.1257)
    .duct(0.30, 0.0314)
)
```

What the drawing may show follows from what the elements know. A duct is
drawn to scale from its declared length and area; a shunt declares an
impedance, which fixes no length, no area and no volume, so it is marked
at the station where it joins the run and nothing about its shape is
invented (see `plot_geometry`).

**Parameters**

| Name | Description |
| :--- | :--- |
| `frequencies` | Frequencies `f`, Hz (1-D array), shared by every element of the chain. |
| `speed_of_sound` | Speed of sound `c`, m/s. |
| `density` | Air density `rho`, kg/m3. |

### SilencerChain.duct()

```python
SilencerChain.duct(length: float, area: float) -> SilencerChain
```

Append a straight duct of length `L` and area `S`.

**Parameters**

| Name | Description |
| :--- | :--- |
| `length` | Duct length `L`, m. A zero-length duct is the identity matrix, so it is neither computed against nor drawn. |
| `area` | Cross-sectional area `S`, m2. |

**Returns:** The chain, so the calls can be written one after another.

### SilencerChain.elements

*property*

The recorded elements, in order from inlet to outlet.

**Returns:** The elements added so far.

### SilencerChain.frequencies

*property*

The analysis frequencies shared by every element, Hz.

**Returns:** The frequency grid the chain was built on.

### SilencerChain.plot_geometry()

```python
SilencerChain.plot_geometry(
    ax: Axes | None = None,
    *,
    language: str = 'en',
) -> Axes
```

Draw the chain: its ducts to scale, its branch points marked.

Every duct is drawn at its declared length and equivalent circular
diameter `d = 2 sqrt(S / pi)`, so the runs, the area steps between
them and the overall length are read off the page. A shunt element
holds an impedance and no geometry at all, so it is not drawn as a
stub of any length: it is marked with a leader at the station where it
joins the run, carrying its label and, when the analysis grid resolves
one, the frequency at which it comes closest to shorting the duct.

Requires matplotlib (`pip install phonometry[plot]`); returns the
`Axes`.

**Parameters**

| Name | Description |
| :--- | :--- |
| `ax` | Existing axes, or `None` to create a figure. |
| `language` | Label language, `"en"` (default) or `"es"`. |

**Returns:** The axes.

**Raises**

| Exception | When |
| :--- | :--- |
| ValueError | If the chain holds no duct of positive length, and so has no geometry and no scale to draw at. |

### SilencerChain.result()

```python
SilencerChain.result(
    *,
    inlet_area: float,
    outlet_area: float,
    source_impedance: ArrayLike | None = None,
    radiation_impedance: ArrayLike | None = None,
) -> ReactiveSilencerResult
```

Evaluate the chain into a [`ReactiveSilencerResult`](/phonometry/reference/api/noise_control/silencers/#reactivesilencerresult).

The port areas are the pipes the chain is connected between, which
`transmission_loss` needs and the chain itself does not contain: put
them in the chain as duct elements if the drawing is to show them.

**Parameters**

| Name | Description |
| :--- | :--- |
| `inlet_area` | Inlet pipe area `S_in`, m2. |
| `outlet_area` | Outlet pipe area `S_out`, m2. |
| `source_impedance` | Optional source impedance `Z_s` for the insertion loss, Pa s/m3. |
| `radiation_impedance` | Optional radiation impedance `Z_r` for the insertion loss, Pa s/m3. |

**Returns:** The result, carrying a snapshot of this chain so that it can be drawn as well as plotted and reported.

**Raises**

| Exception | When |
| :--- | :--- |
| ValueError | If the chain is empty. |

### SilencerChain.shunt()

```python
SilencerChain.shunt(
    branch_impedance: ArrayLike,
    *,
    label: str | None = None,
) -> SilencerChain
```

Append a side branch of acoustic impedance `Z_b`.

The branch is the only element that can be given a `label`, because
it is the only one the drawing cannot identify by its dimensions.

**Parameters**

| Name | Description |
| :--- | :--- |
| `branch_impedance` | Acoustic impedance `Z_b` of the branch, Pa s/m3: one value per analysis frequency, or a scalar held constant over the grid. |
| `label` | What the branch is, e.g. `"Helmholtz resonator, 125 Hz"`. Rendered verbatim in the drawing, in whatever language it is written in. |

**Returns:** The chain, so the calls can be written one after another.

**Raises**

| Exception | When |
| :--- | :--- |
| ValueError | If `branch_impedance` is neither a scalar nor one value per analysis frequency. |

### SilencerChain.transfer_matrix

*property*

The compound four-pole matrix of the chain.

**Returns:** The ordered product [`cascade`](/phonometry/reference/api/noise_control/silencers/#cascade) makes of the element matrices, `(n_freq, 2, 2)`.

**Raises**

| Exception | When |
| :--- | :--- |
| ValueError | If the chain is empty. |

## SilencerChainElement

```python
SilencerChainElement(
    matrix: np.ndarray,
    length: float | None = None,
    area: float | None = None,
    label: str | None = None,
    shorting_frequency: float | None = None,
)
```

One recorded element of a [`SilencerChain`](/phonometry/reference/api/noise_control/silencers/#silencerchain).

The element carries its four-pole matrix and, with it, whatever geometry
the call that produced the matrix was given. That is the whole asymmetry
of a hand-built chain: [`duct_matrix`](/phonometry/reference/api/noise_control/silencers/#duct_matrix) is handed a length and an area,
so a duct element knows its shape, while [`shunt_matrix`](/phonometry/reference/api/noise_control/silencers/#shunt_matrix) is handed an
impedance and nothing else, so a shunt element has no shape to know.

**Attributes**

| Name | Description |
| :--- | :--- |
| `matrix` | The element's `(n_freq, 2, 2)` four-pole matrix. |
| `length` | Duct length `L`, m, or `None` for a shunt element. |
| `area` | Duct cross-sectional area `S`, m2, or `None` for a shunt element. |
| `label` | The name the element was given, or `None`. |
| `shorting_frequency` | For a shunt element, the analysis frequency at which `\|Z_b\|` is least (where the branch comes closest to shorting the duct), or `None` when that least value sits on an end of the analysis grid, and for every duct element. |

### SilencerChainElement.is_duct

*property*

Whether this is a duct element (an element with a declared area).

**Returns:** `True` for a duct element, `False` for a shunt element.

## transmission_loss

```python
transmission_loss(
    transfer_matrix: _Complex,
    *,
    inlet_area: float,
    outlet_area: float,
    speed_of_sound: float = 343.0,
    density: float = 1.206,
) -> NDArray[np.float64]
```

Transmission loss of a four-pole element (Munjal Eq. (3.27), no flow).

$$
\mathrm{TL} = 10 \log_{10}\!\left[(Z_n/Z_1) \frac{1}{4} \lvert T_{11} + T_{12}/Z_n + Z_1 T_{21} + (Z_1/Z_n) T_{22} \rvert^2\right]
$$

with $Z_1 = \rho c / S_{\mathrm{in}}$ and
$Z_n = \rho c / S_{\mathrm{out}}$ (Munjal, *Acoustics
of Ducts and Mufflers* 2nd ed., Eq. (3.27)). Do not "restore" the Bies
Eq. (8.141) weighting: as printed there the equation fails the
sudden-expansion limit for unequal port areas (see `docs/ERRATA.md`).

**Parameters**

| Name | Description |
| :--- | :--- |
| `transfer_matrix` | A `(n_freq, 2, 2)` compound matrix. |
| `inlet_area` | Inlet pipe area `S_in`, m2. |
| `outlet_area` | Outlet pipe area `S_out`, m2. |
| `speed_of_sound` | Speed of sound `c`, m/s. |
| `density` | Air density `rho`, kg/m3. |

**Returns:** The transmission loss per frequency, dB.
