In [18]:
from atmPy import sizedistribution
from atmPy.instruments.DMA import smps
from atmPy.instruments.DMA import dma
import matplotlib.pyplot as plt
import atmPy.aerosol as aerosol
import atmPy.atmosphere as atm
import numpy as np
import pandas as pd

%matplotlib inline

Demonstration of DMA Calculations

The DMA is represented as a single object in the code. The object consists of three attributes: _ro, _ri and _l, the dimensions of the DMA (outer and inner radius - $r_o$ and $r_i$ as well as length $l$). These parameters are considered to be "private" and are defined by the children although the class can be used directly by setting these directly.

The DMA object contains one method: v2d. This method calculates the diameter of a size selected particle as a function of voltage, atmospheric conditions and flow rates.

def v2d(self, v, gas, qc, qm):
        """
        Find selected diameter at a given voltage.

        This function uses a Newton-Raphson root finder to solve for the 
        diameter of the particle.

        Parameters
        ----------
        v:      float
                Voltage in Volts
        gas:    gas object
                Carrier gas object for performing calculations
        qc:     float
                Input sheath flow in lpm
        qm:     float
                Output sheath flow in lpm

        Returns
        -------
        Diameter in nanometers

        """
        gamma = self._l/log(self._ro/self._ri)

        # Convert flow rates from lpm to m3/s
        qc = float(qc)/60*0.001
        qm = float(qm)/60*0.001

        # Central mobility
        zc = (qc+qm)/(4*pi*gamma*v)
        return newton(lambda d: z(d, gas, 1)-zc, 1, maxiter=1000)

This method uses a Newton-Raphson zero finding routine to determine the diameter associated with central mobility.

Using the DMA Class

The following code demonstrates how to use the DMA class that is found in the atmPy package.


In [21]:
# Instantiate a DMA object based on the NOAA wide DMA dimensions
noaa = dma.NoaaWide()

# We also need a gas object that will represent the carrier gas of interest: air
air = atm.Air()

# Set the conditions to something representative of the conditions in the Boulder DMA
air.t = 23
air.p = 820

d = noaa.v2d(50, air, 5, 5)

print('The diameter associated with a 5 lpm flow and 50 V is ' + str(d) + ' nm.')


The diameter associated with a 5 lpm flow and 50 V is 49.7500677677439 nm.

Generating a Simulation Size Distribution

In order to test the SMPS inversion, we need to simulate some type of size distribution

Generate a size distribution that we can use to test the retrieval of the SMPS data. We will put the geometric mean (dg) at 60 nm, close to the observed point during HAGiS.


In [2]:
sd = sizedistribution.simulate_sizedistribution(d=[10, 2000],
                                                ndp=250, 
                                                dg=60, 
                                                sg=0.2, 
                                                nmode=3000)
f,a = sd.plot()
a.set_xlim([10,1000])
a.grid()


C:\Anaconda3\lib\site-packages\numpy\core\_methods.py:59: RuntimeWarning: Mean of empty slice.
  warnings.warn("Mean of empty slice.", RuntimeWarning)

Now, we need to transform the size distribution so that it is what the SMPS might observe.


In [3]:
nd = sd.convert2numberconcentration()
f,a = nd.plot()


Now, we need to "correct" the size distribution so that it is what the SMPS would observe:

  1. Starting at the bottom-most bin, calculate the number of singly charged particles in the bin. This will be the base number of particles in the bin.
  2. Based on the total number of particles, calculate the number of multiply charged particles expected as well as the diameters where they would reside.
  3. If the diameter of the multiply charged particles is greater than the min, place that number in the nearest diameter bin.

In [4]:
# demonstrate charing efficiency on size distribution
f2 = [aerosol.ndistr(i,n=-2,t=20) for i in nd.bincenters]
f3 = [aerosol.ndistr(i,n=-3,t=20) for i in nd.bincenters]
f1 = [aerosol.ndistr(i,n=-1,t=20) for i in nd.bincenters]
fig,ax = plt.subplots()

ax.plot(nd.bincenters, f1, 'r', nd.bincenters, f2,'k',  nd.bincenters, f3, 'b')
ax.set_xscale('log')
ax.set_xlabel('Dp (nm)')
ax.set_ylabel('f')
ax.grid()
fig.tight_layout()
ax.set_title('Charging Efficiency vs. Diameter')


