Inverse Models

Size Distribution Inversion

Theory

The size distribution can be found from the inverse of the response function using Tikhonov regularization.

$L_1$ and $L_2$ are the Euclidian norms of the solution and the intial guess.

$L_1 = \left\lVert \bf{A}\rm{π•Ÿ} - \rm{𝕣}\right\rVert_2$

$L_2 = \left\lVert\bf{L}(\rm{π•Ÿ} - \rm{π•Ÿ_i})\right\rVert_2$

where $\rm{π•Ÿ}$ is the true size distribution, $\bf{A}$ is the convolution matrix, 𝕣 is the measured response vector, $\bf{L}$ is a weights matrix, and $\rm{π•Ÿ_i}$ is an initial guess. To solve for the inverse, a balance is sought between the $L_1$ and $L_2$ norms.

$π•Ÿ_{inv} = \arg \min\{L_1^2 + \lambda^2 L_2^2\}$

where $\lambda$ is the regularization parameter. Taking $\bf{L} = \bf{I}$ (weight matrix equals the identity Matrix 𝐈) and $\rm{π•Ÿ_i} = \bf{S}^{-1}\rm{𝕣}$ as initial guess (Matrix 𝐒), the regularized inverse is computed via:

$π•Ÿ_{inv} = (\bf{A}^{\rm{T}}\bf{A} + \lambda^{\rm{2}} \bf{I})^{\rm{-1}}(\bf{A}^{\rm{T}} \rm{𝕣} + \lambda^2\bf{S}^{-1} \rm{𝕣})$

The regularization parameter $\lambda$ "interpolates" between the noisy least-square inverse ($\lambda = 0$) and the initial guess ($\lambda >> 1$). The optimal regularization parameter is found using the L-curve method. The optimal $\lambda_{opt}$ is found using the L-curve method. The L-curve is defined as a plot of $\log(L_1)$ vs. $\log(L_2)$, where $L_1$ and $L_2$ are obtained for a series of discrete $\lambda$ values. The $\lambda_{opt}$ is found at the corner of the L-curve, which is mathematically defined as the point where where the curvature of the L-curve is maximum. Here, the corner of the L-curve is found using the iterative algorithm described in Talukdar and Swihart (2003). The curvature is calculated using Eq. (14) in Hansen (2000), which requires the first and second derivatives of $d\ln(L_i)^2/d\lambda$. These derivatives of $d\ln(L_i)^2/d\lambda$ are estimated numerically.

See session 3 of the Tutorial for an interactive explanation on how Tikhonov regularization works.

Example

The L-curve search method is implemented in the rinv function. Also check out the new rinv2 function, which has much faster performance and is based on generalized cross validation. It also allows to specify higher order inversions. The default implementation gives near identical results to rinv.

π•Ÿβ±βΏα΅› = rinv(𝕣.N, Ξ΄, λ₁=0.1, Ξ»β‚‚=1.0);

The function takes the response vector to be inverted, the DMA grid to read the matrices, and an upper and lower search bound for the optimal regularization parameter.

df = CSV.read("example_data.csv", DataFrame)

t, p, lpm = 293.15, 940e2, 1.666e-5
r₁, rβ‚‚, l = 9.37e-3,1.961e-2,0.44369
Ξ› = DMAconfig(t,p,1lpm,4lpm,r₁,rβ‚‚,l,0.0,:+,6,:cylindrical)
Ξ΄ = setupDMA(Ξ›, vtoz(Ξ›,10000), vtoz(Ξ›,10), 120)

𝕣 = (df,:Dp,:Rcn,Ξ΄) |> interpolateDataFrameOntoΞ΄
π•Ÿβ±βΏα΅› = rinv(𝕣.N, Ξ΄, λ₁=0.1, Ξ»β‚‚=1.0)
π•Ÿβ±βΏα΅›Β² = rinv2(𝕣.N, Ξ΄, λ₁=0.1, Ξ»β‚‚=1.0)
Particle diameter (nm) 10 100 500 π•Ÿβ±βΏα΅› π•Ÿβ±βΏα΅›Β² h,j,k,l,arrows,drag to pan i,o,+,-,scroll,shift-drag to zoom r,dbl-click to reset c for coordinates ? for help ? -1000 -800 -600 -400 -200 0 200 400 600 800 1000 1200 1400 1600 1800 -700 -650 -600 -550 -500 -450 -400 -350 -300 -250 -200 -150 -100 -50 0 50 100 150 200 250 300 350 400 450 500 550 600 650 700 750 800 850 900 950 1000 1050 1100 1150 1200 1250 1300 1350 -1000 0 1000 2000 -660 -640 -620 -600 -580 -560 -540 -520 -500 -480 -460 -440 -420 -400 -380 -360 -340 -320 -300 -280 -260 -240 -220 -200 -180 -160 -140 -120 -100 -80 -60 -40 -20 0 20 40 60 80 100 120 140 160 180 200 220 240 260 280 300 320 340 360 380 400 420 440 460 480 500 520 540 560 580 600 620 640 660 680 700 720 740 760 780 800 820 840 860 880 900 920 940 960 980 1000 1020 1040 1060 1080 1100 1120 1140 1160 1180 1200 1220 1240 1260 1280 1300 1320 dN/dlnD (cm-3) Inverted Size Distribution Particle diameter (nm) 10 100 500 𝕣 h,j,k,l,arrows,drag to pan i,o,+,-,scroll,shift-drag to zoom r,dbl-click to reset c for coordinates ? for help ? -40 -30 -20 -10 0 10 20 30 40 50 60 70 -30 -28 -26 -24 -22 -20 -18 -16 -14 -12 -10 -8 -6 -4 -2 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 52 54 56 58 60 -30 0 30 60 -30 -29 -28 -27 -26 -25 -24 -23 -22 -21 -20 -19 -18 -17 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 N (cm-3) Raw Response Function

