Consolidation of CoURAGE Data Sources#

This example shows how to use ACT to combine multiple datasets to support ARM’s CoURAGE deployment in Baltimore, MD. Example uses ARM, EPA, and ASOS data.

import os
from datetime import datetime

import matplotlib.pyplot as plt
import numpy as np

import act

# Get Surface Meteorology data
lat = (39.04, 39.6)
lon = (-77.10, -76.04)
time_window = [datetime(2024, 12, 15), datetime(2024, 12, 20)]
asos_dict = act.discovery.get_asos_data(time_window, lat_range=lat, lon_range=lon, regions='MD')
asos_stations = asos_dict.keys()

# Set up a dictionary to fill with data
data_dict = {}

# Fill the dictionary with ASOS data
for s in asos_stations:
    ds = asos_dict[s]
    ds = ds.where(~np.isnan(ds.tmpf), drop=True)
    ds['tmpf'].attrs['units'] = 'degF'
    ds.utils.change_units(variables='tmpf', desired_unit='degC', verbose=True)
    data_dict[s] = ds

# You need an account and token from https://docs.airnowapi.org/ first
# And then you can download EPA data
airnow_token = os.getenv('AIRNOW_API')
if airnow_token is not None and len(airnow_token) > 0:
    latlon = '-76.905,39.185,-76.158,39.499'

    ds_airnow = act.discovery.get_airnow_bounded_obs(
        airnow_token, '2024-12-15T00', '2024-12-20T23', latlon, 'OZONE,PM25', data_type='B'
    )
    ds_airnow = act.utils.convert_2d_to_1d(ds_airnow, parse='sites')
    sites = ds_airnow['sites'].values
    data_dict['EPA'] = ds_airnow
    airnow = True

# Place your username and token here
username = os.getenv('ARM_USERNAME')
token = os.getenv('ARM_PASSWORD')

# Download ARM data
if username is not None and token is not None and len(username) > 1:
    # Example to show how easy it is to download ARM data if a username/token are set
    sdate = '2024-12-15'
    edate = '2024-12-20'

    # Download and read ARM MET data
    results = act.discovery.download_arm_data(username, token, 'crgmetM1.b1', sdate, edate)
    ds_arm = act.io.arm.read_arm_netcdf(results)
    data_dict['ARM'] = ds_arm

    # Download and read ARM Ozone data
    results = act.discovery.download_arm_data(username, token, 'crgaoso3S2.a1', sdate, edate)
    ds_o3 = act.io.arm.read_arm_netcdf(results, cleanup_qc=True)
    ds_o3.qcfilter.datafilter('o3', rm_assessments=['Suspect', 'Bad'], del_qc_var=False)
    data_dict['ARM_O3'] = ds_o3

    # Download and read ARM SMPS data
    results = act.discovery.download_arm_data(username, token, 'crgaossmpsS2.b1', sdate, edate)
    ds_smps = act.io.arm.read_arm_netcdf(results)
    data_dict['ARM_SMPS'] = ds_smps

    # Set up plot and plot all surface temperature data
    display = act.plotting.TimeSeriesDisplay(data_dict, figsize=(12, 10), subplot_shape=(3,))
    for k in data_dict.keys():
        if 'ARM' not in k and k != 'EPA':
            display.plot('tmpf', dsname=k, label=k, subplot_index=(0,))
        elif k == 'ARM':
            display.plot('temp_mean', dsname=k, label=k, subplot_index=(0,))
            display.day_night_background(dsname='ARM', subplot_index=(0,))

    display.set_yrng([-5, 20], subplot_index=(0,))

    # Plot up ozone data
    title = 'Comparison of ARM and EPA Ozone Measurements'
    display.plot('o3', dsname='ARM_O3', label='ARM', subplot_index=(1,))
    if airnow:
        display.plot(
            'OZONE_sites_2',
            dsname='EPA',
            label='EPA ' + sites[2],
            subplot_index=(1,),
            set_title=title,
        )
    display.set_yrng([0, 60], subplot_index=(1,))
    display.day_night_background(dsname='ARM', subplot_index=(1,))

    # Plot SMPS data
    title = 'ARM SMPS Concentrations and EPA PM2.5'
    if airnow:
        display.plot('PM2.5_sites_0', dsname='EPA', label='EPA ' + sites[0], subplot_index=(2,))
        display.plot('PM2.5_sites_1', dsname='EPA', label='EPA ' + sites[1], subplot_index=(2,))
        display.plot(
            'PM2.5_sites_3',
            dsname='EPA',
            label='EPA ' + sites[3],
            subplot_index=(2,),
            set_title=title,
        )

    display.set_yrng([0, 30], subplot_index=(2,))
    plt.legend(loc=2)
    ax2 = display.axes[2].twinx()
    ax2.plot(ds_smps['time'], ds_smps['total_N_conc'], color='purple')
    ax2.set_ylabel('ARM SMPS (' + ds_smps['total_N_conc'].attrs['units'] + ')')
    display.day_night_background(dsname='ARM', subplot_index=(2,))

    plt.show()