Skip to content

AeroViz.size

Size-distribution processing: statistics, weighting conversions and the SMPS–APS merge. Worked examples: Size Distribution; call-by-call summary: Post-Processing Functions. Theory: Log-normal Distribution, ICRP 66, Mie.

Input

df_pnsd.columns = [11.8, 13.6, 15.7, ..., 523.3]   # diameters (nm) as numeric column labels
df_pnsd.index   = DatetimeIndex                     # one row per scan
df_gRH          = DataFrame({'gRH': [1.2, 1.3, ...]}, index=time_index)   # for to_dry

This is exactly what RawDataReader('SMPS' | 'APS', ...) returns.

Functions

AeroViz.size

Top-level size-distribution functions; convenience wrappers — see AeroViz.dataProcess.SizeDistr.* for full algorithm details.

Attributes

__all__ module-attribute

__all__ = ['psd_stats', 'psd_distributions', 'merge_psd']

Classes

SizeDist

SizeDist(data: DataFrame, state: Literal['dN', 'ddp', 'dlogdp'] = 'dlogdp', weighting: Literal['n', 's', 'v', 'ext_in', 'ext_ex'] = 'n')

A class representing particle size distribution data.

This class encapsulates particle size distribution data and provides convenient properties for accessing particle diameters, logarithmic bin widths, and distribution state information.

Attributes:

Name Type Description
_data DataFrame

The processed PSD data stored as a pandas DataFrame.

_dp ndarray

The array of particle diameters from the PSD data.

_dlogdp ndarray

The array of logarithmic particle diameter bin widths.

_index DatetimeIndex

The index of the DataFrame representing time.

_state str

The state of particle size distribution data ('dN', 'ddp', 'dlogdp').

_weighting str

The weighting type for distribution calculations.

Methods:

Name Description
data

Returns the size distribution DataFrame.

dp

Returns the particle diameter array.

dlogdp

Returns the logarithmic bin width array.

index

Returns the time index.

state

Returns the distribution state.

weighting

Returns the weighting type.

Examples:

>>> from pandas import read_csv
>>> df = read_csv('PNSD_dNdlogdp.csv', parse_dates=['Time'], index_col='Time')
>>> psd = SizeDist(df, state='dlogdp', weighting='n')
>>> print(psd.dp)

Initialize a SizeDist object.

Parameters:

Name Type Description Default
data DataFrame

The particle size distribution data with particle diameters as columns. Column names must be numeric diameter values in nm.

required
state (dN, ddp, dlogdp)

The state of the distribution data: - 'dN': Raw number concentration - 'ddp': dN/ddp normalized - 'dlogdp': dN/dlogdp normalized

'dN'
weighting (n, s, v, ext_in, ext_ex)

The weighting type for property calculations: - 'n': Number weighting - 's': Surface weighting - 'v': Volume weighting - 'ext_in': Internal extinction weighting - 'ext_ex': External extinction weighting

'n'

Raises:

Type Description
ValueError

If data is None or empty, or column names are not numeric.

TypeError

If data is not a DataFrame.

Attributes
data property
data: DataFrame

Return the size distribution DataFrame.

dp property writable
dp: ndarray

Return the particle diameter array in nm.

dlogdp property writable
dlogdp: ndarray

Return the logarithmic bin width array.

index property
index

Return the time index of the distribution data.

state property writable
state

Return the distribution state.

weighting property writable
weighting

Return the weighting type.

Methods:
to_surface
to_surface() -> DataFrame

Convert to surface area distribution.

Formula: dS/dlogDp = π * dp² * dN/dlogDp

Returns:

Type Description
DataFrame

Surface area distribution (nm² / cm³).

Examples:

>>> psd = SizeDist(df)
>>> surface = psd.to_surface()
to_volume
to_volume() -> DataFrame

Convert to volume distribution.

Formula: dV/dlogDp = (π/6) * dp³ * dN/dlogDp

Returns:

Type Description
DataFrame

Volume distribution (nm³ / cm³).

Examples:

>>> psd = SizeDist(df)
>>> volume = psd.to_volume()
properties
properties() -> DataFrame

Calculate statistical properties of the distribution.

Returns:

Type Description
DataFrame

Properties including GMD, GSD, mode, and mode contributions.

Examples:

>>> psd = SizeDist(df)
>>> props = psd.properties()
to_extinction
to_extinction(RI: DataFrame, method: str = 'internal', result_type: str = 'extinction') -> DataFrame

Calculate extinction distribution using Mie theory.

Parameters:

Name Type Description Default
RI DataFrame

Refractive index data with n and k columns.

