Inventory Analysis and Visualization¶

To use this notebook, the GRA2PES dataset must be downloaded and regridded using the process outlined in https://github.com/agmeyer4/atmos/tree/merge-atmos-column/workflows/gra2pes. There is also data included from the EPA and EDGAR datasets which must be downloaded and linked independently.

In [1]:
#Import libraries
import os
import xarray as xr
import numpy as np
import pandas as pd
import xesmf as xe
import sys 
from plotly import graph_objects as go
from plotly.subplots import make_subplots
from cartopy.io.img_tiles import OSM
import calendar
import matplotlib.pyplot as plt
import pyproj
import matplotlib
import datetime
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import matplotlib.pyplot as plt
import pandas as pd
from matplotlib.ticker import ScalarFormatter, FuncFormatter
import seaborn as sns
import cartopy.feature as cfeature
import cartopy.io.img_tiles as cimgt
from cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter
sys.path.append('/uufs/chpc.utah.edu/common/home/u0890904/LAIR_1/atmos')
from configs.gra2pes import gra2pes_config
from workflows.gra2pes import gra2pes_regrid
from utils import gen_utils, datetime_utils, xr_utils, plot_utils, gra2pes_utils

#Autoreload changed local modules
%load_ext autoreload
%autoreload 2
In [2]:
#Define the paths
project_path = '/uufs/chpc.utah.edu/common/home/u0890904/LAIR_1/Projects/Ratio_paper_2025_v5'
project_figures_path = os.path.join(project_path,'Figures')
project_data_path = os.path.join(project_path,'Data')
project_pickle_path = os.path.join(project_path,'Data','Pickled')
inventory_figures_path = os.path.join(project_figures_path,'inventories')

wbb_loc = {'lat':40.768,'lon':-111.854}
uusyr_loc = {'lat':41.089,'lon':-112.119}

molar_masses = {'CO2':44.01, 'CO':28.01, 'CH4':16.04}

slv_extent = {'lon_min':-112.15,
                  'lon_max':-111.7,
                  'lat_min':40.351,
                  'lat_max':41.2} 

map_extent={'lon_min':-112.22,
            'lon_max':-111.65,
            'lat_min':40.35,
            'lat_max':41.3} 

specs = ['CO2','CO','CH4']
In [3]:
# Define point sources
point_sources = {

    'landfills' :   {   'locs':[(-112.043, 40.746),
                             (-112.055, 40.558),
                             (-111.917, 40.911)],
                        'plot_details':{'marker':'^','color':'red','edgecolor':'black','s':150}
                    },

    'refineries': {     'locs':[(-111.924, 40.825),
                                (-111.920, 40.838),
                                (-111.909, 40.794),
                                (-111.904, 40.887),
                                (-111.910, 40.868)],
                        'plot_details':{'marker':'o','s':150,'color':'red','edgecolor':'black'}
                    },
    'wastewater' : {    'locs':[(-112.074, 40.728),
                                (-111.900, 40.682),
                                (-111.919, 40.504),
                                (-111.931, 40.813),
                                (-111.924, 40.615),
                                (-111.942, 40.842),
                                (-111.933, 40.903),
                                (-111.945, 41.001)],
                        'plot_details':{'marker':'^','color':'green'}
                    },

}

Load Data¶

GRA2PES¶

In [4]:
# Load the GRA2PES data organized using atmos.inventories.gra2pes

config = gra2pes_config.Gra2pesConfig('/uufs/chpc.utah.edu/common/home/u0890904/LAIR_1/atmos/configs/gra2pes/gra2pes_config_1.yaml')
regrid_id = '0.025x0.025'
dtr = datetime_utils.DateTimeRange('2021-01-01','2021-12-31 23:59',tz = 'UTC')
rgh = gra2pes_utils.RegriddedGra2pesHandler(config,regrid_id)
regridded_ds = rgh.open_ds_inrange(dtr,sectors='all',chunks = {'utc_hour':1,'month':1,'lat':100,'lon':100})
grapes_ds = rgh.rework_ds_dt(regridded_ds[['CO2','CO','HC01']])
grapes_ds = grapes_ds.rename({'HC01':'CH4'})
grapes_gca = xr.open_dataset(os.path.join(rgh.regridded_path,'details','grid_out_area.nc'))['cell_area']

# Trim to the slv extent
grapes_slv_ds = xr_utils.trim_ds_to_extent(grapes_ds,slv_extent)
grapes_slv_gca = xr_utils.trim_ds_to_extent(grapes_gca,slv_extent)
In [5]:
# Convert Units
unit_converter = xr_utils.UnitConverter()
grapes_slv_ds = unit_converter.convert(grapes_slv_ds, 'km^-2', 'm^-2')
grapes_slv_ds = unit_converter.convert_mole_to_g(grapes_slv_ds, molar_masses)
grapes_slv_ds = unit_converter.convert(grapes_slv_ds, 'g', 'metric_Ton')
absolute_grapes_slv_ds = unit_converter.convert_m2_to_gridcell(grapes_slv_ds, grapes_slv_gca)
In [6]:
# Coarsen the data to 0.05x0.05 to match the resolution of GRA2PES in LCC
absolute_grapes_slv_ds_coarse = absolute_grapes_slv_ds.coarsen(lat = 2, lon = 2, boundary = 'trim').sum()

EDGAR¶

In [7]:
# Define a loading function for the EDGAR data

