Module hytraj.hymodel
Expand source code
import glob
import pandas as pd
import numpy as np
import xarray as xr
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap, addcyclic, cm
def normalize(data):
data = (data - data.min()) / (data.max() - data.min())
return data
class HyReceptor:
def __init__(
self,
ozone,
traj,
station_name="Davis",
latx=np.arange(-90, 91, 1),
lonx=np.arange(-180, 181, 1),
):
self.ozone = ozone
self.traj = traj[station_name]
self.lat = self.traj.sel(geo="lat").to_pandas()
self.lon = self.traj.sel(geo="lon").to_pandas()
self.latx = latx
self.lonx = lonx
self.dates = self.lat.columns
self.station = station_name
self.location = self.traj.isel(step=0, time=0).sel(geo=["lat", "lon"]).values
def get_density(self, lon, lat, density=True):
density, lonx, latx = np.histogram2d(
lon, lat, [self.lonx, self.latx], density=density
)
return density
def get_weight(self, psc):
wgt = np.ones_like(psc)
avg = np.average(psc)
wgt[psc >= 2 * avg] = 1.0
wgt[(avg <= psc) & (psc < 2 * avg)] = 0.75
wgt[(0.5 * avg <= psc) & (psc < avg)] = 0.5
wgt[(psc < 0.5 * avg)] = 0.25
return wgt
def to_nc(self, data, name="Potential Source Contribution Function"):
pscf = xr.DataArray(
data,
coords=[self.lonx[:-1], self.latx[:-1]],
dims=["Longitude", "Latitude"],
)
pscf.name = name
pscf.attrs["station_name"] = self.station
return pscf
def calculate_pscf(self, thresh=0.5, multi=False, weighted=True):
self.ozone_threshold = self.ozone.quantile(thresh)
sub = self.ozone[self.ozone > self.ozone_threshold]
slat = self.lat[sub.index]
slon = self.lon[sub.index]
self.dates_with_more_than_threshold_ozone = sub.index
num1 = self.get_density(
self.lon.values.flatten(), self.lat.values.flatten(), density=False
)
num2 = self.get_density(
slon.values.flatten(), slat.values.flatten(), density=False
)
self.pscf = num2 / num1
self.rt_pscf = num2
if weighted:
wgt = self.get_weight(self.rt_pscf)
self.pscf *= wgt
self.pscf = self.to_nc(self.pscf, name="PSCF")
if not multi:
return self.pscf
else:
return num2, num1, np.sum(num1)
def calculate_cwt(self, weighted=True):
cwt = np.zeros((self.lonx.shape[0] - 1, self.latx.shape[0] - 1))
tau = np.zeros_like(cwt)
nums = np.zeros_like(cwt)
for column in self.lat.columns:
tlen = float(len(self.lat[column].values))
num = self.get_density(
self.lon[column].values.flatten(),
self.lat[column].values.flatten(),
density=False,
)
ttau = num / tlen
cwt += ttau * self.ozone[column]
tau += ttau
nums += num
self.cwt = cwt / tau
self.rt_cwt = nums
if weighted:
wgt = self.get_weight(self.rt_cwt)
self.cwt *= wgt
self.cwt = self.to_nc(self.cwt, name="CWT")
return self.cwt
def calculate_rtwc(self, normalise=True):
err = 100
bcwt = self.calculate_cwt(weighted=True)
bcwt = np.nan_to_num(bcwt.values)
while err >= 0.001:
cwt = np.zeros((self.lonx.shape[0] - 1, self.latx.shape[0] - 1))
tau = np.zeros_like(cwt)
nums = np.zeros_like(cwt)
for column in self.lat.columns:
ozo = self.ozone[column]
num = self.get_density(
self.lon[column].values.flatten(),
self.lat[column].values.flatten(),
density=False,
)
tlen = float(len(self.lat[column].values))
ttau = num / tlen
tavg = np.sum(bcwt * num / tlen)
cwt += ttau * ozo * bcwt / tavg
tau += ttau
nums += num
cwt = np.nan_to_num(cwt / tau)
err = (cwt - bcwt) / bcwt
err = np.nansum(err)
bcwt = cwt
if normalise:
bcwt = normalize(bcwt)
bcwt[bcwt == 0] = np.nan
self.rtwc = self.to_nc(bcwt, name="RTWC")
return self.rtwc
def plot_map(self, pscf, boundinglat=-20, axes=[]):
if len(axes) == 0:
fig, ax = plt.subplots(1, 1, figsize=(6, 6))
cax = fig.add_axes([0.15, 0.06, 0.7, 0.04])
else:
fig, ax, cax = axes
m = Basemap(
projection="spstere", lon_0=180, boundinglat=boundinglat, round=True, ax=ax
)
data, lonx = addcyclic(pscf.T.values, pscf["Longitude"])
latx, lonx = np.meshgrid(pscf["Latitude"], lonx)
lonx, latx = m(lonx, latx)
if pscf.max().values <= 1:
h = m.contourf(lonx, latx, data.T, levels=np.arange(0, 1.1, 0.1))
else:
levels = np.arange(0, pscf.max(), 2)
h = m.contourf(lonx, latx, data.T, levels=levels)
plt.colorbar(h, cax=cax, orientation="horizontal")
m.drawcoastlines(linewidth=1.5)
m.drawcountries(linewidth=0.55)
ax.set_title(self.station, fontweight="bold")
return fig, ax, m
Functions
def normalize(data)
-
Expand source code
def normalize(data): data = (data - data.min()) / (data.max() - data.min()) return data
Classes
class HyReceptor (ozone, traj, station_name='Davis', latx=array([-90, -89, -88, -87, -86, -85, -84, -83, -82, -81, -80, -79, -78, -77, -76, -75, -74, -73, -72, -71, -70, -69, -68, -67, -66, -65, -64, -63, -62, -61, -60, -59, -58, -57, -56, -55, -54, -53, -52, -51, -50, -49, -48, -47, -46, -45, -44, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90]), lonx=array([-180, -179, -178, -177, -176, -175, -174, -173, -172, -171, -170, -169, -168, -167, -166, -165, -164, -163, -162, -161, -160, -159, -158, -157, -156, -155, -154, -153, -152, -151, -150, -149, -148, -147, -146, -145, -144, -143, -142, -141, -140, -139, -138, -137, -136, -135, -134, -133, -132, -131, -130, -129, -128, -127, -126, -125, -124, -123, -122, -121, -120, -119, -118, -117, -116, -115, -114, -113, -112, -111, -110, -109, -108, -107, -106, -105, -104, -103, -102, -101, -100, -99, -98, -97, -96, -95, -94, -93, -92, -91, -90, -89, -88, -87, -86, -85, -84, -83, -82, -81, -80, -79, -78, -77, -76, -75, -74, -73, -72, -71, -70, -69, -68, -67, -66, -65, -64, -63, -62, -61, -60, -59, -58, -57, -56, -55, -54, -53, -52, -51, -50, -49, -48, -47, -46, -45, -44, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180]))
-
Expand source code
class HyReceptor: def __init__( self, ozone, traj, station_name="Davis", latx=np.arange(-90, 91, 1), lonx=np.arange(-180, 181, 1), ): self.ozone = ozone self.traj = traj[station_name] self.lat = self.traj.sel(geo="lat").to_pandas() self.lon = self.traj.sel(geo="lon").to_pandas() self.latx = latx self.lonx = lonx self.dates = self.lat.columns self.station = station_name self.location = self.traj.isel(step=0, time=0).sel(geo=["lat", "lon"]).values def get_density(self, lon, lat, density=True): density, lonx, latx = np.histogram2d( lon, lat, [self.lonx, self.latx], density=density ) return density def get_weight(self, psc): wgt = np.ones_like(psc) avg = np.average(psc) wgt[psc >= 2 * avg] = 1.0 wgt[(avg <= psc) & (psc < 2 * avg)] = 0.75 wgt[(0.5 * avg <= psc) & (psc < avg)] = 0.5 wgt[(psc < 0.5 * avg)] = 0.25 return wgt def to_nc(self, data, name="Potential Source Contribution Function"): pscf = xr.DataArray( data, coords=[self.lonx[:-1], self.latx[:-1]], dims=["Longitude", "Latitude"], ) pscf.name = name pscf.attrs["station_name"] = self.station return pscf def calculate_pscf(self, thresh=0.5, multi=False, weighted=True): self.ozone_threshold = self.ozone.quantile(thresh) sub = self.ozone[self.ozone > self.ozone_threshold] slat = self.lat[sub.index] slon = self.lon[sub.index] self.dates_with_more_than_threshold_ozone = sub.index num1 = self.get_density( self.lon.values.flatten(), self.lat.values.flatten(), density=False ) num2 = self.get_density( slon.values.flatten(), slat.values.flatten(), density=False ) self.pscf = num2 / num1 self.rt_pscf = num2 if weighted: wgt = self.get_weight(self.rt_pscf) self.pscf *= wgt self.pscf = self.to_nc(self.pscf, name="PSCF") if not multi: return self.pscf else: return num2, num1, np.sum(num1) def calculate_cwt(self, weighted=True): cwt = np.zeros((self.lonx.shape[0] - 1, self.latx.shape[0] - 1)) tau = np.zeros_like(cwt) nums = np.zeros_like(cwt) for column in self.lat.columns: tlen = float(len(self.lat[column].values)) num = self.get_density( self.lon[column].values.flatten(), self.lat[column].values.flatten(), density=False, ) ttau = num / tlen cwt += ttau * self.ozone[column] tau += ttau nums += num self.cwt = cwt / tau self.rt_cwt = nums if weighted: wgt = self.get_weight(self.rt_cwt) self.cwt *= wgt self.cwt = self.to_nc(self.cwt, name="CWT") return self.cwt def calculate_rtwc(self, normalise=True): err = 100 bcwt = self.calculate_cwt(weighted=True) bcwt = np.nan_to_num(bcwt.values) while err >= 0.001: cwt = np.zeros((self.lonx.shape[0] - 1, self.latx.shape[0] - 1)) tau = np.zeros_like(cwt) nums = np.zeros_like(cwt) for column in self.lat.columns: ozo = self.ozone[column] num = self.get_density( self.lon[column].values.flatten(), self.lat[column].values.flatten(), density=False, ) tlen = float(len(self.lat[column].values)) ttau = num / tlen tavg = np.sum(bcwt * num / tlen) cwt += ttau * ozo * bcwt / tavg tau += ttau nums += num cwt = np.nan_to_num(cwt / tau) err = (cwt - bcwt) / bcwt err = np.nansum(err) bcwt = cwt if normalise: bcwt = normalize(bcwt) bcwt[bcwt == 0] = np.nan self.rtwc = self.to_nc(bcwt, name="RTWC") return self.rtwc def plot_map(self, pscf, boundinglat=-20, axes=[]): if len(axes) == 0: fig, ax = plt.subplots(1, 1, figsize=(6, 6)) cax = fig.add_axes([0.15, 0.06, 0.7, 0.04]) else: fig, ax, cax = axes m = Basemap( projection="spstere", lon_0=180, boundinglat=boundinglat, round=True, ax=ax ) data, lonx = addcyclic(pscf.T.values, pscf["Longitude"]) latx, lonx = np.meshgrid(pscf["Latitude"], lonx) lonx, latx = m(lonx, latx) if pscf.max().values <= 1: h = m.contourf(lonx, latx, data.T, levels=np.arange(0, 1.1, 0.1)) else: levels = np.arange(0, pscf.max(), 2) h = m.contourf(lonx, latx, data.T, levels=levels) plt.colorbar(h, cax=cax, orientation="horizontal") m.drawcoastlines(linewidth=1.5) m.drawcountries(linewidth=0.55) ax.set_title(self.station, fontweight="bold") return fig, ax, m
Methods
def calculate_cwt(self, weighted=True)
-
Expand source code
def calculate_cwt(self, weighted=True): cwt = np.zeros((self.lonx.shape[0] - 1, self.latx.shape[0] - 1)) tau = np.zeros_like(cwt) nums = np.zeros_like(cwt) for column in self.lat.columns: tlen = float(len(self.lat[column].values)) num = self.get_density( self.lon[column].values.flatten(), self.lat[column].values.flatten(), density=False, ) ttau = num / tlen cwt += ttau * self.ozone[column] tau += ttau nums += num self.cwt = cwt / tau self.rt_cwt = nums if weighted: wgt = self.get_weight(self.rt_cwt) self.cwt *= wgt self.cwt = self.to_nc(self.cwt, name="CWT") return self.cwt
def calculate_pscf(self, thresh=0.5, multi=False, weighted=True)
-
Expand source code
def calculate_pscf(self, thresh=0.5, multi=False, weighted=True): self.ozone_threshold = self.ozone.quantile(thresh) sub = self.ozone[self.ozone > self.ozone_threshold] slat = self.lat[sub.index] slon = self.lon[sub.index] self.dates_with_more_than_threshold_ozone = sub.index num1 = self.get_density( self.lon.values.flatten(), self.lat.values.flatten(), density=False ) num2 = self.get_density( slon.values.flatten(), slat.values.flatten(), density=False ) self.pscf = num2 / num1 self.rt_pscf = num2 if weighted: wgt = self.get_weight(self.rt_pscf) self.pscf *= wgt self.pscf = self.to_nc(self.pscf, name="PSCF") if not multi: return self.pscf else: return num2, num1, np.sum(num1)
def calculate_rtwc(self, normalise=True)
-
Expand source code
def calculate_rtwc(self, normalise=True): err = 100 bcwt = self.calculate_cwt(weighted=True) bcwt = np.nan_to_num(bcwt.values) while err >= 0.001: cwt = np.zeros((self.lonx.shape[0] - 1, self.latx.shape[0] - 1)) tau = np.zeros_like(cwt) nums = np.zeros_like(cwt) for column in self.lat.columns: ozo = self.ozone[column] num = self.get_density( self.lon[column].values.flatten(), self.lat[column].values.flatten(), density=False, ) tlen = float(len(self.lat[column].values)) ttau = num / tlen tavg = np.sum(bcwt * num / tlen) cwt += ttau * ozo * bcwt / tavg tau += ttau nums += num cwt = np.nan_to_num(cwt / tau) err = (cwt - bcwt) / bcwt err = np.nansum(err) bcwt = cwt if normalise: bcwt = normalize(bcwt) bcwt[bcwt == 0] = np.nan self.rtwc = self.to_nc(bcwt, name="RTWC") return self.rtwc
def get_density(self, lon, lat, density=True)
-
Expand source code
def get_density(self, lon, lat, density=True): density, lonx, latx = np.histogram2d( lon, lat, [self.lonx, self.latx], density=density ) return density
def get_weight(self, psc)
-
Expand source code
def get_weight(self, psc): wgt = np.ones_like(psc) avg = np.average(psc) wgt[psc >= 2 * avg] = 1.0 wgt[(avg <= psc) & (psc < 2 * avg)] = 0.75 wgt[(0.5 * avg <= psc) & (psc < avg)] = 0.5 wgt[(psc < 0.5 * avg)] = 0.25 return wgt
def plot_map(self, pscf, boundinglat=-20, axes=[])
-
Expand source code
def plot_map(self, pscf, boundinglat=-20, axes=[]): if len(axes) == 0: fig, ax = plt.subplots(1, 1, figsize=(6, 6)) cax = fig.add_axes([0.15, 0.06, 0.7, 0.04]) else: fig, ax, cax = axes m = Basemap( projection="spstere", lon_0=180, boundinglat=boundinglat, round=True, ax=ax ) data, lonx = addcyclic(pscf.T.values, pscf["Longitude"]) latx, lonx = np.meshgrid(pscf["Latitude"], lonx) lonx, latx = m(lonx, latx) if pscf.max().values <= 1: h = m.contourf(lonx, latx, data.T, levels=np.arange(0, 1.1, 0.1)) else: levels = np.arange(0, pscf.max(), 2) h = m.contourf(lonx, latx, data.T, levels=levels) plt.colorbar(h, cax=cax, orientation="horizontal") m.drawcoastlines(linewidth=1.5) m.drawcountries(linewidth=0.55) ax.set_title(self.station, fontweight="bold") return fig, ax, m
def to_nc(self, data, name='Potential Source Contribution Function')
-
Expand source code
def to_nc(self, data, name="Potential Source Contribution Function"): pscf = xr.DataArray( data, coords=[self.lonx[:-1], self.latx[:-1]], dims=["Longitude", "Latitude"], ) pscf.name = name pscf.attrs["station_name"] = self.station return pscf