noise_control.duct_path
End-to-end duct-borne noise calculation: fan to room, element by element.
The classic consulting workflow of HVAC acoustics is a bookkeeping exercise. Start from the sound power the fan puts into the duct, walk down the path, and at every element subtract what it attenuates and add back what its own airflow regenerates. What arrives at the terminal device is converted into a sound pressure level by the room effect, the supply and return paths are combined, and the result is compared with the room criterion. If it fails, the sheet itself says which element to change.
This module implements that cascade as a result object. It follows the worked sheet of Long, Architectural Acoustics 2nd ed., Table 14.9 (a 5000 cfm forward-curved fan feeding a room through a supply and a return path, checked against NC 30) and the same row structure as the industry procedure of AHRI Standard 885, Procedure for Estimating Occupied Space Sound Levels in the Application of Air Terminals and Air Outlets, Table 8: one row per physical element, octave bands across the columns, regenerated noise as its own row combined logarithmically rather than subtracted, and a cumulative received-level row at the foot.
Three arithmetic conventions matter and are worth stating once:
- Attenuations are positive. Every element model in
phonometry.noise_control.hvacreturns a loss as a positive number of decibels, and the cascade subtracts it. Published worksheets print the same quantity as a negative level change;DuctPathResult.tablefollows the worksheet sign so the printed sheet reads like the reference. - Regenerated noise adds on a power basis. The self-noise of an element is a sound power level in its own right and is combined with the level arriving at that point as .
- There is a self-noise floor. Long’s worked sheet uses a 0 dB self-noise
sound power level as the default and whenever a calculated level would be
negative, which is why his received spectrum bottoms out near 0 dB rather
than running to minus infinity.
self_noise_floorreproduces that and can be switched off withNone.
The elements themselves come from phonometry.noise_control.hvac (fan
sound power, straight lined and unlined ducts, elbows, flexible duct, branch
splits, end reflection, silencer self-noise, plenums and the room effect) and
from manufacturer data, which is what a real calculation uses for silencers and
air terminal devices. phonometry.noise_control.duct_modes supplies the
cut-on frequency above which the plane-wave elements stop being valid, and the
cascade raises a
PlaneWaveWarning when a duct
section is declared and the analysis runs past it.
Auto-generated from the source docstrings by
scripts/generate_api_docs.py(make api-docs). Do not edit by hand.
combine_duct_paths
Section titled “combine_duct_paths”combine_duct_paths( paths: list[DuctPathResult] | tuple[DuctPathResult, ...], *, criterion: str | None = None, target: float | None = None, label: str = 'Combined paths',) -> DuctPathResultCombine the received spectra of several duct paths into one result.
A room is usually fed by more than one path (Long’s worked sheet combines a supply and a return path before comparing with NC 30). The received levels add on a power basis, and the combination is rated against the same design criterion.
Parameters
| Name | Description |
|---|---|
paths | Two or more DuctPathResult objects sharing the same analysis bands. |
criterion | Room-criterion family; None (default) takes it from the first path. |
target | Design criterion value; None (default) takes it from the first path that declares one. |
label | A short human label of the combination. |
Returns: A DuctPathResult whose contributions hold the per-path received spectra and whose stages are empty.
Raises
| Exception | When |
|---|---|
| ValueError | If fewer than one path is given or the bands differ. |
duct_path
Section titled “duct_path”duct_path( frequencies: ArrayLike, source_level: ArrayLike, elements: list[DuctElement] | tuple[DuctElement, ...], *, room_effect: ArrayLike | None = None, source_label: str = 'Fan', criterion: str = 'NC', target: float | None = None, self_noise_floor: float | None = 0.0, section: dict[str, float] | None = None, flow_velocity: float = 0.0, speed_of_sound: float = 343.0, label: str = 'Duct path',) -> DuctPathResultCascade a duct path from the source sound power to the received level.
Walks the elements in order, subtracting each attenuation and combining each regenerated sound power level on a power basis, then applies the room effect and rates the result against the design criterion. This is the calculation of Long Table 14.9 and of AHRI 885 Table 8.
Parameters
| Name | Description |
|---|---|
frequencies | Octave-band centre frequencies, Hz (1-D array). The published sheets use 63 Hz to 8 kHz. |
source_level | Sound power level entering the path, dB re 1 pW; e.g. from phonometry.noise_control.hvac.fan_sound_power. |
elements | The path elements in order, as DuctElement entries. Their attenuation and self_noise accept a scalar, a per-band array or an HvacSpectrumResult. |
room_effect | The room effect as a positive attenuation, dB (from phonometry.noise_control.hvac.room_effect). None leaves the received spectrum as a sound power level at the terminal device. |
source_label | Description of the source for the printed sheet. |
criterion | Room-criterion family, "NC" (default) or "RC". |
target | The design criterion value (e.g. 30), or None. |
self_noise_floor | Default regenerated sound power level, dB re 1 pW, applied where an element declares none and as a floor under every element’s self-noise. Long’s worked sheet uses 0 dB; pass None to disable the floor entirely. |
section | Optional duct cross section for the plane-wave validity check, as {"diameter": d}, {"width": a, "height": b} or {"area": s}, in metres. When given, a PlaneWaveWarning is raised for analysis frequencies above the first cut-on. |
flow_velocity | Mean axial flow speed in that section, m/s, used for the cut-on Mach correction. |
speed_of_sound | Speed of sound, m/s, for the same check. |
label | A short human label of the path. |
Returns: A DuctPathResult.
Raises
| Exception | When |
|---|---|
| ValueError | If the spectra do not share one value per band or the criterion family is unknown. |
| TypeError | If an entry of elements is not a DuctElement. |
DuctElement
Section titled “DuctElement”DuctElement( label: str, attenuation: Any = None, self_noise: Any = None, code: str = '',)One element of a duct path: what it attenuates and what it regenerates.
Attributes
| Name | Description |
|---|---|
label | Human description of the element, e.g. "Silencer, 3 ft, standard pressure drop". |
attenuation | Octave-band attenuation as a positive loss, dB. A scalar is broadcast over the bands; an HvacSpectrumResult (or anything exposing values) is accepted directly. None means no attenuation. |
self_noise | Octave-band regenerated sound power level of the element, dB re 1 pW, or None when the element regenerates nothing (the cascade then applies its self-noise floor). |
code | A short worksheet code for the printed sheet (AHRI 885 style, e.g. "D1", "S"); defaults to the row number. |
DuctPathResult
Section titled “DuctPathResult”DuctPathResult( frequencies: np.ndarray, source_level: np.ndarray, source_label: str, stages: tuple[DuctPathStage, ...], room_effect: np.ndarray | None, received_level: np.ndarray, criterion: str, target: float | None, label: str, contributions: tuple[tuple[str, np.ndarray], ...] = ...,)The end-to-end duct-borne noise calculation of one path (or of a sum).
Built by duct_path for a single fan-to-room path, and by
combine_duct_paths for the logarithmic sum of several such paths
(whose stages are empty and whose contributions carry one entry per
path).
Attributes
| Name | Description |
|---|---|
frequencies | Octave-band centre frequencies, Hz. |
source_level | Sound power level entering the path, dB re 1 pW. |
source_label | Description of the source, e.g. the fan. |
stages | One DuctPathStage per element, in path order. |
room_effect | The room effect applied after the last element, as a positive attenuation in dB, or None when the path was not taken into a room (received_level is then still a sound power level). |
received_level | The spectrum at the receiver: a sound pressure level in dB when room_effect was applied, otherwise the sound power level leaving the last element. |
criterion | "NC" or "RC", the room-criterion family used. |
target | The design criterion value (e.g. 30 for NC 30), or None when no target was declared. |
contributions | (label, received_level) per contributing path for a combination; empty for a single path. |
label | A short human label of the path. |
DuctPathResult.criterion_curve
Section titled “DuctPathResult.criterion_curve”property
The design criterion curve at frequencies, dB, or None.
The NC or RC curve of target, sampled at the analysis bands, so
it can be compared band by band with received_level.
DuctPathResult.exceedance
Section titled “DuctPathResult.exceedance”property
Band-by-band excess of the received level over the criterion, dB.
Positive where the design criterion is exceeded. None when no
target was declared.
DuctPathResult.meets_target
Section titled “DuctPathResult.meets_target”property
True when no band exceeds the design criterion curve.
None when no target was declared. This is the band-by-band test a
design sheet applies, not the NC rating of rating, which the
standard derives by its own two-step procedure.
DuctPathResult.plot()
Section titled “DuctPathResult.plot()”DuctPathResult.plot( ax: Axes | None = None, *, language: str = 'en', **kwargs: Any,) -> AxesPlot the spectrum cascading down the path against the criterion curve.
One line per element shows where the spectrum stands after that
element, from the source at the top to the received level at the
bottom, with the design criterion curve overlaid. Requires matplotlib
(pip install phonometry[plot]).
Parameters
| Name | Description |
|---|---|
ax | Existing axes, or None to create a figure. |
language | Label language, "en" (default) or "es". |
kwargs | Forwarded to the received-level Axes.plot. |
Returns: The axes.
DuctPathResult.rating
Section titled “DuctPathResult.rating”property
The room-criterion rating of received_level.
An NCResult when
criterion is "NC", otherwise an
RCResult (ANSI/ASA S12.2-2019).
DuctPathResult.report()
Section titled “DuctPathResult.report()”DuctPathResult.report( path: str, *, metadata: ReportMetadata | None = None, engine: str = 'reportlab', verbose: bool = False, language: str = 'en',) -> strRender the duct-borne noise calculation sheet to a PDF at path.
Writes a one-page duct-path sheet in the layout of the published procedures (AHRI Standard 885 Table 8; Long Table 14.9): the method-basis line, an optional metadata header (client, system, room, instrumentation, climate, date), the element table with the octave bands across the columns and one row per element (attenuations printed with the worksheet’s negative sign, regenerated noise shaded as its own row, the running level after each element and the room effect and received spectrum at the foot), the cascade chart against the criterion curve, the boxed room-criterion rating, the verdict against the design criterion, and a method-basis strip.
Parameters
| Name | Description |
|---|---|
path | Destination path of the PDF file. |
metadata | Optional ReportMetadata supplying the header (client, specimen the air system, test_room the served room, instrumentation, temperature, relative_humidity, pressure, test_date) and the footer identity. The design criterion comes from the result’s own target; a requirement in the metadata overrides it. |
engine | Rendering back end; only "reportlab" is supported. |
verbose | When True the table adds the running level after every element; when False only the source, the element attenuations, the regenerated-noise rows and the received spectrum are printed, which keeps a long path on one page. |
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]). |
DuctPathResult.table()
Section titled “DuctPathResult.table()”DuctPathResult.table() -> list[dict[str, Any]]The per-element sheet, one entry per printed row.
The rows follow the layout of a published duct-borne calculation sheet
(Long Table 14.9; AHRI 885 Table 8): the source, then for each element
its attenuation, the Sum after subtracting it, its regenerated noise
and the Combined level, then the room effect and the received
spectrum. Each entry has code, label, kind (one of
"source", "attenuation", "sum", "self_noise",
"level", "contribution", "room_effect", "received",
"criterion") and values (a per-band array). An attenuation row
carries the worksheet sign, i.e. the negative of the positive loss
the models return; a "contribution" row is the received spectrum of
one path of a combination.
Returns: The list of row dictionaries, in printing order.
DuctPathStage
Section titled “DuctPathStage”DuctPathStage( label: str, code: str, attenuation: np.ndarray, attenuated: np.ndarray, self_noise: np.ndarray, level: np.ndarray,)The computed rows of one element of a duct path.
The four spectra are exactly the four printed rows of a duct-borne calculation sheet: what the element takes out, what the level becomes, what the element puts back and where the path stands afterwards.
Attributes
| Name | Description |
|---|---|
label | Human description of the element. |
code | The worksheet code of the row. |
attenuation | Attenuation of the element as a positive loss, dB. |
attenuated | Level after subtracting the attenuation (the Sum row), dB. |
self_noise | Regenerated sound power level of the element, dB re 1 pW, after the self-noise floor has been applied. |
level | Level leaving the element, the energy sum of attenuated and self_noise (the Combined row), dB. |