def load_edgar_nc(edgar_path,spec,emi_or_flx,year):
    if spec in ['CH4','CO2']:
        nc_folder_name = f'v8.0_FT2022_GHG_{spec}_{year}_TOTALS_{emi_or_flx}_nc'
        nc_file_name = '_'.join(nc_folder_name.split('_')[:-1]) + '.nc'
    elif spec == 'CO':
        nc_folder_name = f'EDGARv6.1_{spec}_{year}_TOTALS.0.1x0.1'
        nc_file_name = (nc_folder_name) + '.nc'

    nc_fullpath = os.path.join(edgar_path,spec,emi_or_flx,nc_folder_name,nc_file_name)
    try:    
        ds = xr.open_dataset(nc_fullpath)
    except FileNotFoundError as f:
        print(f)
        raise FileNotFoundError(f'There doesnt seem to be data for this configuration:{spec},{emi_or_flx},{year}')
    if spec == 'CO':
        ds = coord_change_360_180(ds)
        ds = ds.rename({'emi_co':'fluxes'})
        ds['fluxes'].attrs['substance'] = 'CO'

    gca_fname = nc_file_name.split('.nc')[0] + '_gca' + '.nc'
    try:
        gca_fullpath = os.path.join(edgar_path,spec,emi_or_flx,nc_folder_name,gca_fname)
        gca_ds = xr.open_dataset(gca_fullpath)
        if spec == 'CO':
            gca_ds = coord_change_360_180(gca_ds)
        ds = add_gca_to_ds(ds,gca_ds)
    except FileNotFoundError:
        print(f'No grid cell area file {gca_fullpath} found. Create with cdo to use. Returning ds without gca')
        return ds

    return ds

def coord_change_360_180(ds):
    return ds.assign_coords(lon=(((ds.lon + 180) % 360) - 180)).sortby('lon')

def add_gca_to_ds(orig_ds,gca_ds):
    orig_ds['grid_cell_area'] = gca_ds['cell_area']
    return orig_ds
In [8]:
#Load Edgar inventories
edgar_path = '/uufs/chpc.utah.edu/common/home/lin-group9/agm/inventories/EDGAR/'
emi_or_flx = 'flx'

# For each species, load the dataset and process
edgar_dss = {
    'CO2': {'year':2021,'ds':None},
    'CH4': {'year':2021,'ds':None},
    'CO': {'year':2018,'ds':None}
}

for spec, info in edgar_dss.items():
    ds = load_edgar_nc(edgar_path,spec,emi_or_flx,info['year'])
    ds['emissions'] = (ds['grid_cell_area'] * ds['fluxes']) * 86400 * 365 * 1E-3  # Emissions in metric tons per year
    ds['emissions'].attrs = {'substance': spec, 'units': 'metric_Ton gridcell-1 year-1'}
    edgar_dss[spec]['ds'] = xr_utils.trim_ds_to_extent(ds,slv_extent)

EPA Massakers¶

Flux values in original nc files are $Flux_{ch4}$ in $[{molec_{CH4}}/{cm^2s}]$.

Convert flux values to emissions values by $Emission_{ch4} = Flux_{ch4} * grid cell area$
Where $Emission_{ch4}$ in $[molec_{ch4}/s]$

CONVERSION to g/yr

$Emissions_{ch4} [g/year] = Emissions_{ch4} [molec_{ch4}/s] * Conversion factor $

$Conversion factor = 1/6.022x10^23 [mole/molec_{ch4}] * 16.04 [g_{ch4}/mole] * 86400 [s/day] * y [days/year] $

The number of days per year (y) depends on whether the given year is a leap year.

In [9]:
# Define a loading function for the EPA data
def load_epa_nc(path,year):
    fname = f'Express_Extension_Gridded_GHGI_Methane_v2_{year}.nc'
    ds = xr.open_dataset(os.path.join(path,fname))
    name_map = {k: k.replace('emi','flx') for k in ds} #in the nc file, the data variables are names "emi..." which is confusing as they are fluxes. so rename to flx...
    ds = ds.rename(name_map)
    return ds
In [10]:
#Load the Maasakkers data
epa_path = '/uufs/chpc.utah.edu/common/home/lin-group9/agm/inventories/Maasakkers'
year = 2020

epa_full = load_epa_nc(epa_path,year).sum(dim='time',keep_attrs = True)
epa_slv = xr_utils.trim_ds_to_extent(epa_full,slv_extent)

#Sum variables
data_variables = list(epa_slv.keys()) #all of the vars in the nc file
emi_variables = [var for var in data_variables if var != 'grid_cell_area'] #drop the grid cell area from the variables list so we can sum
og_units = epa_slv[emi_variables[0]].attrs['units']
for emi_var in emi_variables: #check to make sure the units are the same on all of the emissions variables
    if epa_slv[emi_var].attrs['units'] != og_units:
        raise ValueError(f'The units are different in {emi_var}, so you shouldnt sum')
epa_slv['flx_ch4_sum'] = epa_slv[emi_variables].to_array().sum('variable') #sum along the emissions variables
epa_slv['flx_ch4_sum'].attrs = {'units':og_units} #add the units

#convert flux units to emissions units
# emissions[tonne/yr] = grid_cell_area[cm2] * flx_ch4[molec/cm2/s] * (1/6.022E23)[mol/molec] * 16.04[gch4/mol] * 86400[s/d] * 365[d/y] * 1E-6[tonne/g]
epa_slv['emissions'] = (epa_slv['grid_cell_area']*epa_slv['flx_ch4_sum'])*(1/6.022E23)*16.04*86400*365*1E-6  #multiply the sums by the grid cell area
epa_slv['emissions'].attrs['units'] = 'metric_Ton gridcell-1 year-1'

absolute_epa_slv_yrsum = epa_slv

Compute and Plot Yearly totals¶

In [11]:
# Aggregate to yearly emissions

absolute_grapes_slv_yrsum_coarse = absolute_grapes_slv_ds_coarse.sel(sector='total').sum(dim='datetime',keep_attrs = True)
absolute_grapes_slv_yrsum_coarse = unit_converter.update_all_units(absolute_grapes_slv_yrsum_coarse, 'hr^-1','yr^-1')
In [12]:
#CO2 inventory plot
fig_id = f'2021_gra2pes_co2_fullyear_inventory'
savefig = True
showfig = True