The file example_data.csv is a comma delimited text files that contains diameter and a resonse vector. The function interpolateDataFrameOntoΞ΄ takes the columns :Dp and :Rcn from the DataFrame df and creates a response distribution of the type SizeDistribution that matches the DMA grid Ξ΄. Finally, π•Ÿβ±βΏα΅› is computed using the rinv function.

The method was compared to the inverted size distribution given by the manufacturer Aerosol Instrument Manager (AIM) software, assuming no diffusion correction in either method. Figure 3 in the manuscript shows excellent correspondence between this package and the commercial software.

CCN Inversion

Size resolved CCN measurements have a long history (e.g. Cruz and Pandis, 1997, Snider et al., 2006). Particles are typically dried, charge neutralized, and passed through the DMA.At the exit, the flow is split between a CPC that measures all particles and a CCN counter that measures particles that form cloud droplets at a water supersaturation set by the instrument. In this configuration the DMA voltage can either be stepped or continuously scanned. The ratio of CCN-to-CPC response function is used to determine the fraction of particles that activate at a given diameter. The diameter where 50% of the particles activate is taken to be the activation diameter. The activation of particles into cloud droplets is proportional to the volume of solute in the particle. Therefore, larger multiply charged particles activate first. This leads to a bias in the inferred D50 diameter if the activated fraction is computed from the ratio of the raw response functions (Petters et al., 2007, 2009).

The CCN transmission function is modeled using a cumulative Gauss integral

$T_{af} = \frac{1}{2}\left[1 + \mathrm{erf}\left(\frac{d-\mu}{\sigma}\right) \right]$

This function is applied to the mobility size distribution. Then the response function is computed. Empirically, activated fraction can be computed using from the ratio of size distributions and response functions.

To predict the ratio of π•£αΆœαΆœβΏ/π•£αΆœβΏ in terms of the activated fraction model Taf:

$π•Ÿ^{\mathrm{cn}} = (\bf{A}^{\rm{T}}\bf{A} + \lambda^{\rm{2}} \bf{I})^{\rm{-1}}(\bf{A}^{\rm{T}} \rm{𝕣^{\mathrm{cn}}} + \lambda^2 \bf{S}^{-1} \rm{𝕣^{\mathrm{cn}}})$

$\left( \frac{𝕣^{\mathrm{ccn}}}{𝕣^{\mathrm{cn}}} \right)_{\mathrm{model}} = \frac{\mathbf{A}[T_{af}\; .*\; π•Ÿ^{\mathrm{cn}}]}{\mathbf{A}π•Ÿ^{\mathrm{cn}}}$

This mirrors the approach in Petters et al. (2007). The model + fit can be solved using this package as follows.

π•ŸαΆœβΏ = (𝐀'𝐀 + Ξ»^2𝐈)^(-1) * (𝐀'π•£αΆœβΏ  + Ξ»^2 * 𝐒^(-1)*π•£αΆœβΏ)
model(x,p) = (Ξ΄.𝐀*(π•ŸαΆœβΏ.N.*Taf(π•ŸαΆœβΏ, p[1], p[2])))./(Ξ΄.𝐀*π•ŸαΆœβΏ.N)
fit = curve_fit(model, Ξ΄.Dp, π•£αΆœαΆœβΏ.N./π•£αΆœβΏ.N, [65.0, 3.0])
Note

The example below includes a hidden threholding step. (See complete example in the examples folder) Thresholding is performed for bins with low counts, which results in NaN and Inf in 𝕒𝕗. This is necessary because at low concentrations a bin may have zero or very low concentration, resulting in unrealistic or NaN/InF values in the activated fraction array. Thus if counts/concentration is below the threshold, the activated fraction is forced to one for large diameters and zero for small diameters. The noise threshold may need to be adjusted for different datasets.

The code loads the CCN (π•£αΆœαΆœβΏ) and CN (π•£αΆœβΏ) response functions from file. The activated fraction is computed through the ratio of the distributions using one of the SizeDistribution Number Operators.

𝕒𝕗 = π•£αΆœαΆœβΏ/π•£αΆœβΏ

And here is the complete example showing how to fit the 𝕒𝕗 response curve.

