<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Using OPCSIM to Simulate a Nephelometer
This section of the tutorial will walk you through how we model Nephelometers, how you can build/model a Nephelometer, and how we can evaluate Nephelometers across a wide range of conditions using this tool.
Step1:  Nephelometer Representation
In OPCSIM, we define a Nephelometer using two parameters
Step2:  Calibration
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
Step3:  We can explore the calibration factors that were just determined - the units are a bit arbitrary, since we don't consider the intensity/power of the laser as we assume it is constant. Thus, these units are something like $cm^2/(\mu g/ m^3)$
Step4:  Similarly, we get ratio's for $PM_{2.5}$ and $PM_{10}$
Step5:  Evaluating a Nephelometer for New Aerosol Distributions
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}$.
Step6:  What if we went ahead and tried to evaluate on a totally unseen distribution? Let's go ahead and evaluate on an urban distribution
Step7:  First, let's determine the actual $PM_1$, $PM_{2.5}$, and $PM_{10}$ loadings for this distribution
Step8:  Next, let's evaluate the Nephelometer
<ASSISTANT_TASK:>
Python Code:
# 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})
Explanation: Using OPCSIM to Simulate a Nephelometer
This section of the tutorial will walk you through how we model Nephelometers, how you can build/model a Nephelometer, and how we can evaluate Nephelometers across a wide range of conditions using this tool.
End of explanation
# 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
Explanation: Nephelometer Representation
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:
End of explanation
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.)
Explanation: Calibration
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:
End of explanation
neph.pm1_ratio
Explanation: We can explore the calibration factors that were just determined - the units are a bit arbitrary, since we don't consider the intensity/power of the laser as we assume it is constant. Thus, these units are something like $cm^2/(\mu g/ m^3)$
End of explanation
neph.pm25_ratio
neph.pm10_ratio
Explanation: Similarly, we get ratio's for $PM_{2.5}$ and $PM_{10}$:
End of explanation
# evaluate the same distribution we used to calibrate
neph.evaluate(d1, rh=0.)
# evaluate the same distribution we used to calibrate, but at a higher RH
neph.evaluate(d1, rh=85.0)
Explanation: Evaluating a Nephelometer for New Aerosol Distributions
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}$.
End of explanation
d2 = opcsim.load_distribution("urban")
d2
Explanation: What if we went ahead and tried to evaluate on a totally unseen distribution? Let's go ahead and evaluate on an urban distribution:
End of explanation
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)))
Explanation: First, let's determine the actual $PM_1$, $PM_{2.5}$, and $PM_{10}$ loadings for this distribution:
End of explanation
neph.evaluate(d2, rh=0.)
Explanation: Next, let's evaluate the Nephelometer:
End of explanation
<END_TASK>