plot_ds = absolute_grapes_slv_yrsum_coarse
spec = 'CO2'
cmap = 'Blues'

fig = plt.figure(figsize = (15,15))
labsize = 44
proj = ccrs.PlateCarree()

ax = plt.axes(projection = proj)
ax.set_extent([map_extent['lon_min'],map_extent['lon_max'],map_extent['lat_min'],map_extent['lat_max']],crs=proj)
request = cimgt.GoogleTiles(style='satellite')
scale = 12.0 # prob have to adjust this
ax.add_image(request,int(scale))

max_value = plot_ds[spec].max().values
vmax_value = max_value*0.6
map = plot_ds[spec].plot.pcolormesh('lon','lat',ax = ax,alpha=0.6,cmap=cmap,add_colorbar=False,edgecolors = (0,0,0,0.1),linewidth = 0.2,vmax = vmax_value)

ax.scatter(wbb_loc['lon'],wbb_loc['lat'],c = 'white',marker='X',s=500,zorder = 10,label = "WBB",edgecolor = 'k',linewidth = 2)

cbformat = matplotlib.ticker.ScalarFormatter()   # create the formatter
cbformat.set_powerlimits((-2,2)) 
cbar = plt.colorbar(map,fraction=0.05,pad = 0.02,format=cbformat)
exp = gen_utils.calculate_exponent(max_value)
cbar.set_label(label = f"$CO_{2}$ emissions (Tonne$\\times 10^{{{exp}}}$)",size = labsize)
cbar.ax.tick_params(labelsize = labsize)


emissions_sum = float(plot_ds[spec].sum())
exp= gen_utils.calculate_exponent(emissions_sum)
sn_es = round(emissions_sum/10**(exp),2)
text = f"$CO_{2}$ sum ={sn_es}$\\times 10^{{{exp}}}$ Tonne"

t1 = fig.text(0.275,0.92,text,fontsize = labsize)
t1.set_bbox(dict(facecolor = (1,1,1,0.7)))
#plt.title("2021 GRA2PES $CO_{2}$ Inventory",fontsize = labsize*1.2)
fig.tight_layout()
ax.set_title("")


# Explicitly set longitude and latitude ticks
xticks = [-112.1,-111.9,-111.7]
yticks = [40.4,40.6,40.8,41.0,41.2]
ax.set_xticks(xticks, crs=proj)
ax.set_yticks(yticks, crs=proj)
lon_formatter = LongitudeFormatter()
lat_formatter = LatitudeFormatter()
ax.xaxis.set_major_formatter(lon_formatter)
ax.yaxis.set_major_formatter(lat_formatter)
ax.set_yticklabels([f"{y:.1f}°" for y in yticks], fontsize=labsize/2)
ax.set_xticklabels([f"{x:.1f}°" for x in xticks], fontsize=labsize/2)
ax.tick_params(
    left=False, labelleft=True,   # hide left labels
    top=False, labeltop=False,     # hide top labels
    right=False, labelright=False,   # show right labels
    bottom=False, labelbottom=True, # show bottom labels
    length=0  # remove the tick marks themselves
)
ax.set_xlabel('')  # remove x-axis label
ax.set_ylabel('')  # remove y-axis label


if savefig:
    fig_name = f'{fig_id}.png'
    fig.savefig(os.path.join(inventory_figures_path,fig_name),dpi=500,bbox_inches = "tight")
if showfig:
    plt.show()
else:
    plt.close()
No description has been provided for this image
In [13]:
#CH4 inventory plot
fig_id = f'2021_gra2pes_ch4_fullyear_inventory'
savefig = True
showfig = True

plot_ds = absolute_grapes_slv_yrsum_coarse

spec = 'CH4'
cmap = 'Reds'

fig = plt.figure(figsize = (15,15))
labsize = 44
proj = ccrs.PlateCarree()

ax = plt.axes(projection = proj)
ax.set_extent([map_extent['lon_min'],map_extent['lon_max'],map_extent['lat_min'],map_extent['lat_max']],crs=proj)
request = cimgt.GoogleTiles(style='satellite')
scale = 12.0 # prob have to adjust this
ax.add_image(request,int(scale))


max_value = plot_ds[spec].max().values
vmax_value = max_value*0.9
map = plot_ds[spec].plot.pcolormesh('lon','lat',ax = ax,alpha=0.6,cmap=cmap,add_colorbar=False,edgecolors = (0,0,0,0.1),linewidth = 0.2,vmax = vmax_value)

ax.scatter(wbb_loc['lon'],wbb_loc['lat'],c = 'white',marker='X',s=500,zorder = 10,label = "WBB",edgecolor = 'k',linewidth = 2)

cbformat = matplotlib.ticker.ScalarFormatter()   # create the formatter
cbformat.set_powerlimits((-2,2)) 
cbar = plt.colorbar(map,fraction=0.05,pad = 0.02,format=cbformat)
exp = gen_utils.calculate_exponent(max_value)
cbar.set_label(label = f"$CH_{4}$ emissions (Tonne$\\times 10^{{{exp}}}$)",size = labsize)
cbar.ax.tick_params(labelsize = labsize)

emissions_sum = float(plot_ds[spec].sum())
exp= gen_utils.calculate_exponent(emissions_sum)
sn_es = round(emissions_sum/10**(exp),2)
text = f"$CH_{4}$ sum ={sn_es}$\\times 10^{{{exp}}}$ Tonne"
ax.set_title("")

t1 = fig.text(0.275,0.92,text,fontsize = labsize)
t1.set_bbox(dict(facecolor = (1,1,1,0.8)))
fig.tight_layout()

