示例#1
0
        'red': [6, 7, 8],
        'orange': [9, 10, 11]
    }
    month_ls = {'-': [1, 4, 7, 10], '--': [2, 5, 8, 11], '-.': [12, 3, 6, 9]}

    year = 2015

    # ---------------------------------------------------
    # Read, Process and save f(RH)
    # ---------------------------------------------------

    # RH
    RH_paths = [
        datadir + 'WXT_KSSW_' + y + '_15min.nc' for y in ['2014', '2015']
    ]
    RH_obs = eu.netCDF_read(RH_paths)

    # N(D) #use the Dn_accum.
    N_path = N_dir + 'accum_Ntot_Dn_NK_SMPS_' + str(year) + '.npy'
    N = np.load(N_path).flat[0]
    r_accum_m = N['Dv_accum'] / 2 / 1e9  # conv. to [m] from [nm]

    # f(RH) read in
    f_RH_data = {}
    rel_vol_species = {}
    rel_vol_time = {}

    for site in ['NK', 'Ch', 'Ha']:

        # read in f(RH) LUTs for each site
        path = fRHdir + dataRes + '_f(RH)_' + site + '_905.0nm.nc'
def read_wxt_obs(years, time):

    """
    Read in RH observations from KSSW, time match them to the model data, and extend them in height to match the
    dimensions of model RH
    :param years:
    :param time:
    :return wxt_obs:
    """

    met_vars = ['RH', 'Tair', 'press']
    vars = met_vars + ['time']
    filepath = ['C:/Users/Elliott/Documents/PhD Reading/PhD Research/Aerosol Backscatter/MorningBL/data/L1/' + \
               'Davis_BGH_' + str(i) + '_15min.nc' for i in years]
    wxt_obs_raw = eu.netCDF_read(filepath, vars=vars)


    # set up array to be filled
    wxt_obs = {}
    for met_var in met_vars:
        wxt_obs[met_var] = np.empty(len(time))
        wxt_obs[met_var][:] = np.nan
    wxt_obs['time'] = time

    # find data region and create an average if appropriate
    print_step = range(1000,20000, 1000)
    for t, time_t in enumerate(time):

        if t in print_step:
            print 't ='+str(t)

        # time t-1 (start of original time period, as all data is relevent for time ENDING at time_t)
        tm1 = t-1
        time_tm1 = time_t - dt.timedelta(minutes=60)

        # # start of time period
        # idx_extent = 8000
        # s_idx = int(eu.binary_search(wxt_obs_raw['time'], time_tm1, lo=max(0, tm1 - idx_extent),
        #                           hi=min(tm1 + idx_extent, len(wxt_obs_raw['time']))))
        # # end of time period
        # e_idx = int(eu.binary_search(wxt_obs_raw['time'], time_t, lo=max(0, t - idx_extent),
        #                           hi=min(t + idx_extent, len(wxt_obs_raw['time']))))

        s_idx = int(eu.binary_search(wxt_obs_raw['time'], time_tm1))
        # end of time period
        e_idx = int(eu.binary_search(wxt_obs_raw['time'], time_t))

        # if the time_range time and data['time'] found in this iteration are within an acceptable range (15 mins)
        tm1_diff = time_tm1 - wxt_obs_raw['time'][s_idx]
        t_diff = time_t - wxt_obs_raw['time'][e_idx]


        # _, s_idx, tm1_diff = eu.nearest(wxt_obs_raw['time'], time_tm1)
        # _, e_idx, t_diff = eu.nearest(wxt_obs_raw['time'], time_t)


        if (tm1_diff.total_seconds() <= 15 * 60) & (t_diff.total_seconds() <= 15 * 60):
            for met_var in met_vars:
                wxt_obs[met_var][t] = np.nanmean(wxt_obs_raw[met_var][s_idx:e_idx+1])


    # create RH_frac using RH data
    wxt_obs['RH_frac'] = wxt_obs['RH'] / 100.0

    # calculate extra variables
    e_s_hpa = 6.112 * (np.exp((17.67 * wxt_obs['Tair']) / (wxt_obs['Tair'] + 243.5)))  # [hPa] # sat. v. pressure
    e_s = e_s_hpa * 100.0  # [Pa] # sat. v. pressure
    wxt_obs['e'] = wxt_obs['RH_frac'] * e_s  # [Pa] # v. pressure
    wxt_obs['r_v'] = wxt_obs['e'] / (1.61 * ((wxt_obs['press']*100.0) - wxt_obs['e'])) # water_vapour mixing ratio [kg kg-1]
    wxt_obs['q'] =  wxt_obs['e'] / ((1.61 * ((wxt_obs['press']*100.0) - wxt_obs['e'])) + wxt_obs['e']) # specific humidity [kg kg-1]
    wxt_obs['Tv'] = (1 + (0.61 * wxt_obs['q'])) * (wxt_obs['Tair'] + 273.15) # virtual temp [K]
    wxt_obs['air_density'] = (wxt_obs['press']*100.0) / (286.9 * wxt_obs['Tv'])# [kg m-3]

    return wxt_obs