Out[4]:
<matplotlib.text.Text at 0xcf16f28>

In [5]:
# Create an air object of aerosl calculations...
air = atm.Air()
air.t = 20
air.p = 850

# Validate vs. Paul Barron's calculations; LOOKS GOOD
aerosol.z(1000, air, 1)


Out[5]:
1.0963556340099868e-09

In [6]:
# Define a function for finding the diameter
fmin = lambda dm: (np.abs(nd.bincenters - dm)).argmin()

xd = nd.copy()

for i,n in enumerate(xd.data.iloc[0,:].values):
    f1 = aerosol.ndistr(xd.bincenters[i],n=-1,t = air.t)
    f2 = aerosol.ndistr(xd.bincenters[i],n=-2,t = air.t)
    f3 = aerosol.ndistr(xd.bincenters[i],n=-3,t = air.t)
    
    d2 =aerosol.z2d(aerosol.z(xd.bincenters[i],air, n=2),air, n=1) 
    if d2>= xd.bins[0]:
        k = fmin(d2)
        if d2 > xd.bins[k]:
            xd.data.values[0,k] += f2*n
        else:
            xd.data.values[0,k-1] +=f2*n
    
    d3 =aerosol.z2d(aerosol.z(xd.bincenters[i],air, n=3),air, n=1) 
    if d3>= xd.bins[0]:
        k = fmin(d3)
        if d3 > xd.bins[k]:
            xd.data.values[0,k] += f3*n
        else:
            xd.data.values[0,k-1] +=f3*n
    
    xd.data.values[0,i] = f1*n   
    # print([i, xd.bincenters[i], fmin(d2), d2, fmin(d3), d3])

In [7]:
f,a = xd.plot()
a.grid()


So there is some funniness in the discreet nature of the problem - the reason the discreet peaks appear is that the algorithm is dumping multiples of multiples into a single bin. I have tried to address this by dumping the data into the bins with a fixed bin width (rather than searching for the nearest bin center), but to no avail. The problem is exacerbated by increasing the granularity of the problem (the opposite of what I thought might happen).

At this point, I will just move ahead and hope that I get this right...


In [8]:
# The SMPS routines need a mean data dataframe with the following entries
mean_data = {"Sh_Q_VLPM":3.9, "Aer_Temp_C": air.t, "Aer_Pres_PSI":air.p, "Aer_Q_VLPM":0.39
    }

# Convert the dict to a list and generate a dataframe
df = pd.DataFrame([mean_data])

In [9]:
# Create a new SMPS object using the dimensions of the NOAA Wide DMA...
tSMPS = smps.SMPS(dma.NoaaWide())

In [10]:
xd.data.iloc[0,:].values