# Explicitly set longitude and latitude ticks
xticks = [-112.1,-111.9,-111.7]
yticks = [40.4,40.6,40.8,41.0,41.2]
ax.set_xticks(xticks, crs=proj)
ax.set_yticks(yticks, crs=proj)
lon_formatter = LongitudeFormatter()
lat_formatter = LatitudeFormatter()
ax.xaxis.set_major_formatter(lon_formatter)
ax.yaxis.set_major_formatter(lat_formatter)
ax.set_yticklabels([f"{y:.1f}°" for y in yticks], fontsize=labsize/2)
ax.set_xticklabels([f"{x:.1f}°" for x in xticks], fontsize=labsize/2)
ax.tick_params(
    left=False, labelleft=True,   # hide left labels
    top=False, labeltop=False,     # hide top labels
    right=False, labelright=False,   # show right labels
    bottom=False, labelbottom=True, # show bottom labels
    length=0  # remove the tick marks themselves
)
ax.set_xlabel('')  # remove x-axis label
ax.set_ylabel('')  # remove y-axis label


if savefig:
    fig_name = f'{fig_id}.png'
    fig.savefig(os.path.join(inventory_figures_path,fig_name),dpi=500,bbox_inches = "tight")
if showfig:
    plt.show()
else:
    plt.close()
No description has been provided for this image
In [14]:
#CO inventory plot
fig_id = f'2021_gra2pes_co_fullyear_inventory'
savefig = True
showfig = True

plot_ds = absolute_grapes_slv_yrsum_coarse

spec = 'CO'
cmap = 'Oranges'

fig = plt.figure(figsize = (15,15))
labsize = 44
proj = ccrs.PlateCarree()

ax = plt.axes(projection = proj)
ax.set_extent([map_extent['lon_min'],map_extent['lon_max'],map_extent['lat_min'],map_extent['lat_max']],crs=proj)
request = cimgt.GoogleTiles(style='satellite')
scale = 12.0 # prob have to adjust this
ax.add_image(request,int(scale))


max_value = plot_ds[spec].max().values
vmax_value = max_value*0.9
map = plot_ds[spec].plot.pcolormesh('lon','lat',ax = ax,alpha=0.6,cmap=cmap,add_colorbar=False,edgecolors = (0,0,0,0.1),linewidth = 0.2,vmax = vmax_value)

ax.scatter(wbb_loc['lon'],wbb_loc['lat'],c = 'white',marker='X',s=500,zorder = 10,label = "WBB",edgecolor = 'k',linewidth = 2)

cbformat = matplotlib.ticker.ScalarFormatter()   # create the formatter
cbformat.set_powerlimits((-2,2)) 
cbar = plt.colorbar(map,fraction=0.05,pad = 0.02,format=cbformat)
exp = gen_utils.calculate_exponent(max_value)
cbar.set_label(label = f"$CO$ emissions (Tonne$\\times 10^{{{exp}}}$)",size = labsize)
cbar.ax.tick_params(labelsize = labsize)

# Explicitly set longitude and latitude ticks
xticks = [-112.1,-111.9,-111.7]
yticks = [40.4,40.6,40.8,41.0,41.2]
ax.set_xticks(xticks, crs=proj)
ax.set_yticks(yticks, crs=proj)
lon_formatter = LongitudeFormatter()
lat_formatter = LatitudeFormatter()
ax.xaxis.set_major_formatter(lon_formatter)
ax.yaxis.set_major_formatter(lat_formatter)
ax.set_yticklabels([f"{y:.1f}°" for y in yticks], fontsize=labsize/2)
ax.set_xticklabels([f"{x:.1f}°" for x in xticks], fontsize=labsize/2)
ax.tick_params(
    left=False, labelleft=True,   # hide left labels
    top=False, labeltop=False,     # hide top labels
    right=False, labelright=False,   # show right labels
    bottom=False, labelbottom=True, # show bottom labels
    length=0  # remove the tick marks themselves
)
ax.set_xlabel('')  # remove x-axis label
ax.set_ylabel('')  # remove y-axis label

emissions_sum = float(plot_ds[spec].sum())
exp=gen_utils.calculate_exponent(emissions_sum)
sn_es = round(emissions_sum/10**(exp),2)
text = f"$CO$ sum ={sn_es}$\\times 10^{{{exp}}}$ Tonne"
ax.set_title("")

t1 = fig.text(0.255,0.92,text,fontsize = labsize)
t1.set_bbox(dict(facecolor = (1,1,1,0.8)))
fig.tight_layout()

if savefig:
    fig_name = f'{fig_id}.png'
    fig.savefig(os.path.join(inventory_figures_path,fig_name),dpi=500,bbox_inches = "tight")
if showfig:
    plt.show()
else:
    plt.close()
No description has been provided for this image

EDGAR¶

In [15]:
#CO2 inventory plot
fig_id = f'2021_edgar_co2_fullyear_inventory'
savefig = True
showfig = True

spec = 'CO2'
plot_ds = edgar_dss[spec]['ds']
var = 'emissions'
cmap = 'Blues'

fig = plt.figure(figsize = (15,15))
labsize = 44
proj = ccrs.PlateCarree()

ax = plt.axes(projection = proj)
ax.set_extent([map_extent['lon_min'],map_extent['lon_max'],map_extent['lat_min'],map_extent['lat_max']],crs=proj)
request = cimgt.GoogleTiles(style='satellite')
scale = 12.0 # prob have to adjust this
ax.add_image(request,int(scale))


max_value = plot_ds[var].max().values
vmax_value = max_value*0.6
map = plot_ds[var].plot.pcolormesh('lon','lat',ax = ax,alpha=0.6,cmap=cmap,add_colorbar=False,edgecolors = (0,0,0,0.1),linewidth = 0.2,vmax = vmax_value)

ax.scatter(wbb_loc['lon'],wbb_loc['lat'],c = 'white',marker='X',s=500,zorder = 10,label = "WBB",edgecolor = 'k',linewidth = 2)