required
method (internal, external, core_shell, sensitivity)

Mixing method for Mie calculation.

'internal'
result_type (extinction, scattering, absorption)

Type of optical result.

'extinction'

Returns:

Type Description
DataFrame

Extinction distribution (Mm⁻¹).

Examples:

>>> psd = SizeDist(df)
>>> ext = psd.to_extinction(df_RI, method='internal')
mode_statistics
mode_statistics(unit: str = 'nm') -> dict

Calculate statistics for different size modes.

Computes number, surface, and volume distributions along with GMD, GSD, total, and mode for each size range.

Parameters:

Name Type Description Default
unit (nm, um)

Unit of particle diameter in the data.

'nm'

Returns:

Type Description
dict
  • 'number': Number distribution (dN)
  • 'number_norm': Normalized number distribution (dN/dlogDp)
  • 'surface': Surface area distribution
  • 'surface_norm': Normalized surface distribution
  • 'volume': Volume distribution
  • 'volume_norm': Normalized volume distribution
  • 'statistics': DataFrame with GMD, GSD, total, mode per size mode

Examples:

>>> psd = SizeDist(df)
>>> stats = psd.mode_statistics()
>>> stats['statistics']  # GMD, GSD for each mode
to_dry
to_dry(df_gRH: DataFrame, uniform: bool = True) -> DataFrame

Convert ambient (wet) PSD to dry PSD.

Shrinks particles according to hygroscopic growth factor and redistributes concentrations to appropriate smaller diameter bins.

Parameters:

Name Type Description Default
df_gRH DataFrame

DataFrame with 'gRH' column (growth factor = Dp_wet / Dp_dry).

required
uniform bool

If True, apply uniform gRH across all sizes. If False, apply size-dependent gRH based on lognormal distribution.

True

Returns:

Type Description
DataFrame

Dry particle size distribution.

Examples:

>>> psd = SizeDist(df_pnsd)
>>> dry_psd = psd.to_dry(df_chem[['gRH']])
lung_deposition
lung_deposition(activity: str = 'light') -> dict

Calculate lung deposition using ICRP 66 model.

Based on the ICRP (International Commission on Radiological Protection) Human Respiratory Tract Model for particle deposition.

Parameters:

Name Type Description Default
activity (sleep, sitting, light, heavy)

Activity level affecting breathing pattern: - 'sleep': Sleeping (nasal, 7.5 L/min) - 'sitting': Sitting awake (nasal, 9 L/min) - 'light': Light exercise (nasal+oral, 25 L/min) - 'heavy': Heavy exercise (oral, 50 L/min)

'sleep'

Returns:

Type Description
dict
  • 'DF': Deposition fraction DataFrame (HA, TB, AL, Total)
  • 'deposited': Deposited number distribution
  • 'dose': Regional deposited dose (particles/cm³)
  • 'total_dose': Total deposited particles
Notes

Deposition regions: - HA (Head Airways): 頭部氣道 (鼻、咽、喉) - TB (Tracheobronchial): 氣管支氣管區 - AL (Alveolar): 肺泡區

References
  • ICRP Publication 66 (1994)
  • Hinds, W.C. (1999) Aerosol Technology

Examples:

>>> psd = SizeDist(df)
>>> lung = psd.lung_deposition(activity='light')
>>> lung['DF']        # Deposition fractions
>>> lung['dose']      # Regional dose

Functions:

merge_v1

merge_v1(df_smps, df_aps, aps_unit, shift_mode, smps_overlap_lowbound, aps_fit_highbound, density_range=(0.6, 2.6))

merge_v2

merge_v2(df_smps, df_aps, aps_unit='um', smps_overlap_lowbound=500, aps_fit_highbound=1000, density_range=(0.6, 2.6))

merge_v3

merge_v3(df_smps, df_aps, aps_unit='um', smps_overlap_lowbound=500, aps_fit_highbound=1000, dndsdv_alg=True, density_range=(0.6, 2.6))

merge_v4

merge_v4(df_smps, df_aps, df_pm25, aps_unit='um', smps_overlap_lowbound=500, aps_fit_highbound=1000, dndsdv_alg=True, density_range=(0.6, 2.6), times_range=(0.8, 1.25, 0.05))

merge_v5

merge_v5(df_smps, df_aps, df_pm1, aps_unit='um', smps_overlap_lowbound=500, aps_fit_highbound=1000, density_range=(0.5, 3.0), n_iter=3, mass_rel_unc=None)

EXPERIMENTAL mass-anchored merge. Requires a PM1 mass reference.

Parameters:

Name Type Description Default
df_smps DataFrame

SMPS / APS dN/dlogDp (diameters as columns; APS in µm if aps_unit='um').

required
df_aps DataFrame

SMPS / APS dN/dlogDp (diameters as columns; APS in µm if aps_unit='um').

required
df_pm1 Series or DataFrame

PM1 mass concentration (µg/m³), same time index. Drives the density via mass closure. Prefer a volatile-corrected PM1.

required
density_range tuple

Physical clip for the effective density (g/cm³).

(0.5, 3.0)
n_iter int

Mass-closure fixed-point iterations (V(<1µm) depends weakly on the shift).

3
mass_rel_unc Series

Per-timestamp relative mass uncertainty (e.g. volatile fraction). Used only to propagate into density_unc; defaults to 0.1.

None

Returns:

Type Description
dict

data (merged dN/dlogDp), density (daily effective density), density_hourly (raw hourly mass-closure density) and density_unc.

psd_stats

psd_stats(df, hybrid_bin_start_loc=None, unit='nm', bin_range=(11.8, 19810), input_type='dlogdp')

Compute mode statistics for a particle size distribution.

Convenience wrapper around the logic in AeroViz.dataProcess.SizeDistr.SizeDistr.basic. Builds a SizeDist from the input DataFrame, computes per-bin dlogdp, and returns the mode statistics (with the 'statistics' key renamed to 'other' for backward compatibility).

Parameters:

Name Type Description Default
df DataFrame

Raw particle size distribution data. Column labels must be diameters convertible to float.

required
hybrid_bin_start_loc int

Column index where the bin spacing changes (for hybrid instruments such as merged SMPS+APS). No longer needed — when None the per-bin widths are used, which handle a hybrid grid correctly on their own. Kept so existing callers reproduce their previous numbers.

None
unit (nm, um)

Unit of the diameter columns.

'nm'
bin_range tuple of float

Inclusive (min, max) diameter range (in unit) to keep.

(11.8, 19810)
input_type (dlogdp, norm, raw)

'dlogdp'/'norm' — input is already normalized (dN/dlogDp). Anything else — input is raw counts and will be divided by dlogdp.

'dlogdp'

Returns:

Type Description
dict

Distributions and statistics by weighting (keys include 'number', 'surface', 'volume', 'other').

psd_distributions

psd_distributions(df_pnsd)

Compute number / surface / volume distributions and their properties.

Convenience wrapper around the logic in AeroViz.dataProcess.SizeDistr.SizeDistr.distributions.

Parameters:

Name Type Description Default
df_pnsd DataFrame

Particle number size distribution (dN/dlogDp). Column labels must be diameters in nm.

required

Returns:

Type Description
dict

{'number', 'surface', 'volume', 'properties'} — the first three are DataFrames keyed by diameter; 'properties' concatenates the per-distribution properties (GMD, GSD, mode, etc.).

merge_psd

merge_psd(df_smps, df_aps, *, version: int = 4, df_pm25=None, df_pm1=None, aps_unit: str = 'um', smps_overlap_lowbound: float = 500, aps_fit_highbound: float = 1000, shift_mode: str = 'mobility', dndsdv_alg: bool = True, density_range: tuple = (0.6, 2.6), times_range: tuple = (0.8, 1.25, 0.05))

Merge SMPS and APS particle size distributions into a continuous PSD.

Parameters:

Name Type Description Default
df_smps DataFrame

SMPS and APS particle size distributions. Columns are diameters (SMPS in nm; APS in µm if aps_unit='um', else nm).

required
df_aps DataFrame

SMPS and APS particle size distributions. Columns are diameters (SMPS in nm; APS in µm if aps_unit='um', else nm).

required
version (1, 2, 3, 4, 5)

Algorithm version: 1 — Original power-law fit with shift_mode parameter. 2 — Simplified output, no QC filtering. 3 — Multiprocessing + dN/dS/dV correlation algorithm. 4 — PM2.5 fitness function + SMPS times correction (RECOMMENDED). 5 — ⚠️ EXPERIMENTAL / 測試中 ⚠️ mass-anchored density (PM1 mass closure, daily) instead of the degenerate overlap. Requires df_pm1. Returns data + density / density_hourly / density_unc. API/behaviour not stable.

1
df_pm25 DataFrame

PM2.5 reference for fitness. Required when version=4.

None
df_pm1 Series or DataFrame

PM1 mass reference (µg/m³). Required when version=5. Prefer a volatile-corrected PM1 (the dominant uncertainty in the density).

None
aps_unit (um, nm)

Unit of the APS diameter columns.