t, p, lpm = 293.15, 940e2, 1.666e-5
r₁, rβ‚‚, l = 9.37e-3,1.961e-2,0.44369
Ξ› = DMAconfig(t,p,1lpm,4lpm,r₁,rβ‚‚,l,0.0,:+,6,:cylindrical)
Ξ΄ = setupDMA(Ξ›, vtoz(Ξ›,10000), vtoz(Ξ›,10), 120)

# Load a simple comma delimited text file - file contains :Dp, :Rcn, :Rccn
df = CSV.read("example_data.csv", DataFrame)
π•£αΆœβΏ = (df,:Dp,:Rcn,Ξ΄) |> interpolateDataFrameOntoΞ΄        # CN response distribution
π•£αΆœαΆœβΏ = (df,:Dp,:Rccn,Ξ΄) |> interpolateDataFrameOntoΞ΄;     # CCN response distribution
𝕒𝕗 = π•£αΆœαΆœβΏ/π•£αΆœβΏ
Taf(π•Ÿ,ΞΌ,Οƒ) = @. 0.5 * (1.0 + erf((π•Ÿ.Dp - ΞΌ)./(sqrt(2.0Οƒ))))
𝐈, 𝐒, 𝐀, Ξ» =  Ξ΄.𝐈, Ξ΄.𝐒, Ξ΄.𝐀, 0.5
π•ŸαΆœβΏ = (𝐀'𝐀 + Ξ»^2𝐈)^(-1) * (𝐀'π•£αΆœβΏ  + Ξ»^2 * 𝐒^(-1)*π•£αΆœβΏ)
model(x,p) = (𝐀 * (π•ŸαΆœβΏ.N .* Taf(π•ŸαΆœβΏ, p[1], p[2])))./( 𝐀 * π•ŸαΆœβΏ.N)
fit = curve_fit(model, 𝕒𝕗.Dp, 𝕒𝕗.N, [65.0, 3.0])
Ax = fit.param
afmodel = model(Ξ΄.Dp, Ax)
Apparent +1 Mobility Diameter (nm) 10 100 500 𝕒𝕗 (data) 𝕒𝕗 (model) h,j,k,l,arrows,drag to pan i,o,+,-,scroll,shift-drag to zoom r,dbl-click to reset c for coordinates ? for help ? 0.0 0.2 0.4 0.6 0.8 1.0 1.2 Fraction (-) Activated Fraction Apparent +1 Mobility Diameter (nm) 10 100 500 π•£αΆœβΏ π•£αΆœαΆœβΏ h,j,k,l,arrows,drag to pan i,o,+,-,scroll,shift-drag to zoom r,dbl-click to reset c for coordinates ? for help ? -1500 -1000 -500 0 500 1000 1500 2000 2500 -1000 -900 -800 -700 -600 -500 -400 -300 -200 -100 0 100 200 300 400 500 600 700 800 900 1000 1100 1200 1300 1400 1500 1600 1700 1800 1900 -1000 0 1000 2000 -950 -900 -850 -800 -750 -700 -650 -600 -550 -500 -450 -400 -350 -300 -250 -200 -150 -100 -50 0 50 100 150 200 250 300 350 400 450 500 550 600 650 700 750 800 850 900 950 1000 1050 1100 1150 1200 1250 1300 1350 1400 1450 1500 1550 1600 1650 1700 1750 1800 1850 dN/dlnD (cm-3) Raw response function

Tandem DMA Inversion

A complete example for tandem DMA inversion is provided in inversion3.jl in the examples folder, which reproduces Figure 3 in Petters (2021).

Briefly, the code for inversion contains these key features:

Λ₁, Ξ›β‚‚, δ₁, Ξ΄β‚‚ = initializeDMAs(Dd, k)
Ax = [[1300.0, 60.0, 1.4], [2000.0, 200.0, 1.6]]
π•ŸαΆœβΏ = DMALognormalDistribution(Ax, δ₁)
gf, ge, 𝐀 = TDMAmatrix(π•ŸαΆœβΏ, Dd, Λ₁, Ξ›β‚‚, Ξ΄β‚‚, k)
model = TDMA1Dpdf(π•ŸαΆœβΏ, Λ₁, Ξ›β‚‚, (Dd, 0.8, 5.0, k));

The initializeDMAs sets up the grids for DMA 1 and 2, Ax are the parameters for the assumed size distribution, π•ŸαΆœβΏ is the instantiation of that size distribution. gf, ge, and 𝐀 are the growth factor bin midpoints, growth factor bin edges and the convolution matrix, and model is the forward model function.

The code below is to invert the data. xβ‚€ is the initial guess, lb,ub the lower and ope bounds, and xΞ»1 and xΞ»2 are the inverted solutions for the two main methods stated in the manuscript.

xβ‚€ = N1./sum(N1)
lb, ub = zeros(k), ones(k)
xΞ»1 = invert(𝐀, N1, Lβ‚–xβ‚€B(2, xβ‚€, lb, ub))
e1 = @> sqrt.(sum((xΞ»1 .- f).^2.0)./k) round(digits = 3)
xΞ»2 = invert(𝐀, N1, Lβ‚–Dβ‚“B(0, 0.001, lb, ub))
e2 = @> sqrt.(sum((xΞ»2 .- f).^2.0)./k) round(digits = 3)

f03.svg