cbformat = matplotlib.ticker.ScalarFormatter()   # create the formatter
cbformat.set_powerlimits((-2,2)) 
cbar = plt.colorbar(map,fraction=0.05,pad = 0.02,format=cbformat)
exp = gen_utils.calculate_exponent(max_value)
cbar.set_label(label = f"$CO_{2}$ emissions (Tonne$\\times 10^{{{exp}}}$)",size = labsize)
cbar.ax.tick_params(labelsize = labsize)

#ax.legend(fontsize = labsize-8,loc = 4)

emissions_sum = float(plot_ds[var].sum())
exp= gen_utils.calculate_exponent(emissions_sum)
sn_es = round(emissions_sum/10**(exp),2)
text = f"$CO_{2}$ sum ={sn_es}$\\times 10^{{{exp}}}$ Tonne"

t1 = fig.text(0.24,0.92,text,fontsize = labsize)
t1.set_bbox(dict(facecolor = (1,1,1,0.8)))
fig.tight_layout()
ax.set_title("")

if savefig:
    fig_name = f'{fig_id}.png'
    fig.savefig(os.path.join(inventory_figures_path,fig_name),dpi=500,bbox_inches = "tight")
if showfig:
    plt.show()
else:
    plt.close()
No description has been provided for this image
In [16]:
#CH4 inventory plot
fig_id = f'2021_edgar_ch4_fullyear_inventory'
savefig = True
showfig = True

spec = 'CH4'
plot_ds = edgar_dss[spec]['ds']
var = 'emissions'
cmap = 'Reds'

fig = plt.figure(figsize = (15,15))
labsize = 44
proj = ccrs.PlateCarree()

ax = plt.axes(projection = proj)
ax.set_extent([map_extent['lon_min'],map_extent['lon_max'],map_extent['lat_min'],map_extent['lat_max']],crs=proj)
request = cimgt.GoogleTiles(style='satellite')
scale = 12.0 # prob have to adjust this
ax.add_image(request,int(scale))


max_value = plot_ds[var].max().values
vmax_value = max_value*0.9
map = plot_ds[var].plot.pcolormesh('lon','lat',ax = ax,alpha=0.6,cmap=cmap,add_colorbar=False,edgecolors = (0,0,0,0.1),linewidth = 0.2,vmax = vmax_value)

ax.scatter(wbb_loc['lon'],wbb_loc['lat'],c = 'white',marker='X',s=500,zorder = 10,label = "WBB",edgecolor = 'k',linewidth = 2)

cbformat = matplotlib.ticker.ScalarFormatter()   # create the formatter
cbformat.set_powerlimits((-2,2)) 
cbar = plt.colorbar(map,fraction=0.05,pad = 0.02,format=cbformat)
exp = gen_utils.calculate_exponent(max_value)
cbar.set_label(label = f"$CH_{4}$ emissions (Tonne$\\times 10^{{{exp}}}$)",size = labsize)
cbar.ax.tick_params(labelsize = labsize)

#ax.legend(fontsize = labsize-8,loc = 4)

emissions_sum = float(plot_ds[var].sum())
exp= gen_utils.calculate_exponent(emissions_sum)
sn_es = round(emissions_sum/10**(exp),2)
text = f"$CH_{4}$ sum ={sn_es}$\\times 10^{{{exp}}}$ Tonne"
ax.set_title("")

t1 = fig.text(0.275,0.92,text,fontsize = labsize)
t1.set_bbox(dict(facecolor = (1,1,1,0.8)))
fig.tight_layout()

if savefig:
    fig_name = f'{fig_id}.png'
    fig.savefig(os.path.join(inventory_figures_path,fig_name),dpi=500,bbox_inches = "tight")
if showfig:
    plt.show()
else:
    plt.close()
No description has been provided for this image
In [17]:
#CO inventory plot
fig_id = f'2018_edgar_co_fullyear_inventory'
savefig = True
showfig = True

spec = 'CO'
plot_ds = edgar_dss[spec]['ds']
var = 'emissions'
cmap = 'Oranges'

fig = plt.figure(figsize = (15,15))
labsize = 44
proj = ccrs.PlateCarree()

ax = plt.axes(projection = proj)
ax.set_extent([map_extent['lon_min'],map_extent['lon_max'],map_extent['lat_min'],map_extent['lat_max']],crs=proj)
request = cimgt.GoogleTiles(style='satellite')
scale = 12.0 # prob have to adjust this
ax.add_image(request,int(scale))


max_value = plot_ds[var].max().values
vmax_value = max_value*0.9
map = plot_ds[var].plot.pcolormesh('lon','lat',ax = ax,alpha=0.6,cmap=cmap,add_colorbar=False,edgecolors = (0,0,0,0.1),linewidth = 0.2,vmax = vmax_value)

ax.scatter(wbb_loc['lon'],wbb_loc['lat'],c = 'white',marker='X',s=500,zorder = 10,label = "WBB",edgecolor = 'k',linewidth = 2)

cbformat = matplotlib.ticker.ScalarFormatter()   # create the formatter
cbformat.set_powerlimits((-2,2)) 
cbar = plt.colorbar(map,fraction=0.05,pad = 0.02,format=cbformat)
exp = gen_utils.calculate_exponent(max_value)
cbar.set_label(label = f"$CO$ emissions (Tonne$\\times 10^{{{exp}}}$)",size = labsize)
cbar.ax.tick_params(labelsize = labsize)

#ax.legend(fontsize = labsize-8,loc = 4)

emissions_sum = float(plot_ds[var].sum())
exp=gen_utils.calculate_exponent(emissions_sum)
sn_es = round(emissions_sum/10**(exp),2)
text = f"$CO$ sum ={sn_es}$\\times 10^{{{exp}}}$ Tonne"
ax.set_title("")

t1 = fig.text(0.285,0.92,text,fontsize = labsize)
t1.set_bbox(dict(facecolor = (1,1,1,0.8)))
fig.tight_layout()

if savefig:
    fig_name = f'{fig_id}.png'
    fig.savefig(os.path.join(inventory_figures_path,fig_name),dpi=500,bbox_inches = "tight")