'um'
smps_overlap_lowbound float

SMPS bin lower bound for the overlap region (nm).

500
aps_fit_highbound float

APS bin upper bound for the power-law fit region (nm).

1000
shift_mode (mobility, aerodynamic)

Only used when version=1.

'mobility'
dndsdv_alg bool

Apply dN/dS/dV correlation refinement. Only used when version >= 3.

True
density_range tuple of float

Plausible effective-density range (g/cm³) for quality control. Each timestamp's shift² is its estimated effective density; timestamps outside this range are dropped (set to NaN). Widen for looser QC (e.g. (0.3, 2.6)), narrow for stricter. Applied in every version.

(0.6, 2.6)
times_range tuple of 3 floats

(start, stop, step) for the SMPS-times grid search. Only used when version=4.

(0.8, 1.25, 0.05)

Returns:

Type Description
dict

Every version returns a dict keyed consistently. Two keys are always present:

  • 'data' : the recommended merged dN/dlogDp (diameters in nm as columns). v1 → the single power-law merge; v2 → mobility merge; v3/v4 → the APS-corrected dN/dS/dV merge (cor_dndsdv).
  • 'density' : estimated effective density (g/cm³).

Version-specific extras:

  • v2 : 'data_aero' (aerodynamic-diameter merge).
  • v3 : 'data_dn', 'data_dndsdv', 'data_cor_dn' (the other algorithm variants); 'density' has one column per variant.
  • v4 : same variants as v3 plus 'times' (the chosen SMPS-times multiplier per algorithm).

Raises:

Type Description
ValueError

If version is not in {1, 2, 3, 4, 5}, version=4 without df_pm25, or version=5 without df_pm1.

The SizeDist class

The engine behind the functions, for per-row conversions (extinction, dry PSD, lung deposition). Not deprecated.

Attribute Type Meaning
data DataFrame the distribution as given
dp ndarray diameters (nm)
dlogdp ndarray log-spacing per bin
index DatetimeIndex time index
state str 'dN', 'ddp' or 'dlogdp' — how data is normalised
weighting str 'n', 's', 'v', 'ext_in', 'ext_ex'

Modes used by mode_statistics(): Nucleation 10–25 nm, Aitken 25–100 nm, Accumulation 100–1000 nm, Coarse 1000–2500 nm (absent when out of range). to_extinction(RI, method=...) accepts 'internal', 'external', 'core_shell' (EC core, everything else as shell) and 'sensitivity'.

AeroViz.dataProcess.SizeDistr.SizeDist

SizeDist(data: DataFrame, state: Literal['dN', 'ddp', 'dlogdp'] = 'dlogdp', weighting: Literal['n', 's', 'v', 'ext_in', 'ext_ex'] = 'n')

A class representing particle size distribution data.

This class encapsulates particle size distribution data and provides convenient properties for accessing particle diameters, logarithmic bin widths, and distribution state information.

Attributes:

Name Type Description
_data DataFrame

The processed PSD data stored as a pandas DataFrame.

_dp ndarray

The array of particle diameters from the PSD data.

_dlogdp ndarray

The array of logarithmic particle diameter bin widths.

_index DatetimeIndex

The index of the DataFrame representing time.

_state str

The state of particle size distribution data ('dN', 'ddp', 'dlogdp').

_weighting str

The weighting type for distribution calculations.

Methods:

Name Description
data

Returns the size distribution DataFrame.

dp

Returns the particle diameter array.

dlogdp

Returns the logarithmic bin width array.

index

Returns the time index.

state

Returns the distribution state.

weighting

Returns the weighting type.

Examples:

>>> from pandas import read_csv
>>> df = read_csv('PNSD_dNdlogdp.csv', parse_dates=['Time'], index_col='Time')
>>> psd = SizeDist(df, state='dlogdp', weighting='n')
>>> print(psd.dp)

Initialize a SizeDist object.

Parameters:

Name Type Description Default
data DataFrame

The particle size distribution data with particle diameters as columns. Column names must be numeric diameter values in nm.

required
state (dN, ddp, dlogdp)

The state of the distribution data: - 'dN': Raw number concentration - 'ddp': dN/ddp normalized - 'dlogdp': dN/dlogdp normalized

'dN'
weighting (n, s, v, ext_in, ext_ex)

The weighting type for property calculations: - 'n': Number weighting - 's': Surface weighting - 'v': Volume weighting - 'ext_in': Internal extinction weighting - 'ext_ex': External extinction weighting

'n'

Raises:

Type Description
ValueError

If data is None or empty, or column names are not numeric.