示例#3
0
def main():

    # Read in the mass data for 2016
    # Read in RH data for 2016
    # convert gases and such into the aerosol particles
    # swell the particles based on the CLASSIC scheme stuff
    # use Mie code to calculate the backscatter and extinction
    # calculate lidar ratio
    # plot lidar ratio

    # ==============================================================================
    # Setup
    # ==============================================================================

    # which modelled data to read in
    model_type = 'UKV'

    # directories
    maindir = '/home/nerc/Documents/MieScatt/'
    datadir = '/home/nerc/Documents/MieScatt/data/'

    savedir = maindir + 'figures/LidarRatio/'

    # data
    wxtdatadir = datadir
    massdatadir = datadir
    ffoc_gfdir = datadir

    # RH data
    wxt_inst_site = 'WXT_KSSW'

    # data year
    year = '2016'

    # aerosol particles to calculate (OC = Organic carbon, CBLK = black carbon, both already measured)
    # match dictionary keys further down
    aer_particles = ['(NH4)2SO4', 'NH4NO3', 'NaCl', 'CORG', 'CBLK']

    all_species = ['(NH4)2SO4', 'NH4NO3', 'NaCl', 'CORG', 'CBLK', 'H2O']
    # aer names in the complex index of refraction files
    aer_names = {
        '(NH4)2SO4': 'Ammonium sulphate',
        'NH4NO3': 'Ammonium nitrate',
        'CORG': 'Organic carbon',
        'NaCl': 'Generic NaCl',
        'CBLK': 'Soot',
        'MURK': 'MURK'
    }

    # density of molecules [kg m-3]
    # CBLK: # Zhang et al., (2016) Measuring the morphology and density of internally mixed black carbon
    #           with SP2 and VTDMA: New insight into the absorption enhancement of black carbon in the atmosphere
    # ORG: Range of densities for organic carbon is mass (0.625 - 2 g cm-3)
    #  Haywood et al 2003 used 1.35 g cm-3 but Schkolink et al., 2006 claim the average is 1.1 g cm-3 after a lit review
    aer_density = {
        '(NH4)2SO4': 1770.0,
        'NH4NO3': 1720.0,
        'NaCl': 2160.0,
        'CORG': 1100.0,
        'CBLK': 1200.0
    }

    # Organic carbon growth curve (Assumed to be the same as aged fossil fuel organic carbon

    # pure water density
    water_density = 1000.0  # kg m-3

    # wavelength to aim for
    ceil_lambda = [0.905e-06]

    # ==============================================================================
    # Read data
    # ==============================================================================

    # read in the complex index of refraction data for the aerosol species (can include water)
    n_species = read_n_data(aer_particles, aer_names, ceil_lambda, getH2O=True)

    # Read in physical growth factors (GF) for organic carbon (assumed to be the same as aged fossil fuel OC)
    gf_ffoc_raw = eu.csv_read(ffoc_gfdir + 'GF_fossilFuelOC_calcS.csv')
    gf_ffoc_raw = np.array(gf_ffoc_raw)[1:, :]  # skip header

    gf_ffoc = {
        'RH_frac': np.array(gf_ffoc_raw[:, 0], dtype=float),
        'GF': np.array(gf_ffoc_raw[:, 1], dtype=float)
    }

    # Read in species by mass data
    # Units are grams m-3
    mass_in = read_mass_data(massdatadir, year)

    # Read WXT data
    wxtfilepath = wxtdatadir + wxt_inst_site + '_' + year + '_15min.nc'
    WXT_in = eu.netCDF_read(wxtfilepath, vars=['RH', 'Tair', 'press', 'time'])
    WXT_in['RH_frac'] = WXT_in['RH'] * 0.01
    WXT_in['time'] -= dt.timedelta(
        minutes=15
    )  # change time from 'obs end' to 'start of obs', same as the other datasets

    # Trim times
    # as WXT and mass data are 15 mins and both line up exactly already
    #   therefore trim WXT to match mass time
    mass_in, WXT_in = trim_mass_wxt_times(mass_in, WXT_in)

    # Time match so mass and WXT times line up INTERNALLY as well
    date_range = eu.date_range(WXT_in['time'][0], WXT_in['time'][-1], 15,
                               'minutes')

    # make sure there are no time stamp gaps in the data so mass and WXT will match up perfectly, timewise.
    print 'beginning time matching for WXT...'
    WXT = internal_time_completion(WXT_in, date_range)
    print 'end time matching for WXT...'

    # same but for mass data
    print 'beginning time matching for mass...'
    mass = internal_time_completion(mass_in, date_range)
    print 'end time matching for mass...'

    # Create idealised number distribution for now... (dry distribution)
    # idealised dist is equal for all particle types for now.
    step = 0.005
    r_range_um = np.arange(0.000 + step, 5.000 + step, step)
    r_range_m = r_range_um * 1.0e-06

    r_mean = 0.11e-06
    sigma = 0

    # ==============================================================================
    # Process data
    # ==============================================================================

    # molecular mass of each molecule
    mol_mass_amm_sulp = 132
    mol_mass_amm_nit = 80
    mol_mass_nh4 = 18
    mol_mass_n03 = 62
    mol_mass_s04 = 96

    # Convert into moles
    # calculate number of moles (mass [g] / molar mass)
    # 1e-06 converts from micrograms to grams.
    moles = {
        'SO4': mass['SO4'] / mol_mass_s04,
        'NO3': mass['NO3'] / mol_mass_n03,
        'NH4': mass['NH4'] / mol_mass_nh4
    }

    # calculate ammonium sulphate and ammonium nitrate from gases
    # adds entries to the existing dictionary
    mass = calc_amm_sulph_and_amm_nit_from_gases(moles, mass)

    # convert chlorine into sea salt assuming all chlorine is sea salt, and enough sodium is present.
    #      potentially weak assumption for the chlorine bit due to chlorine depletion!
    mass['NaCl'] = mass['CL'] * 1.65

    # convert masses from g m-3 to kg kg-1_air for swelling.
    # Also creates the air density and is stored in WXT
    mass_kg_kg, WXT = convert_mass_to_kg_kg(mass, WXT, aer_particles)

    # start with just 0.11 microns as the radius - can make it more fancy later...
    r_d_microns = 0.11  # [microns]
    r_d_m = r_d_microns * 1.0e-6  # [m]

    # calculate the number of particles for each species using radius_m and the mass
    # Hopefull not needed!
    num_part = {}
    for aer_i in aer_particles:
        num_part[aer_i] = mass_kg_kg[aer_i] / (
            (4.0 / 3.0) * np.pi * (aer_density[aer_i] / WXT['dryair_rho']) *
            (r_d_m**3.0))

    # calculate dry volume
    V_dry_from_mass = {}
    for aer_i in aer_particles:
        # V_dry[aer_i] = (4.0/3.0) * np.pi * (r_d_m ** 3.0)
        V_dry_from_mass[aer_i] = mass_kg_kg[aer_i] / aer_density[aer_i]  # [m3]

        # if np.nan (i.e. there was no mass therefore no volume) make it 0.0
        bin = np.isnan(V_dry_from_mass[aer_i])
        V_dry_from_mass[aer_i][bin] = 0.0

    # ---------------------------------------------------------
    # Swell the particles (r_md,aer_i) [microns]

    # set up dictionary
    r_md = {}

    # calculate the swollen particle size for these three aerosol types
    # Follows CLASSIC guidence, based off of Fitzgerald (1975)
    for aer_i in ['(NH4)2SO4', 'NH4NO3', 'NaCl']:
        r_md[aer_i] = calc_r_md_species(r_d_microns, WXT, aer_i)

    # set r_md for black carbon as r_d, assuming black carbon is completely hydrophobic
    r_md['CBLK'] = np.empty(len(date_range))
    r_md['CBLK'][:] = r_d_microns

    # calculate r_md for organic carbon using the MO empirically fitted g(RH) curves
    r_md['CORG'] = np.empty(len(date_range))
    r_md['CORG'][:] = np.nan
    for t, time_t in enumerate(date_range):

        _, idx, _ = eu.nearest(gf_ffoc['RH_frac'], WXT['RH_frac'][t])
        r_md['CORG'][t] = r_d_microns * gf_ffoc['GF'][idx]

    # -----------------------------------------------------------

    # calculate abs volume of wetted particles (V_abs,wet,aer_i)
    # use the growth factors calculated based on r_d to calc V_wet from V_dry(mass, density)

    # calculate the physical growth factor, wetted particle density, wetted particle volume, ...
    #   and water volume (V_wet - Vdry)

    GF = {}
    # aer_wet_density = {}
    V_wet_from_mass = {}
    V_water_i = {}
    for aer_i in aer_particles:  # aer_particles:

        # physical growth factor
        GF[aer_i] = r_md[aer_i] / r_d_microns

        # # wet aerosol density
        # aer_wet_density[aer_i] = (aer_density[aer_i] / (GF[aer_i]**3.0)) + \
        #                          (water_density * (1.0 - (1.0 / (GF[aer_i]**3.0))))

        # wet volume, using the growth rate from r_d to r_md
        # if np.nan (i.e. there was no mass therefore no volume) make it 0.0
        V_wet_from_mass[aer_i] = V_dry_from_mass[aer_i] * (GF[aer_i]**3.0)
        bin = np.isnan(V_wet_from_mass[aer_i])
        V_wet_from_mass[aer_i][bin] = 0.0

        # water volume contribution from just this aer_i
        V_water_i[aer_i] = V_wet_from_mass[aer_i] - V_dry_from_mass[aer_i]

    # ---------------------------
    # Calculate relative volume of all aerosol AND WATER (to help calculate n_mixed)

    # calculate total water volume
    V_water_2d = np.array(V_water_i.values(
    ))  # turn into a 2D array (does not matter column order)
    V_water_tot = np.nansum(V_water_2d, axis=0)

    # combine volumes of the DRY aerosol and the water into a single 2d array shape=(time, substance)
    # V_abs = np.transpose(np.vstack([np.array(V_dry_from_mass.values()),V_water_tot]))
    # shape = (time, species)
    V_abs = np.transpose(
        np.vstack([
            np.array([V_dry_from_mass[i] for i in aer_particles]), V_water_tot
        ]))

    # now calculate the relative volume of each of these (V_rel)
    # scale absolute volume to find relative volume of each (such that sum(all substances for time t = 1))
    vol_sum = np.nansum(V_abs, axis=1)
    vol_sum[vol_sum == 0.0] = np.nan
    scaler = 1.0 / (vol_sum)  # a value for each time step

    # if there is no mass data for time t, and therefore no volume data, then set scaler to np.nan
    bin = np.isinf(scaler)
    scaler[bin] = np.nan

    # Relative volumes
    V_rel = {'H2O': scaler * V_water_tot}
    for aer_i in aer_particles:
        V_rel[aer_i] = scaler * V_dry_from_mass[aer_i]

    # --------------------------------------------------------------
    # Calculate relative volume of the swollen aerosol (to weight and calculate r_md)

    # V_wet_from_mass
    V_abs_aer_only = np.transpose(
        np.array([V_wet_from_mass[aer_i] for aer_i in aer_particles]))

    # now calculate the relative volume of each of these (V_rel_Aer_only)
    # scale absolute volume to find relative volume of each (such that sum(all substances for time t = 1))
    vol_sum_aer_only = np.nansum(V_abs_aer_only, axis=1)
    vol_sum_aer_only[vol_sum_aer_only == 0.0] = np.nan
    scaler = 1.0 / (vol_sum_aer_only)  # a value for each time step

    # if there is no mass data for time t, and therefore no volume data, then set scaler to np.nan
    bin = np.isinf(scaler)
    scaler[bin] = np.nan

    # Relative volumes
    V_rel_aer_only = {}
    for aer_i in aer_particles:
        V_rel_aer_only[aer_i] = scaler * V_wet_from_mass[aer_i]

    # for aer_i in aer_particles:
    #      print aer_i
    #      print V_rel[aer_i][-1]

    # --------------------------------------------------------------

    # calculate n_mixed using volume mixing method
    # volume mixing for CIR (eq. 12, Liu and Daum 2008)

    n_mixed = np.array([V_rel[i] * n_species[i] for i in V_rel.iterkeys()])
    n_mixed = np.sum(n_mixed, axis=0)

    # calculate volume mean radii from r_md,aer_i (weighted by V_rel,wet,aer_i)
    r_md_avg = np.array(
        [V_rel_aer_only[aer_i] * r_md[aer_i] for aer_i in aer_particles])
    r_md_avg = np.nansum(r_md_avg, axis=0)
    r_md_avg[r_md_avg == 0.0] = np.nan

    # convert from microns to m
    r_md_avg_m = r_md_avg * 1e-6

    # calculate the size parameter for the average aerosol size
    x_wet_mixed = (2.0 * np.pi * r_md_avg_m) / ceil_lambda[0]

    # --------------------------

    # calculate Q_back and Q_ext from the avergae r_md and n_mixed
    S = np.empty(len(date_range))
    S[:] = np.nan
    for t, time_t in enumerate(date_range):

        x_i = x_wet_mixed[t]  # size parameter_i
        n_i = n_mixed[t]  # complex index of refraction i

        if t in np.arange(0, 35000, 500):
            print t

        if np.logical_and(~np.isnan(x_i), ~np.isnan(n_i)):

            particle = Mie(x=x_i, m=n_i)
            Q_ext = particle.qext()
            Q_back = particle.qb()

            # calculate the lidar ratio
            S_t = Q_ext / Q_back
            S[t] = Q_ext / Q_back

    # ---------------------

    # simple plot of S
    fig, ax = plt.subplots(1, 1, figsize=(6, 6))
    plt.plot_date(date_range, S)
    plt.savefig(savedir + 'quickplot.png')
    plt.close(fig)

    # --------------------------

    # Testing lidar ratio computation

    # read in Franco's computation of the lidar ratio CIR=1.47 + 0.099i, lambda=905nm
    lr_f = eu.netCDF_read(
        '/home/nerc/Documents/MieScatt/testing/lr_1.47_0.099_0.905.nc',
        ['DIAMETER', 'LIDAR_RATIO'])

    step = 0.005
    r_range_um = np.arange(0.000 + step, 10.000 + step, step)
    r_range_m = r_range_um * 1.0e-06
    x_range = (2.0 * np.pi * r_range_m) / ceil_lambda[0]

    # calculate Q_back and Q_ext from the avergae r_md and n_mixed
    #S_r = lidar ratio
    S_r = np.empty(len(r_range_m))
    S_r[:] = np.nan
    for r_idx, r_i in enumerate(r_range_m):

        x_i = x_range[r_idx]  # size parameter_i
        n_i = complex(1.47 + 0.099j)  # fixed complex index of refraction i

        # print loop progress
        if r_idx in np.arange(0, 2100, 100):
            print r_idx

        particle = Mie(x=x_i, m=n_i)
        Q_ext = particle.qext()
        Q_back = particle.qb()
        Q_back_alt = Q_back / (4.0 * np.pi)

        # #Q_back = particle.qb()
        # S12 = particle.S12(-1)
        # S11 = S12[0].imag
        # S22 = S12[1].imag
        # Q_back_fancy = ((np.abs(S11)**2) + (np.abs(S22)**2))/(2 * np.pi * (x_i**2))

        # calculate the lidar ratio
        # S_t = Q_ext / Q_back
        S_r[r_idx] = Q_ext / Q_back_alt

    # simple plot of S
    fig, ax = plt.subplots(1, 1, figsize=(6, 5))
    plt.loglog(r_range_um * 2, S_r, label='mine')  # diameter [microns]
    plt.loglog(lr_f['DIAMETER'], lr_f['LIDAR_RATIO'], label='Franco' 's')
    plt.xlim([0.01, 100.0])
    plt.ylim([1.0, 10.0e7])
    plt.ylabel('Lidar Ratio')
    plt.xlabel('Diameter [microns]')
    plt.legend()
    plt.tight_layout()
    plt.savefig(savedir + 'quickplot_S_vs_r.png')
    plt.close(fig)

    # -----------------------------------------------

    d_test = 0.001e-06
    r_test = d_test / 2.0
    r_test_microns = r_test * 1.0e6

    x_i = (2.0 * np.pi * r_test) / ceil_lambda[0]  # size parameter_i
    n_i = complex(1.47 + 0.099j)  # fixed complex index of refraction i

    particle = Mie(x=x_i, m=n_i)
    Q_ext = particle.qext()
    Q_back = particle.qb()
    Q_back_alt = Q_back / (4.0 * np.pi)

    # calculate extinction and scattering cross section
    C_ext = Q_ext * np.pi * (r_test_microns**2.0)
    C_back = Q_back * np.pi * (r_test_microns**2.0)
    C_back_alt = Q_back_alt * np.pi * (r_test_microns**2.0)

    S12 = particle.S12(-1)

    S11 = S12[0].imag
    S22 = S12[1].imag

    Q_back_fancy = ((np.abs(S11)**2) + (np.abs(S22)**2)) / (2 * np.pi *
                                                            (x_i**2))

    # calculate the lidar ratio
    S_t = Q_ext / Q_back
    S_test = Q_ext / Q_back_alt
    S_c_test = C_ext / C_back
    S_c_alt = C_ext / C_back_alt

    return