if showfig:
    plt.show()
else:
    plt.close()
No description has been provided for this image

EPA¶

In [18]:
#CH4 inventory plot
fig_id = f'2020_epa_ch4_fullyear_inventory'
savefig = True
showfig = True

spec = 'CH4'
plot_ds = absolute_epa_slv_yrsum
var = 'emissions'
cmap = 'Reds'

fig = plt.figure(figsize = (15,15))
labsize = 44
proj = ccrs.PlateCarree()

ax = plt.axes(projection = proj)
ax.set_extent([map_extent['lon_min'],map_extent['lon_max'],map_extent['lat_min'],map_extent['lat_max']],crs=proj)
request = cimgt.GoogleTiles(style='satellite')
scale = 12.0 # prob have to adjust this
ax.add_image(request,int(scale))


max_value = plot_ds[var].max().values
vmax_value = max_value*0.9
map = plot_ds[var].plot.pcolormesh('lon','lat',ax = ax,alpha=0.6,cmap=cmap,add_colorbar=False,edgecolors = (0,0,0,0.1),linewidth = 0.2,vmax = vmax_value)

ax.scatter(wbb_loc['lon'],wbb_loc['lat'],c = 'white',marker='X',s=500,zorder = 10,label = "WBB",edgecolor = 'k',linewidth = 2)

cbformat = matplotlib.ticker.ScalarFormatter()   # create the formatter
cbformat.set_powerlimits((-2,2)) 
cbar = plt.colorbar(map,fraction=0.05,pad = 0.02,format=cbformat)
exp = gen_utils.calculate_exponent(max_value)
cbar.set_label(label = f"$CH_{4}$ emissions (Tonne$\\times 10^{{{exp}}}$)",size = labsize)
cbar.ax.tick_params(labelsize = labsize)

#ax.legend(fontsize = labsize-8,loc = 4)

emissions_sum = float(plot_ds[var].sum())
exp= gen_utils.calculate_exponent(emissions_sum)
sn_es = round(emissions_sum/10**(exp),2)
text = f"$CH_{4}$ sum ={sn_es}$\\times 10^{{{exp}}}$ Tonne"
ax.set_title("")

t1 = fig.text(0.275,0.92,text,fontsize = labsize)
t1.set_bbox(dict(facecolor = (1,1,1,0.8)))
fig.tight_layout()

if savefig:
    fig_name = f'{fig_id}.png'
    fig.savefig(os.path.join(inventory_figures_path,fig_name),dpi=500,bbox_inches = "tight")
if showfig:
    plt.show()
else:
    plt.close()
No description has been provided for this image

Ratios¶

In [19]:
# Comparing ratios with EM27 means we only want to look at the daylight hours

daylight_hours = [14,15,16,17,18,19,20,21,22,23,0,1]
grapes_daylight_ds = absolute_grapes_slv_ds_coarse.where(absolute_grapes_slv_ds_coarse['datetime'].dt.hour.isin(daylight_hours),drop = True)
grapes_daylight_ds_total = grapes_daylight_ds.sel(sector='total')
grapes_daylight_ds = grapes_daylight_ds.sel(sector=grapes_daylight_ds.sector[grapes_daylight_ds.sector != "total"])

Yearly, daylight¶

In [20]:
# Aggregate to yearly emissions during daylight hours
absolute_grapes_slv_yrsum_daylight = grapes_daylight_ds_total.sum(dim='datetime',keep_attrs = True)
absolute_grapes_slv_yrsum_daylight = unit_converter.update_all_units(absolute_grapes_slv_yrsum_daylight, 'hr^-1','yr^-1')
In [21]:
# Print the ratios as reported in the manuscript
ir = xr_utils.InventoryRatios(absolute_grapes_slv_yrsum_daylight,molar_masses)
print(f"CO:CO2 ratio = {ir.get_total_ratio('CO','CO2')*1000} permil")
print(f"CH4:CO2 ratio = {ir.get_total_ratio('CH4','CO2')*1000} permil")
CO:CO2 ratio = 6.106649237470725 permil
CH4:CO2 ratio = 3.973079270326021 permil

Seasonal, daylight¶

In [22]:
# Define the seasons
seasons = {'DJF':{'months':[12,1,2]},
           'MAM':{'months':[3,4,5]},
           'JJA':{'months':[6,7,8]},
           'SON':{'months':[9,10,11]}
}
In [23]:
# Get and print the seasonal ratios

grapes_season_ratios = {}
for season in seasons.keys():
    months = seasons[season]['months']
    season_sum_ds = grapes_daylight_ds_total.where(grapes_daylight_ds_total['datetime'].dt.month.isin(months),drop = True).sum(dim='datetime',keep_attrs = True)
    season_sum_ds = unit_converter.update_all_units(season_sum_ds, 'hr^-1','season^-1')

    ir = xr_utils.InventoryRatios(season_sum_ds,molar_masses)
    ch4_co2 = ir.get_total_ratio('CH4','CO2')
    co_co2 = ir.get_total_ratio('CO','CO2')
    grapes_season_ratios[season] = {'ch4_co2':ch4_co2,'co_co2':co_co2}

    print(f"{season}")
    print(f"CH4:CO2 ratio = {ir.get_total_ratio('CH4','CO2')*1000} permil")
    print(f"CO:CO2 ratio = {ir.get_total_ratio('CO','CO2')*1000} permil")
    print("")
DJF
CH4:CO2 ratio = 3.3845618480081976 permil
CO:CO2 ratio = 5.506621140658151 permil

MAM
CH4:CO2 ratio = 3.9530358845863596 permil
CO:CO2 ratio = 6.105654891904161 permil

JJA
CH4:CO2 ratio = 4.570129302373411 permil
CO:CO2 ratio = 6.672402895752505 permil

SON
CH4:CO2 ratio = 4.0778679621754 permil
CO:CO2 ratio = 6.235658154424306 permil