TypeError

If data is not a DataFrame.

Methods:

to_surface

to_surface() -> DataFrame

Convert to surface area distribution.

Formula: dS/dlogDp = π * dp² * dN/dlogDp

Returns:

Type Description
DataFrame

Surface area distribution (nm² / cm³).

Examples:

>>> psd = SizeDist(df)
>>> surface = psd.to_surface()

to_volume

to_volume() -> DataFrame

Convert to volume distribution.

Formula: dV/dlogDp = (π/6) * dp³ * dN/dlogDp

Returns:

Type Description
DataFrame

Volume distribution (nm³ / cm³).

Examples:

>>> psd = SizeDist(df)
>>> volume = psd.to_volume()

to_extinction

to_extinction(RI: DataFrame, method: str = 'internal', result_type: str = 'extinction') -> DataFrame

Calculate extinction distribution using Mie theory.

Parameters:

Name Type Description Default
RI DataFrame

Refractive index data with n and k columns.

required
method (internal, external, core_shell, sensitivity)

Mixing method for Mie calculation.

'internal'
result_type (extinction, scattering, absorption)

Type of optical result.

'extinction'

Returns:

Type Description
DataFrame

Extinction distribution (Mm⁻¹).

Examples:

>>> psd = SizeDist(df)
>>> ext = psd.to_extinction(df_RI, method='internal')

to_dry

to_dry(df_gRH: DataFrame, uniform: bool = True) -> DataFrame

Convert ambient (wet) PSD to dry PSD.

Shrinks particles according to hygroscopic growth factor and redistributes concentrations to appropriate smaller diameter bins.

Parameters:

Name Type Description Default
df_gRH DataFrame

DataFrame with 'gRH' column (growth factor = Dp_wet / Dp_dry).

required
uniform bool

If True, apply uniform gRH across all sizes. If False, apply size-dependent gRH based on lognormal distribution.

True

Returns:

Type Description
DataFrame

Dry particle size distribution.

Examples:

>>> psd = SizeDist(df_pnsd)
>>> dry_psd = psd.to_dry(df_chem[['gRH']])

properties

properties() -> DataFrame

Calculate statistical properties of the distribution.

Returns:

Type Description
DataFrame

Properties including GMD, GSD, mode, and mode contributions.

Examples:

>>> psd = SizeDist(df)
>>> props = psd.properties()

mode_statistics

mode_statistics(unit: str = 'nm') -> dict

Calculate statistics for different size modes.

Computes number, surface, and volume distributions along with GMD, GSD, total, and mode for each size range.

Parameters:

Name Type Description Default
unit (nm, um)

Unit of particle diameter in the data.

'nm'

Returns:

Type Description
dict
  • 'number': Number distribution (dN)
  • 'number_norm': Normalized number distribution (dN/dlogDp)
  • 'surface': Surface area distribution
  • 'surface_norm': Normalized surface distribution
  • 'volume': Volume distribution
  • 'volume_norm': Normalized volume distribution
  • 'statistics': DataFrame with GMD, GSD, total, mode per size mode

Examples:

>>> psd = SizeDist(df)
>>> stats = psd.mode_statistics()
>>> stats['statistics']  # GMD, GSD for each mode

lung_deposition

lung_deposition(activity: str = 'light') -> dict

Calculate lung deposition using ICRP 66 model.

Based on the ICRP (International Commission on Radiological Protection) Human Respiratory Tract Model for particle deposition.

Parameters:

Name Type Description Default
activity (sleep, sitting, light, heavy)

Activity level affecting breathing pattern: - 'sleep': Sleeping (nasal, 7.5 L/min) - 'sitting': Sitting awake (nasal, 9 L/min) - 'light': Light exercise (nasal+oral, 25 L/min) - 'heavy': Heavy exercise (oral, 50 L/min)

'sleep'

Returns:

Type Description
dict
  • 'DF': Deposition fraction DataFrame (HA, TB, AL, Total)
  • 'deposited': Deposited number distribution
  • 'dose': Regional deposited dose (particles/cm³)
  • 'total_dose': Total deposited particles
Notes

Deposition regions: - HA (Head Airways): 頭部氣道 (鼻、咽、喉) - TB (Tracheobronchial): 氣管支氣管區 - AL (Alveolar): 肺泡區

References
  • ICRP Publication 66 (1994)
  • Hinds, W.C. (1999) Aerosol Technology

Examples:

>>> psd = SizeDist(df)
>>> lung = psd.lung_deposition(activity='light')
>>> lung['DF']        # Deposition fractions
>>> lung['dose']      # Regional dose