Skip to content

Size Distribution Analysis

A complete size distribution processing workflow, including SMPS-APS merging, distribution conversion, mode statistics, and lung deposition calculation.

Data Preparation

Reading SMPS and APS Data

from datetime import datetime
from pathlib import Path
from AeroViz import RawDataReader

# Read SMPS (10-600 nm)
smps = RawDataReader(
    instrument='SMPS',
    path=Path('/path/to/smps'),
    start=datetime(2024, 1, 1),
    end=datetime(2024, 3, 31),
    mean_freq='1h'
)

# Read APS (0.5-20 um)
aps = RawDataReader(
    instrument='APS',
    path=Path('/path/to/aps'),
    start=datetime(2024, 1, 1),
    end=datetime(2024, 3, 31),
    mean_freq='1h'
)

SMPS-APS Merging

Basic Merging

from AeroViz.dataProcess import DataProcess

dp = DataProcess('SizeDistr', Path('./output'))

# v4 merging (recommended, with PM2.5 correction)
result = dp.merge_SMPS_APS_v4(
    df_smps=smps,
    df_aps=aps,
    df_pm25=pm25_data  # Optional, for density correction
)

# Output
merged_pnsd = result['data_dn']       # Merged dN
merged_dndsdv = result['data_dndsdv'] # dN, dS, dV distributions
density = result['density']           # Estimated particle density

Merging Parameter Description

Parameter Description
overlap_range SMPS-APS overlap range (default 500-700 nm)
shift_mode APS diameter shift mode
density_range Density search range (default 1.0-2.5 g/cm3)

Distribution Conversion and Statistics

Using SizeDist Class

from AeroViz.dataProcess.SizeDistr import SizeDist

# Create PSD object
psd = SizeDist(merged_pnsd, state='dlogdp', weighting='n')

# Distribution conversion
surface = psd.to_surface()  # Surface area distribution (nm2/cm3)
volume = psd.to_volume()    # Volume distribution (nm3/cm3)

# Basic statistics
props = psd.properties()
print(f"Total N: {props['total_n']:.0f} #/cm3")
print(f"GMD: {props['GMD_n']:.1f} nm")
print(f"GSD: {props['GSD_n']:.2f}")

Mode Statistics

# Calculate mode statistics
stats = psd.mode_statistics()

# Mode distributions
nucleation = stats['number']['Nucleation']   # 10-25 nm
aitken = stats['number']['Aitken']           # 25-100 nm
accumulation = stats['number']['Accumulation']  # 100-1000 nm
coarse = stats['number']['Coarse']           # 1000-2500 nm

# Statistical summary
summary = stats['statistics']
print(summary)
#                 GMD    GSD   total    mode
# Nucleation     18.2   1.45    2500    17.5
# Aitken         52.3   1.62    5200    48.0
# Accumulation  185.0   1.85    1800   160.0
# Coarse        1850    2.10     120  1500.0

Extinction Distribution Calculation

Requires Refractive Index Data

# Calculate refractive index from chemical composition
dp_chem = DataProcess('Chemistry', Path('./output'))
ri_result = dp_chem.volume_RI(df_chem)
df_RI = ri_result['RI']  # n, k columns

# Calculate extinction distribution
ext_dist = psd.to_extinction(
    RI=df_RI,
    method='internal',      # Mixing mode
    result_type='extinction'
)

Dry PSD Calculation

Hygroscopic Correction

# Calculate growth factor
kappa_result = dp_chem.kappa(df_chem, df_RH)
df_gRH = kappa_result[['gRH']]

# Convert to dry PSD
dry_psd = psd.to_dry(df_gRH, uniform=True)

Lung Deposition Calculation

ICRP 66 Model

# Calculate lung deposition
lung = psd.lung_deposition(activity='light')

# Deposition fractions
df_fraction = lung['DF']
print(df_fraction.mean())
#    HA     TB     AL    Total
# 0.025  0.082  0.245    0.352

# Regional dose
dose = lung['dose']
print(f"Alveolar dose: {dose['AL'].mean():.0f} #/cm3")

# Deposited distribution
deposited = lung['deposited']  # Number deposited at each size

Activity Level Comparison

activities = ['sleep', 'sitting', 'light', 'heavy']
results = {}

for act in activities:
    result = psd.lung_deposition(activity=act)
    results[act] = result['total_dose'].mean()

print("Total deposition by activity:")
for act, dose in results.items():
    print(f"  {act}: {dose:.0f} #/cm3")

Complete Example Script

from datetime import datetime
from pathlib import Path
from AeroViz import RawDataReader
from AeroViz.dataProcess import DataProcess
from AeroViz.dataProcess.SizeDistr import SizeDist

# 1. Read data
smps = RawDataReader('SMPS', Path('./data/smps'),
                     datetime(2024,1,1), datetime(2024,3,31))
aps = RawDataReader('APS', Path('./data/aps'),
                    datetime(2024,1,1), datetime(2024,3,31))

# 2. Merge SMPS-APS
dp = DataProcess('SizeDistr', Path('./output'))
merged = dp.merge_SMPS_APS_v4(smps, aps)
df_pnsd = merged['data_dn']

# 3. Create SizeDist object
psd = SizeDist(df_pnsd, state='dlogdp', weighting='n')

# 4. Distribution conversion
surface = psd.to_surface()
volume = psd.to_volume()

# 5. Statistical analysis
props = psd.properties()
stats = psd.mode_statistics()

# 6. Lung deposition
lung = psd.lung_deposition(activity='light')

# 7. Output results
print(f"Average total N: {props['total_n'].mean():.0f} #/cm3")
print(f"Average GMD: {props['GMD_n'].mean():.1f} nm")
print(f"Average lung deposition: {lung['total_dose'].mean():.0f} #/cm3")