Seasonal Sector¶

In [24]:
# Get the seasonal data separated by sector 

flat_data = []
for season, config in seasons.items():
    months = config['months']
    season_sum_ds = grapes_daylight_ds.where(
        grapes_daylight_ds['datetime'].dt.month.isin(months),
        drop=True
    ).sum(dim='datetime', keep_attrs=True)
    
    # Convert to per-season units
    season_sum_ds = unit_converter.update_all_units(season_sum_ds, 'hr^-1', 'season^-1')
    season_sum = season_sum_ds.sum(dim=['lat', 'lon'], keep_attrs=True)
    season_sum = unit_converter.update_all_units(season_sum, 'gridcell^-1', '')

    season_df = season_sum.to_dataframe().reset_index()

    for var in season_sum.data_vars:
        for sector in season_df['sector'].unique():
            value = season_df[season_df['sector'] == sector][var].values[0]
            flat_data.append([season, var, sector, value])

season_df = pd.DataFrame(flat_data, columns=['Season', 'Variable', 'Sector', 'Sum'])

# Group sectors
sectors_to_keep = ['OFFROAD', 'ONROAD_GAS', 'RES']
season_df['Sector_Grouped'] = season_df['Sector'].apply(lambda x: x if x in sectors_to_keep else 'Other')

# Summarize data
season_df_grouped = (
    season_df
    .groupby(['Season', 'Variable', 'Sector_Grouped'], as_index=False, observed=False)
    .agg({'Sum': 'sum'})
    .rename(columns={'Sector_Grouped': 'Sector'})
)

# Rename sectors for clarity
sector_rename_map = {
    'RES': 'Residential',
    'ONROAD_GAS': 'On Road Gas',
    'OFFROAD': 'Off Road Gas'
}
season_df_grouped['Sector'] = season_df_grouped['Sector'].replace(sector_rename_map)
In [25]:
palette = ['#ffa600','#003f5c','#7a5195','#ef5675']
sector_order = ['Residential', 'On Road Gas', 'Off Road Gas', 'Other']
var = 'CO2'
plot_utils.plot_stacked_seasonal_bars(
    df_grouped=season_df_grouped,
    var=var,
    fig_path=inventory_figures_path,
    fig_id=f'{var}_seasonal_inventory_sectors',
    palette=palette,
    sector_order=sector_order,
    savefig=True,
    showfig=True,
    legfontsize=40,
)
No description has been provided for this image
/uufs/chpc.utah.edu/common/home/u0890904/LAIR_1/atmos/utils/plot_utils.py:228: UserWarning: Tight layout not applied. The bottom and top margins cannot be made large enough to accommodate all Axes decorations.
  fig_leg.tight_layout()
No description has been provided for this image

Gridded Ratios¶

These cells allow for visualization of the grid cell level ratios.

In [26]:
# Define the inventory regridder
ir = xr_utils.InventoryRatios(absolute_grapes_slv_yrsum_daylight,molar_masses)
In [27]:
ratio_ds = ir.get_gc_ratio('CH4','CO2',num_quantile_filter = 0.25, denom_quantile_filter = 0.5)
ratio_ds['CO2_eq_sum'] = ratio_ds['CO2_eq_sum'].fillna(0)

ratio_id = 'ch4_co2'
fig_id = f'ch4co2_gridcell'
savefig = True
showfig = True

da = ratio_ds[ratio_id]*1000
alpha_var = 'CO2_eq_sum'
min_alpha = 0.3
alphas = ratio_ds[alpha_var].values
alphas = (alphas - alphas.min())/(alphas.max()-alphas.min())*(1-min_alpha)+min_alpha

labsize = 40
proj = ccrs.PlateCarree()

fig = plt.figure(figsize = (12,12))
ax = plt.axes(projection = proj)
ax.set_extent([map_extent['lon_min'],map_extent['lon_max'],map_extent['lat_min'],map_extent['lat_max']],crs=proj)
request = cimgt.GoogleTiles(style='satellite')
scale = 12.0 # prob have to adjust this
ax.add_image(request,int(scale))

map = da.plot.pcolormesh('lon','lat',ax = ax,alpha=alphas,cmap='viridis',add_colorbar=False,edgecolors = (0,0,0,1),linewidth = 2,vmin = 0,vmax=10)#,vmin=0,vmax = 10)
da.plot.pcolormesh('lon','lat', ax = ax,facecolors='none', edgecolors=(0, 0, 0, 1), linewidth=2,add_colorbar=False)

ax.scatter(wbb_loc['lon'],wbb_loc['lat'],c = 'white',marker='X',s=500,zorder = 10,label = "WBB",edgecolor = 'k',linewidth = 2)

for lat in da.lat.values:
    for lon in da.lon.values:
        value = da.sel(lat=lat,lon=lon).values
        if value != value:
            continue
        #plt.text(lon,lat,f'{value:.1f}',color='k',ha='center',va='center',size=15)

source_types_to_plot = ['landfills','refineries']
for source_type,sources in point_sources.items():
    if source_type not in source_types_to_plot:
        continue
    locs = sources['locs']
    plot_details = sources['plot_details']
    for loc in locs:
        ax.scatter(loc[0],loc[1],**plot_details)
    

ax.get_figure().gca().set_title("")

cbar = plt.colorbar(map,fraction=0.045,pad = 0.02,location = 'right')
#cbar.set_label(label = r"${\alpha_{CH4}}^o/_{oo}$",size = labsize)
cbar.ax.tick_params(labelsize = labsize)
cbar.set_ticks([0,2,4,6,8,10])
cbar.ax.set_yticklabels([0,2,4,6,8,">10"])

# total_ratio = ir.get_total_ratio('CH4','CO2')*1000
# text = r"${\alpha}_{{CH_{4}},{EI},{tot}}$"
# text += f" = {total_ratio:.2f}"
# text = text+ "$^o/_{oo}$"
# t1 = fig.text(0.405,0.832,text,fontsize = labsize-5)
# t1.set_bbox(dict(facecolor = 'white'))