def test_lidar_computation(ceil_lambda, r_md_m):
    """
    Test my computation of the lidar ratio against Franco's. Done for a monodisperse, soot(like?) aerosol
    :param ceil_lambda:
    :param r_md_m:
    :return:
    """

    import ellUtils as eu

    # Testing lidar ratio computation

    # read in Franco's computation of the lidar ratio CIR=1.47 + 0.099i, lambda=905nm
    lr_f = eu.netCDF_read(
        '/home/nerc/Documents/MieScatt/testing/lr_1.47_0.099_0.905.nc',
        ['DIAMETER', 'LIDAR_RATIO'])

    step = 0.005
    r_range_um = np.arange(0.000 + step, 10.000 + step, step)
    r_range_m = r_range_um * 1.0e-06
    x_range = (2.0 * np.pi * r_range_m) / ceil_lambda[0]

    # calculate Q_back and Q_ext from the avergae r_md and n_mixed
    #S_r = lidar ratio
    S_r = np.empty(len(r_range_m))
    S_r[:] = np.nan
    for r_idx, r_i in enumerate(r_range_m):

        x_i = x_range[r_idx]  # size parameter_i
        n_i = complex(1.47 + 0.0j)  # fixed complex index of refraction i
        # n_i = complex(1.47 + 0.099j)  # fixed complex index of refraction i for soot

        # print loop progress
        if r_idx in np.arange(0, 2100, 100):
            print r_idx

        particle = Mie(x=x_i, m=n_i)
        Q_ext = particle.qext()
        Q_back = particle.qb()
        Q_back_alt = Q_back / (4.0 * np.pi)

        # #Q_back = particle.qb()
        # S12 = particle.S12(-1)
        # S11 = S12[0].imag
        # S22 = S12[1].imag
        # Q_back_fancy = ((np.abs(S11)**2) + (np.abs(S22)**2))/(2 * np.pi * (x_i**2))

        # calculate the lidar ratio
        # S_t = Q_ext / Q_back
        S_r[r_idx] = Q_ext / Q_back_alt

    # simple plot of S
    fig, ax = plt.subplots(1, 1, figsize=(8, 7))
    plt.loglog(r_range_um * 2, S_r, label='mine')  # diameter [microns]
    plt.loglog(lr_f['DIAMETER'], lr_f['LIDAR_RATIO'], label='Franco' 's')

    for aer_i, r_md_m_aer_i in r_md_m.iteritems():
        for r_i in r_md_m_aer_i:
            plt.vlines(r_i, 1, 1e6, linestyle='--', alpha=0.5)

    plt.xlim([0.01, 100.0])
    plt.ylim([1.0, 10.0e7])
    plt.ylabel('Lidar Ratio')
    plt.xlabel('Diameter [microns]')
    plt.legend()
    plt.tight_layout()
    plt.savefig(maindir + 'figures/LidarRatio/' +
                'quickplot_S_vs_r_with_rbin_lines.png')
    plt.close(fig)

    return
    high_idx = np.where(y[idx] > 4.0e-06)[0]
    y[idx][high_idx]

    # # check transmission - seems ok
    # filename = 'C:/Users/Elliott/Documents/PhD Reading/PhD Research/Aerosol Backscatter/clearFO/data/L1/CL31-D_CCW30_NK_2015_15min.nc'
    # data = eu.netCDF_read(filename)
    # _, t_idx, _ = eu.nearest(data['time'], dt.datetime(2015, 6, 4))
    # plt.figure()
    # plt.plot_date(data['time'][t_idx-(24*4):t_idx+(48*4)], data['transmission'][t_idx-(24*4):t_idx+(48*14)])
    # plt.ylabel('transmission for CL31-NK')
    # plt.xlabel('date [YYYY:mm:DD]')

    # check RH trend at the time
    # filename = 'C:/Users/Elliott/Documents/PhD Reading/PhD Research/Aerosol Backscatter/MorningBL/data/L1/Davis_IMU_2015_15min.nc'
    filename = 'C:/Users/Elliott/Documents/PhD Reading/PhD Research/Aerosol Backscatter/MorningBL/data/L1/WXT_KSSW_2015_15min.nc'
    data = eu.netCDF_read(filename)
    _, t_idx, _ = eu.nearest(data['time'], dt.datetime(2015, 6, 4))
    all_range = np.arange(t_idx, t_idx + (110 * 1))

    _, ts_idx, _ = eu.nearest(data['time'], dt.datetime(2015, 6, 4, 20, 0, 0))
    _, te_idx, _ = eu.nearest(data['time'], dt.datetime(2015, 6, 5, 0, 0, 0))
    focus_range = np.arange(ts_idx, te_idx)

    plt.figure(figsize=(11, 4))
    ax = plt.gca()
    # 96 = 1 day of 15 min data
    # plt.plot_date(data['time'][t_idx:t_idx+(110*1)], data['RH'][t_idx:t_idx+(110*1)], color='blue') # all data
    # plt.plot_date(data['time'][ts_idx:te_idx], data['RH'][ts_idx:te_idx], color='red') # just those 5 hours
    plt.plot_date(data['time'][all_range], data['RH'][all_range],
                  color='blue')  # all data
    plt.plot_date(data['time'][focus_range],
def main():

    # Read in the mass data for 2016
    # Read in RH data for 2016
    # convert gases and such into the aerosol particles
    # swell the particles based on the CLASSIC scheme stuff
    # use Mie code to calculate the backscatter and extinction
    # calculate lidar ratio
    # plot lidar ratio

    # ==============================================================================
    # Setup
    # ==============================================================================

    # which modelled data to read in
    model_type = 'UKV'
    res = FOcon.model_resolution[model_type]

    # directories
    maindir = 'C:/Users/Elliott/Documents/PhD Reading/PhD Research/Aerosol Backscatter/MorningBL/'
    datadir = 'C:/Users/Elliott/Documents/PhD Reading/PhD Research/Aerosol Backscatter/MorningBL/data/'
    massdatadir = 'C:/Users/Elliott/Documents/PhD Reading/PhD Research/Aerosol Backscatter/clearFO/data/ERG/'

    savedir = maindir + 'figures/LidarRatio/'

    # data
    wxtdatadir = datadir + 'L1/'

    # RH data
    wxt_inst_site = 'WXT_KSSW'

    # data year
    year = '2016'

    # aerosol particles to calculate (OC = Organic carbon, CBLK = black carbon, both already measured)
    # match dictionary keys further down
    aer_particles = ['(NH4)2SO4', 'NH4NO3', 'NaCl', 'CORG', 'CBLK']

    # density of molecules [kg m-3]
    # CBLK: # Zhang et al., (2016) Measuring the morphology and density of internally mixed black carbon
    #           with SP2 and VTDMA: New insight into the absorption enhancement of black carbon in the atmosphere
    # ORG: Range of densities for organic carbon is mass (0.625 - 2 g cm-3)
    #  Haywood et al 2003 used 1.35 g cm-3 but Schkolink et al., 2006 claim the average is 1.1 g cm-3 after a lit review
    aer_density = {
        '(NH4)2SO4': 1770.0,
        'NH4NO3': 1720.0,
        'NaCl': 2160.0,
        'CORG': 1100.0,
        'CBLK': 1200.0
    }

    # pure water density
    water_density = 1000.0  # kg m-3

    # ==============================================================================
    # Read data
    # ==============================================================================

    # Read in species by mass data
    # Units are grams m-3
    mass_in = read_mass_data(massdatadir, year)

    # Read WXT data
    wxtfilepath = wxtdatadir + wxt_inst_site + '_' + year + '_15min.nc'
    WXT_in = eu.netCDF_read(wxtfilepath, vars=['RH', 'Tair', 'press', 'time'])
    WXT_in['RH_frac'] = WXT_in['RH'] * 0.01
    WXT_in['time'] -= dt.timedelta(
        minutes=15
    )  # change time from 'obs end' to 'start of obs', same as the other datasets

    # Trim times
    # as WXT and mass data are 15 mins and both line up exactly already
    #   therefore trim WXT to match mass time
    mass_in, WXT_in = trim_mass_wxt_times(mass_in, WXT_in)

    # Time match so mass and WXT times line up INTERNALLY as well
    date_range = eu.date_range(WXT_in['time'][0], WXT_in['time'][-1], 15,
                               'minutes')

    # make sure there are no time stamp gaps in the data so mass and WXT will match up perfectly, timewise.
    print 'beginning time matching for WXT...'
    WXT = internal_time_completion(WXT_in, date_range)
    print 'end time matching for WXT...'

    # same but for mass data
    print 'beginning time matching for mass...'
    mass = internal_time_completion(mass_in, date_range)
    print 'end time matching for mass...'

    # ==============================================================================
    # Process data
    # ==============================================================================

    # molecular mass of each molecule
    mol_mass_amm_sulp = 132
    mol_mass_amm_nit = 80
    mol_mass_nh4 = 18
    mol_mass_n03 = 62
    mol_mass_s04 = 96

    # Convert into moles
    # calculate number of moles (mass [g] / molar mass)
    # 1e-06 converts from micrograms to grams.
    moles = {
        'SO4': mass['SO4'] / mol_mass_s04,
        'NO3': mass['NO3'] / mol_mass_n03,
        'NH4': mass['NH4'] / mol_mass_nh4
    }

    # calculate ammonium sulphate and ammonium nitrate from gases
    # adds entries to the existing dictionary
    mass = calc_amm_sulph_and_amm_nit_from_gases(moles, mass)

    # convert chlorine into sea salt assuming all chlorine is sea salt, and enough sodium is present.
    #      potentially weak assumption for the chlorine bit due to chlorine depletion!
    mass['NaCl'] = mass['CL'] * 1.65

    # convert masses from g m-3 to kg kg-1_air for swelling.
    # Also creates the air density and is stored in WXT
    mass_kg_kg, WXT = convert_mass_to_kg_kg(mass, WXT, aer_particles)

    # start with just 0.11 microns as the radius - can make it more fancy later...
    r_d_microns = 0.11  # [microns]
    r_d_m = r_d_microns * 1.0e-6  # [m]
    V_dry_from_r_d = (4.0 / 3.0) * np.pi * (r_d_m**3.0)  # [m3]

    # calculate the number of particles for each species using radius_m and the mass
    # Hopefull not needed!
    num_part = {}
    for aer_i in aer_particles:
        num_part[aer_i] = mass_kg_kg[aer_i] / (
            (4.0 / 3.0) * np.pi * (aer_density[aer_i] / WXT['dryair_rho']) *
            (r_d_m**3.0))

    # calculate dry volume
    V_dry_from_mass = {}
    for aer_i in aer_particles:
        # V_dry[aer_i] = (4.0/3.0) * np.pi * (r_d_m ** 3.0)
        V_dry_from_mass[aer_i] = mass_kg_kg[aer_i] / aer_density[aer_i]  # [m3]

    # ---------------------------------------------------------
    # Swell the particles (r_md,aer_i) [microns]

    # set up dictionary
    r_md = {}

    # calculate the swollen particle size for these three aerosol types
    # Follows CLASSIC guidence, based off of Fitzgerald (1975)
    for aer_i in ['(NH4)2SO4', 'NH4NO3', 'NaCl']:
        r_md[aer_i] = calc_r_md_species(r_d_microns, WXT, aer_i)

    # set r_md for black carbon as r_d, assuming black carbon is completely hydrophobic
    r_md['CBLK'] = np.empty(len(date_range))
    r_md['CBLK'][:] = r_d_microns

    # calculate r_md for organic carbon using the MO empirically fitted g(RH) curves
    # r_md['CORG'] = ...

    # -----------------------------------------------------------

    # calculate abs volume of wetted particles (V_abs,wet,aer_i)
    # use the growth factors calculated based on r_d to calc V_wet from V_dry(mass, density)

    # calculate the physical growth factor, wetted particle density, wetted particle volume, ...
    #   and water volume (V_wet - Vdry)

    GF = {}
    aer_wet_density = {}
    V_wet_from_mass = {}
    V_water_i = {}
    for aer_i in ['(NH4)2SO4', 'NH4NO3', 'NaCl', 'CBLK']:  # aer_particles:

        # physical growth factor
        GF[aer_i] = r_md[aer_i] / r_d_microns

        # wet aerosol density
        aer_wet_density[aer_i] = (aer_density[aer_i] / (GF[aer_i]**3.0)) + \
                                 (water_density * (1.0 - (1.0 / (GF[aer_i]**3.0))))

        # wet volume, using the growth rate from r_d to r_md
        V_wet_from_mass[aer_i] = V_dry_from_mass[aer_i] * (GF[aer_i]**3.0)

        # if np.nan (i.e. there was no mass therefore no volume) make it 0.0
        bin = np.isnan(V_wet_from_mass[aer_i])
        V_wet_from_mass[aer_i][bin] = 0.0

        # water volume contribution from just this aer_i
        V_water_i[aer_i] = V_wet_from_mass[aer_i] - V_dry_from_mass[aer_i]

    # calculate total water volume
    V_water_2d = np.array(V_water_i.values())  # turn into a 2D array
    V_water_tot = np.nansum(V_water_2d, axis=0)

    # combine volumes of the DRY aerosol and the water into a single 2d array shape=(time, substance)
    V_abs = np.transpose(
        np.vstack([np.array(V_dry_from_mass.values()), V_water_tot]))

    # now calculate the relative volume of each of these
    # scale absolute volume to find relative volume of each (such that sum(all substances for time t = 1))
    scaler = 1.0 / (np.nansum(V_abs, axis=1))  # a value for each time step
    scaler_rep = np.transpose(np.array([scaler] *
                                       6))  # 2d array shape=(time, substance)
    V_rel = scaler_rep * V_abs

    # --------------------------------------------------------------

    # calculate n_mixed using volume mixing method

    # calculate relative volume of all wet aerosol (V_rel,wet,aer_i)

    # calculate n_mixed using volume mixing and (V_rel,wet,aer_i)

    # calculate volume mean radii from r_md,aer_i (weighted by V_rel,wet,aer_i)

    # calculate Q_back and Q_ext from the avergae r_md and n_mixed

    # calculate lidar ratio

    # assume radii are 0.11 microns for now...

    return