Out[10]:
array([  1.62566215e-03,   1.99051983e-03,   2.43189139e-03,
         2.96457059e-03,   3.60593488e-03,   4.37633885e-03,
         5.29955619e-03,   6.40327396e-03,   7.71964302e-03,
         9.28588799e-03,   1.11449800e-02,   1.33463750e-02,
         1.59468199e-02,   1.90112269e-02,   2.26136188e-02,
         2.68381406e-02,   3.25820523e-02,   3.85593564e-02,
         4.55330638e-02,   5.36497564e-02,   6.30739938e-02,
         7.39898942e-02,   8.66027477e-02,   1.01140643e-01,
         1.17856084e-01,   1.37027566e-01,   1.58961083e-01,
         1.83991524e-01,   2.12483924e-01,   2.44834519e-01,
         2.81471555e-01,   3.22855804e-01,   3.69480724e-01,
         4.21872207e-01,   4.80587857e-01,   5.46215739e-01,
         6.19372530e-01,   7.00701029e-01,   7.90866965e-01,
         8.90555056e-01,   1.00046428e+00,   1.12130235e+00,
         1.25377929e+00,   1.39860029e+00,   1.55645759e+00,
         1.72802167e+00,   1.91393165e+00,   2.11478499e+00,
         2.33112657e+00,   2.56343735e+00,   2.81212255e+00,
         3.07749972e+00,   3.35978672e+00,   3.65908978e+00,
         3.97539200e+00,   4.30854235e+00,   4.65824546e+00,
         5.02405241e+00,   5.40535280e+00,   5.80136825e+00,
         6.21114763e+00,   6.63356412e+00,   7.06731445e+00,
         7.51092029e+00,   8.01851071e+00,   8.45441485e+00,
         8.92115729e+00,   9.39049024e+00,   9.86015457e+00,
         1.03277713e+01,   1.07908598e+01,   1.12468590e+01,
         1.16931494e+01,   1.21270777e+01,   1.25459829e+01,
         1.45036413e+01,   1.33684812e+01,   1.37227627e+01,
         1.40515513e+01,   1.43525924e+01,   1.46237913e+01,
         1.48632377e+01,   1.50692283e+01,   1.52402870e+01,
         1.55098508e+01,   1.54759498e+01,   1.55353591e+01,
         1.55564905e+01,   1.55392153e+01,   1.54836911e+01,
         1.53903591e+01,   1.52599382e+01,   1.50934160e+01,
         1.48920358e+01,   1.46572812e+01,   1.43908575e+01,
         1.40946714e+01,   1.37708077e+01,   1.35447497e+01,
         1.30447262e+01,   1.26514923e+01,   1.33413935e+01,
         1.17567388e+01,   1.13174273e+01,   1.08682104e+01,
         1.04116391e+01,   9.95020840e+00,   9.48633624e+00,
         9.09036164e+00,   8.55554925e+00,   8.09796582e+00,
         7.64650296e+00,   7.20294119e+00,   6.76890095e+00,
         6.34583710e+00,   5.93503558e+00,   5.53761232e+00,
         5.18239063e+00,   5.06225520e+00,   4.40462142e+00,
         4.07077135e+00,   3.75334446e+00,   3.45250825e+00,
         3.16830230e+00,   2.91155300e+00,   2.64802370e+00,
         2.41296045e+00,   2.19360444e+00,   1.98951488e+00,
         1.80018788e+00,   1.68522882e+00,   1.45942449e+00,
         1.30770991e+00,   1.17233432e+00,   1.04852677e+00,
         9.35613621e-01,   8.32920472e-01,   7.40911361e-01,
         6.55348308e-01,   5.79374946e-01,   5.22713760e-01,
         4.47925989e-01,   3.93675525e-01,   3.44457884e-01,
         3.01054007e-01,   2.62514283e-01,   2.28382586e-01,
         1.98232967e-01,   1.71765918e-01,   1.48307348e-01,
         1.29523728e-01,   1.09668395e-01,   9.41158926e-02,
         8.06119069e-02,   6.88378603e-02,   5.86744426e-02,
         4.98984035e-02,   4.23391522e-02,   3.61249908e-02,
         3.02230176e-02,   2.54731667e-02,   2.14216439e-02,
         1.79741932e-02,   1.50494088e-02,   1.25695349e-02,
         1.04763345e-02,   8.71233879e-03,   7.25858394e-03,
         5.97933382e-03,   4.94010183e-03,   4.07204930e-03,
         3.34938713e-03,   2.74894949e-03,   2.25122989e-03,
         1.83961625e-03,   1.50323920e-03,   1.21970290e-03,
         9.90264728e-04,   8.02257011e-04,   6.48550186e-04,
         5.23171966e-04,   4.21137411e-04,   3.38274437e-04,
         2.71144511e-04,   2.16878039e-04,   1.73275239e-04,
         1.37840862e-04,   1.09564365e-04,   8.69064368e-05,
         6.87932907e-05,   5.43416795e-05,   4.28377362e-05,
         3.37000104e-05,   2.64574060e-05,   2.07291684e-05,
         1.62151810e-05,   1.26460790e-05,   9.84846228e-06,
         7.65444133e-06,   5.93738300e-06,   4.59639278e-06,
         3.55127064e-06,   2.73841391e-06,   2.10749847e-06,
         1.61879637e-06,   1.24102023e-06,   9.49579183e-07,
         7.25192965e-07,   5.52853557e-07,   4.20536704e-07,
         3.19348329e-07,   2.42054685e-07,   1.83128148e-07,
         1.38291090e-07,   1.04240276e-07,   7.84303217e-08,
         5.71830511e-08,   4.25899617e-08,   3.16514361e-08,
         2.34706826e-08,   1.73650442e-08,   1.28204054e-08,
         9.44437529e-09,   6.94208891e-09,   5.09157791e-09,
         3.72614476e-09,   2.72089613e-09,   1.98248043e-09,
         1.44128657e-09,   1.04552886e-09,   7.56773157e-10,
         5.46561659e-10,   3.93872887e-10,   2.83214969e-10,
         2.03197901e-10,   1.45467111e-10,   1.03908882e-10,
         7.40597856e-11,   5.26688451e-11,   3.73737211e-11,
         2.64618196e-11,   1.86944844e-11,   1.31779328e-11,
         9.26873943e-12,   6.50478884e-12,   4.55495848e-12,
         3.18253993e-12,   2.21871251e-12,   1.54335357e-12])

