.. _nephelometer_tutorial:

```
In [ ]:
```# Make imports
import opcsim
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as mticks
import seaborn as sns
%matplotlib inline
# turn off warnings temporarily
import warnings
warnings.simplefilter('ignore')
# Let's set some default seaborn settings
sns.set(context='notebook', style='ticks', palette='dark', font_scale=1.75,
rc={'figure.figsize': (12,6), **opcsim.plots.rc_log})

In OPCSIM, we define a Nephelometer using two parameters: the wavelength of light used in the device and its viewing angle. Unlike photometers and some optical particle counters, most low-cost commercial nephelometers gather light across as wide a range of angles as possible. This minimizes some of the uncertainty associated with the Mie resonance and allows manufacturers to use cheap photo-detectors while still gathering enough signal to distinguish from noise.

To build a Nephelometer, simply initialize using the `opcsim.Nephelometer`

class:

```
In [ ]:
```# init a nephelometer with a 658 nm laser, gathering light from between 7-173 degrees
neph = opcsim.Nephelometer(wl=0.658, theta=(7., 173))
neph

Nephelometers gather the total scattered light from many anglees across an entire aerosol distribution. Typically, users of low-cost nephelometers co-locate their device with a reference device of higher (or known) quality and simply compare the output signal from the nephelometer to the integrated mass value (i.e. $PM_1$, $PM_{2.5}$, or $PM_{10}$) from the reference device. To keep things as simple and realistic as possible, we follow this approach.

To calibrate a nephelometer in OPCSIM, you provide an aerosol distribution to the `calibrate`

method - the actual mass values for $PM_1$, $PM_{2.5}$, and $PM_{10}$ are calculated exactly and the total scattered light is computed as well. The ratio between the total scattered light and each of the mass loadings are stored as calibration factors and are used again when evaluating previously unseen distributions.

To calibrate our nephelometer above to a synthetic distribution of Ammonium Sulfate:

```
In [ ]:
```d1 = opcsim.AerosolDistribution("AmmSulf")
d1.add_mode(n=1e4, gm=125e-3, gsd=1.5, refr=complex(1.521, 0), kappa=0.53, rho=1.77)
# calibrate the nephelometer at 0% RH
neph.calibrate(d1, rh=0.)

```
In [ ]:
```neph.pm1_ratio

Similarly, we get ratio's for $PM_{2.5}$ and $PM_{10}$:

```
In [ ]:
```neph.pm25_ratio

```
In [ ]:
```neph.pm10_ratio

The entire point of this tool is to be able to simulate what would happen under different circumstances. To do so, we use the `evaluate`

method, which takes an AerosolDistribution as an argument (as well as an optional relative humidity) and returns the total scattered light, $PM_1$, $PM_{2.5}$, and $PM_{10}$.

```
In [ ]:
```# evaluate the same distribution we used to calibrate
neph.evaluate(d1, rh=0.)

```
In [ ]:
```# evaluate the same distribution we used to calibrate, but at a higher RH
neph.evaluate(d1, rh=85.0)

**urban** distribution:

```
In [ ]:
```d2 = opcsim.load_distribution("urban")
d2

First, let's determine the actual $PM_1$, $PM_{2.5}$, and $PM_{10}$ loadings for this distribution:

```
In [ ]:
```print ("PM1 = {:.2f} ug/m3".format(d2.cdf(dmin=0., dmax=1., weight='mass', rho=1.65)))
print ("PM2.5 = {:.2f} ug/m3".format(d2.cdf(dmin=0., dmax=2.5, weight='mass', rho=1.65)))
print ("PM10 = {:.2f} ug/m3".format(d2.cdf(dmin=0., dmax=10., weight='mass', rho=1.65)))

Next, let's evaluate the Nephelometer:

```
In [ ]:
```neph.evaluate(d2, rh=0.)

```
In [ ]:
```