# Explicitly set longitude and latitude ticks
xticks = [-112.1,-111.9,-111.7]
yticks = [40.4,40.6,40.8,41.0,41.2]
ax.set_xticks(xticks, crs=proj)
ax.set_yticks(yticks, crs=proj)
lon_formatter = LongitudeFormatter()
lat_formatter = LatitudeFormatter()
ax.xaxis.set_major_formatter(lon_formatter)
ax.yaxis.set_major_formatter(lat_formatter)
ax.set_yticklabels([f"{y:.1f}°" for y in yticks], fontsize=labsize/2)
ax.set_xticklabels([f"{x:.1f}°" for x in xticks], fontsize=labsize/2)
ax.tick_params(
    left=False, labelleft=True,   # hide left labels
    top=False, labeltop=False,     # hide top labels
    right=False, labelright=False,   # show right labels
    bottom=False, labelbottom=True, # show bottom labels
    length=0  # remove the tick marks themselves
)
ax.set_xlabel('')  # remove x-axis label
ax.set_ylabel('')  # remove y-axis label

if savefig:
    fig_name = f'{fig_id}.png'
    fig.savefig(os.path.join(inventory_figures_path,fig_name),dpi=500,bbox_inches = "tight")
if showfig:
    plt.show()
else:
    plt.close()
No description has been provided for this image
In [28]:
ratio_ds = ir.get_gc_ratio('CO','CO2',num_quantile_filter = 0.25, denom_quantile_filter = 0.5)
ratio_ds['CO2_eq_sum'] = ratio_ds['CO2_eq_sum'].fillna(0)

ratio_id = 'co_co2'
fig_id = f'coco2_gridcell'
savefig = True
showfig = True

da = ratio_ds[ratio_id]*1000
alpha_var = 'CO2_eq_sum'
min_alpha = 0.3
alphas = ratio_ds[alpha_var].values
alphas = (alphas - alphas.min())/(alphas.max()-alphas.min())*(1-min_alpha)+min_alpha

labsize = 40
proj = ccrs.PlateCarree()

fig = plt.figure(figsize = (12,12))
ax = plt.axes(projection = proj)
ax.set_extent([map_extent['lon_min'],map_extent['lon_max'],map_extent['lat_min'],map_extent['lat_max']],crs=proj)
request = cimgt.GoogleTiles(style='satellite')
scale = 12.0 # prob have to adjust this
ax.add_image(request,int(scale))

map = da.plot.pcolormesh('lon','lat',ax = ax,alpha=alphas,cmap='viridis',add_colorbar=False,edgecolors = (0,0,0,1),linewidth = 2,vmin = 0,vmax=10)#,vmin=0,vmax = 10)
da.plot.pcolormesh('lon','lat', ax = ax,facecolors='none', edgecolors=(0, 0, 0, 1), linewidth=2,add_colorbar=False)

ax.scatter(wbb_loc['lon'],wbb_loc['lat'],c = 'white',marker='X',s=500,zorder = 10,label = "WBB",edgecolor = 'k',linewidth = 2)

for lat in da.lat.values:
    for lon in da.lon.values:
        value = da.sel(lat=lat,lon=lon).values
        if value != value:
            continue
        #plt.text(lon,lat,f'{value:.1f}',color='k',ha='center',va='center',size=15)

# source_types_to_plot = ['landfills','refineries']
# for source_type,sources in point_sources.items():
#     if source_type not in source_types_to_plot:
#         continue
#     locs = sources['locs']
#     plot_details = sources['plot_details']
#     for loc in locs:
#         ax.scatter(loc[0],loc[1],**plot_details)
    

ax.get_figure().gca().set_title("")

cbar = plt.colorbar(map,fraction=0.045,pad = 0.02,location = 'right')
#cbar.set_label(label = r"${\alpha_{CH4}}^o/_{oo}$",size = labsize)
cbar.ax.tick_params(labelsize = labsize)
cbar.set_ticks([0,2,4,6,8,10])
cbar.ax.set_yticklabels([0,2,4,6,8,">10"])

# total_ratio = ir.get_total_ratio('CH4','CO2')*1000
# text = r"${\alpha}_{{CH_{4}},{EI},{tot}}$"
# text += f" = {total_ratio:.2f}"
# text = text+ "$^o/_{oo}$"
# t1 = fig.text(0.405,0.832,text,fontsize = labsize-5)
# t1.set_bbox(dict(facecolor = 'white'))

# Explicitly set longitude and latitude ticks
xticks = [-112.1,-111.9,-111.7]
yticks = [40.4,40.6,40.8,41.0,41.2]
ax.set_xticks(xticks, crs=proj)
ax.set_yticks(yticks, crs=proj)
lon_formatter = LongitudeFormatter()
lat_formatter = LatitudeFormatter()
ax.xaxis.set_major_formatter(lon_formatter)
ax.yaxis.set_major_formatter(lat_formatter)
ax.set_yticklabels([f"{y:.1f}°" for y in yticks], fontsize=labsize/2)
ax.set_xticklabels([f"{x:.1f}°" for x in xticks], fontsize=labsize/2)
ax.tick_params(
    left=False, labelleft=True,   # hide left labels
    top=False, labeltop=False,     # hide top labels
    right=False, labelright=False,   # show right labels
    bottom=False, labelbottom=True, # show bottom labels
    length=0  # remove the tick marks themselves
)
ax.set_xlabel('')  # remove x-axis label
ax.set_ylabel('')  # remove y-axis label

if savefig:
    fig_name = f'{fig_id}.png'
    fig.savefig(os.path.join(inventory_figures_path,fig_name),dpi=500,bbox_inches = "tight")
if showfig:
    plt.show()
else:
    plt.close()
No description has been provided for this image