In [11]:
dndlogdp = tSMPS.__fwhm__(xd.bincenters, xd.data.iloc[0,:].values, df)

In [12]:
dndlogdp[-1] = 0

In [13]:
xd.data.iloc[0,:].values


Out[13]:
array([  3.12245335e-02,   3.72944010e-02,   4.44492175e-02,
         5.28636823e-02,   6.27369656e-02,   7.42954837e-02,
         8.77958780e-02,   1.03528196e-01,   1.21819264e-01,
         1.43036246e-01,   1.67590364e-01,   1.95940761e-01,
         2.28598483e-01,   2.66130533e-01,   3.09163967e-01,
         3.58389969e-01,   4.14573214e-01,   4.78535287e-01,
         5.51187580e-01,   6.33516164e-01,   7.26588709e-01,
         8.31557756e-01,   9.49661725e-01,   1.08222674e+00,
         1.23066670e+00,   1.39648152e+00,   1.58125822e+00,
         1.78666518e+00,   2.01444920e+00,   2.26643132e+00,
         2.54139399e+00,   2.84894868e+00,   3.18492678e+00,
         3.55294822e+00,   3.95501530e+00,   4.39321519e+00,
         4.86954282e+00,   5.38604859e+00,   5.94460942e+00,
         6.54711523e+00,   7.19528910e+00,   7.89072816e+00,
         8.63502853e+00,   9.42933090e+00,   1.02747307e+01,
         1.11720450e+01,   1.21216351e+01,   1.34154442e+01,
         1.43203270e+01,   1.54259062e+01,   1.65813098e+01,
         1.77881422e+01,   1.90415678e+01,   2.03417396e+01,
         2.16827778e+01,   2.30652258e+01,   2.44838024e+01,
         2.59359786e+01,   2.74181859e+01,   2.89200242e+01,
         3.04418450e+01,   3.19763990e+01,   3.35174753e+01,
         3.50640233e+01,   2.82989783e+01,   3.43302607e+01,
         3.60866698e+01,   3.78299087e+01,   3.94842896e+01,
         4.11170750e+01,   4.26704704e+01,   4.42039294e+01,
         4.56384522e+01,   4.70018247e+01,   4.82627456e+01,
         4.94040819e+01,   5.05062266e+01,   5.14757269e+01,
         5.23326318e+01,   5.30750085e+01,   5.36333626e+01,
         5.42048111e+01,   5.45770718e+01,   5.48324033e+01,
         5.47542415e+01,   5.50335741e+01,   5.48983659e+01,
         5.46417061e+01,   5.42555142e+01,   5.37595284e+01,
         5.30968518e+01,   5.24254247e+01,   5.15969795e+01,
         5.06692561e+01,   4.96495785e+01,   4.85401894e+01,
         4.73543690e+01,   4.60955207e+01,   4.51114831e+01,
         4.33643709e+01,   4.19623991e+01,   4.04682117e+01,
         3.89846866e+01,   3.74539756e+01,   3.59052160e+01,
         3.43341248e+01,   3.27860779e+01,   3.12276516e+01,
         2.99009141e+01,   2.81332234e+01,   2.66228963e+01,
         2.51397528e+01,   2.36836580e+01,   2.22736759e+01,
         2.08979779e+01,   1.95653474e+01,   1.82785491e+01,
         1.71349326e+01,   1.58387558e+01,   1.47058779e+01,
         1.36217076e+01,   1.25905619e+01,   1.16120228e+01,
         1.06880153e+01,   9.85403804e+00,   8.99113956e+00,
         8.22242248e+00,   7.50341811e+00,   6.83252438e+00,
         6.20873867e+00,   5.62960359e+00,   5.10651381e+00,
         4.59726328e+00,   4.14205869e+00,   3.72392667e+00,
         3.34087732e+00,   2.99084871e+00,   2.67597250e+00,
         2.38102632e+00,   2.11799893e+00,   1.88000986e+00,
         1.66521770e+00,   1.47329163e+00,   1.29786631e+00,
         1.14226288e+00,   1.00317371e+00,   8.79144267e-01,
         7.68807543e-01,   6.71270620e-01,   5.84119290e-01,
         5.07555025e-01,   4.40087268e-01,   3.80774679e-01,
         3.28863407e-01,   2.83212486e-01,   2.43480161e-01,
         2.08875790e-01,   1.78807587e-01,   1.52770377e-01,
         1.30190955e-01,   1.10738743e-01,   9.39921339e-02,
         7.96079708e-02,   6.72883030e-02,   5.67405987e-02,
         4.77506158e-02,   4.00993201e-02,   3.36022249e-02,
         2.80977833e-02,   2.34461225e-02,   1.95205783e-02,
         1.62186997e-02,   1.34465936e-02,   1.11245240e-02,
         9.18382279e-03,   7.56508795e-03,   6.21884311e-03,
         5.10119287e-03,   4.17548231e-03,   3.41047064e-03,
         2.77967966e-03,   2.26071811e-03,   1.83473984e-03,
         1.48584397e-03,   1.20072816e-03,   9.68253257e-04,
         7.79123042e-04,   6.25598109e-04,   5.01255054e-04,
         4.00764964e-04,   3.19742651e-04,   2.54556340e-04,
         2.02227313e-04,   1.60312833e-04,   1.26814626e-04,
         1.00103588e-04,   7.88475639e-05,   6.19736161e-05,
         4.86069191e-05,   3.80418879e-05,   2.97097330e-05,
         2.31530477e-05,   1.80048841e-05,   1.39715726e-05,
         1.08186440e-05,   8.35936378e-06,   6.44534822e-06,
         4.95897955e-06,   3.80731991e-06,   2.91675434e-06,
         2.22979083e-06,   1.70098781e-06,   1.29482506e-06,
         9.83544053e-07,   7.45502980e-07,   5.63868493e-07,
         4.22534339e-03,   2.91572631e-03,   1.99389665e-03,
         1.35086973e-03,   9.06423442e-04,   6.02230882e-04,
         3.96058376e-04,   2.57738645e-04,   1.65911928e-04,
         1.05608808e-04,   6.64486779e-05,   4.13111381e-05,
         2.53667179e-05,   1.53776154e-05,   9.19900284e-06,
         5.42756864e-06,   3.15683935e-06,   1.80897665e-06,
         1.02064438e-06,   5.66599453e-07,   3.09244026e-07,
         1.65793172e-07,   8.72221571e-08,   4.49729512e-08,
         2.26928180e-08,   1.11842096e-08,   5.37014411e-09,
         2.50288042e-09,   1.12593745e-09,   4.84161375e-10,
         1.95145932e-10,   6.99935501e-11,   1.69453298e-11])

In [14]:
xd.plot()


Out[14]:
(<matplotlib.figure.Figure at 0xce587b8>,
 <matplotlib.axes._subplots.AxesSubplot at 0xcd0a5c0>)

In [15]:
fig, ax= plt.subplots()
ax.plot(tSMPS.diam_interp, dndlogdp)
ax.set_xscale('log')
ax.grid()



In [16]:
noaa = dma.NoaaWide()
air = atm.Air()

In [17]:
air.t = 25
air.p = 820
noaa.v2d(37.2, air, 3.7, 3.7)


Out[17]:
49.752418885513265

In [ ]: