Module ehfheatwaves.ncio
ncio.py contains the functions for reading from and writing to NetCDF4 files. Created on Sat Apr 14 12:56:42 2018
@author: Tammas Loughran
Expand source code
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
ncio.py contains the functions for reading from and writing to NetCDF4 files.
Created on Sat Apr 14 12:56:42 2018
@author: Tammas Loughran
"""
import datetime as dt
import sys
import netCDF4 as nc
import numpy as np
import pandas as pd
import ehfheatwaves.constants as const
from ehfheatwaves.__init__ import __version__
LAT_NAMES = ('lat','lats','latitude','latitudes')
LON_NAMES = ('lon','lons','longitude','longitudes')
class DatesOrderError(Exception):
"""Exception to be raised when the calendar start day occurs before the end day."""
def __init__(self, start, end):
self.start = start
self.end = end
print("Calendar end date appears before the start date")
print("start: ", self.start)
print("end: ", self.end)
class Calendar360():
"""A basic calendar class with 360 days per year."""
def __init__(self, sdate, edate):
"""## Arguments:
- sdate : Start date of calendar.
- edate : End date of calendar.
"""
if sdate>edate: raise DatesOrderError(sdate, edate)
self.year = np.repeat(range(sdate.year, edate.year+1), 360, 0)
nyears = len(range(sdate.year, edate.year+1))
self.month = np.tile(np.repeat(range(1, 12+1), 30, 0), nyears)
self.day = np.tile(np.tile(range(1, 30+1), 12), nyears)
if (sdate.day!=1)|(edate.month!=1):
sdoyi = (sdate.month - 1)*30 + sdate.day - 1
self.year = self.year[sdoyi:]
self.month = self.month[sdoyi:]
self.day = self.day[sdoyi:]
if (edate.day!=30)|(edate.month!=12):
edoyi = (12 - edate.month)*30 + (30 - edate.day)
self.year = self.year[:-edoyi]
self.month = self.month[:-edoyi]
self.day = self.day[:-edoyi]
def __getitem__(self, i):
"""Return a datetime object for the provided index."""
return dt.datetime(self.year[i], self.month[i], self.day[i])
class TimeData(object):
"""Data class for time data from an netcdf file."""
def __init__(self, filename:str, timevname:str):
"""## Arguments:
- filename : Input file name.
- timevarname : Name of the time variable in the input file.
"""
if any([(wildcard in filename) for wildcard in ['*','?','[']]):
tempnc = nc.MFDataset(filename, 'r')
nctime = tempnc.variables[timevname]
nctime = nc.MFTime(nctime)
else:
tempnc = nc.Dataset(filename, 'r')
nctime = tempnc.variables[timevname]
try:
self.calendar = nctime.calendar
except:
self.calendar = 'proleptic_gregorian'
if not self.calendar:
print('Unrecognized calendar. Using gregorian.')
self.calendar = 'gregorian'
if self.calendar=='360_day':
self.daysinyear = 360
# 360 day season start and end indices
self.SHS = (300,450)
self.SHW = (120,270)
self.dayone = nc.num2date(nctime[0], nctime.units, calendar=self.calendar)
self.daylast = nc.num2date(nctime[-1], nctime.units, calendar=self.calendar)
self.dates = Calendar360(self.dayone, self.daylast)
else:
self.daysinyear = 365
# 365 day season start and end indices
self.SHS = (304,455)
self.SHW = (120,273)
if tempnc.variables[timevname].units=='day as %Y%m%d.%f':
st = str(int(nctime[0]))
nd = str(int(nctime[-1]))
self.dayone = dt.datetime(int(st[:4]), int(st[4:6]), int(st[6:]))
self.daylast = dt.datetime(int(nd[:4]), int(nd[4:6]), int(nd[6:]))
else:
self.dayone = nc.num2date(nctime[0], nctime.units, calendar=self.calendar)
self.daylast = nc.num2date(nctime[-1], nctime.units, calendar=self.calendar)
self.dates = pd.period_range(str(self.dayone), str(self.daylast))
# Remove leap days. Maybe this should be a separate function?
self.noleapdates = self.dates[(self.dates.month!=2)|(self.dates.day!=29)]
def get_mask(options)->np.ndarray:
"""get_mask loads the land-sea mask data from a NetCDF4 file."""
masknc = nc.Dataset(options.maskfile, 'r')
mask = masknc.variables[options.maskvname][:]
if mask.max()>1: mask = mask>50
else: mask = mask>0.5
mask = mask.astype(bool)
mask = np.squeeze(mask)
masknc.close()
if options.invertmask: mask = np.logical_not(mask)
if options.flipmask: mask = np.flipud(mask)
return mask
def load_bp_data(options, timedata:TimeData, variable:str='tmax', mask:np.ndarray=None)->np.ndarray:
"""Load the tmax or tmin data for the base period.
## Arguments:
- options
- timedata : `TimeData` object.
- variable : The variable to load.
- mask : The land-sea mask.
## Returns:
- temp : Temperature data array.
"""
# Determine if we need tmax or tmin and whether the data are in multiple files.
if variable=='tmax':
if options.bpfx:files = options.bpfx
else: files = options.tmaxfile
varname = options.tmaxvname
elif variable=='tmin':
if options.bpfn: files = options.bpfn
else: files = options.tminfile
varname = options.tminvname
# Load ncfile depending on multi or single-file
if any([(wildcard in files) for wildcard in ['*','?','[']]):
tempnc = nc.MFDataset(files, 'r')
bptime = tempnc.variables[options.timevname]
bptime = MFTime(bptime)
else:
tempnc = nc.Dataset(files,'r')
bptime = tempnc.variables[options.timevname]
# Define the base period
if tempnc.variables[options.timevname].units=='day as %Y%m%d.%f':
st = str(int(bptime[0]))
nd = str(int(bptime[-1]))
bpdayone = dt.datetime(int(st[:4]), int(st[4:6]), int(st[6:]))
bpdaylast = dt.datetime(int(nd[:4]), int(nd[4:6]), int(nd[6:]))
else:
bpdayone = nc.num2date(bptime[0], bptime.units, calendar=timedata.calendar)
bpdaylast = nc.num2date(bptime[-1], bptime.units, calendar=timedata.calendar)
if timedata.calendar=='360_day': bpdates = Calendar360(bpdayone, bpdaylast)
else:
bpdates = pd.period_range(str(bpdayone), str(bpdaylast))
if timedata.calendar=='365_day': bpdates = bpdates[(bpdates.month!=2)|(bpdates.day!=29)]
dates_base = bpdates[(options.bpstart<=bpdates.year)&(bpdates.year<=options.bpend)]
try:
temp = tempnc.variables[varname][(options.bpstart<=bpdates.year)&(bpdates.year<=options.bpend)]
except IndexError as FirstException:
raise IndexError(
"Could not index netCDF file time dimension. This could be a calendar problem. "
"Please make sure your input file is daily frequency data and/or that it contains "
f"the base period. You wanted {options.bpstart}-{options.bpend} and your data have "
f"{bpdates.year[0]}-{bpdates.year[-1]}"
) from FirstException
if len(temp.shape)==4: temp = temp.squeeze()
if options.maskfile:
temp = temp[:,mask]
if tempnc.variables[varname].units=='K': temp -= 273.15
# Remove leap days in gregorian calendars
if (timedata.calendar=='gregorian')|(timedata.calendar=='proleptic_gregorian')|(timedata.calendar=='standard'):
temp = temp[(dates_base.month!=2)|(dates_base.day!=29),...]
tempnc.close()
return temp
def remove_leap_days(data:np.ndarray, dates:pd.DatetimeIndex)->np.ndarray:
"""Remove February 29th from a dataset."""
return data[(dates.month!=2)|(dates.day!=29),...]
def get_all_data(files:str, vname:str, options)->tuple:
"""Load all temperature data from a netcdf file.
## Arguments:
- files : filename, may contain wildcards.
- vname : variable name.
- options
## Returns:
- temp : data in (time, x, y) coordinates.
- lats : latitudes
"""
if any([(wildcard in files) for wildcard in ['*','?','[']]):
tempnc = nc.MFDataset(files, 'r')
else:
tempnc = nc.Dataset(files, 'r')
temp = tempnc.variables[vname][:]
if len(temp.shape)==4: temp = temp.squeeze()
# Test for increasing latitude and flip if decreasing
latkey = [vrbl in LAT_NAMES for vrbl in tempnc.variables.keys()].index(True)
latvname = list(tempnc.variables.keys())[latkey]
lats = tempnc.variables[latvname][:]
if (lats[-1]-lats[0])<0: lats = np.flipud(lats)
if tempnc.variables[vname].units=='K': temp -= 273.15
tempnc.close()
return temp, lats
def save_yearly(
HWA,
HWM,
HWN,
HWF,
HWD,
HWT,
tpct,
definition:str,
timedata,
options,
mask:np.ndarray,
)->None:
"""Save yearly data to netcdf file.
Input aspect arrays are 2D, timeXspace and are either reshaped or indexed with a land-sea mask.
## Arguments:
- HWA :
- HWM :
- HWN :
- HWF :
- HWD :
- HWT :
- txpct : Percentile thresholds.
- definition : Name of heatwave definition.
- timedata : TimeData onbject.
- options :
- mask : Land-sea mask.
"""
if any([(wildcard in options.tmaxfile) for wildcard in ['*','?','[']]):
tempnc = nc.MFDataset(options.tmaxfile, 'r')
else:
tempnc = nc.Dataset(options.tmaxfile, 'r')
nyears = HWA.shape[0]
try: experiment = tempnc.__getattribute__('experiment')
except AttributeError: experiment = ''
try: model = tempnc.__getattribute__('model_id')
except AttributeError: model = ''
try: parent = tempnc.__getattribute__('parent_experiment_rip')
except AttributeError: parent = ''
try: realization = tempnc.__getattribute__('realization')
except AttributeError: realization = ''
try: initialization = tempnc.__getattribute__('initialization_method')
except AttributeError: initialization = ''
try:
physics = tempnc.__getattribute__('physics_version')
rip = 'r'+str(realization)+'i'+str(initialization)+'p'+str(physics)
except AttributeError:
physics = ''
rip = ''
latkey = [vrbl in LAT_NAMES for vrbl in tempnc.variables.keys()].index(True)
latvname = list(tempnc.variables.keys())[latkey]
lonkey = [vrbl in LON_NAMES for vrbl in tempnc.variables.keys()].index(True)
lonvname = list(tempnc.variables.keys())[lonkey]
space = (tempnc.dimensions[latvname].__len__(), tempnc.dimensions[lonvname].__len__())
yearlyout = nc.Dataset('%s_heatwaves_%s_%s_%s_yearly_%s.nc'%(definition, model, experiment, rip, options.season), 'w')
yearlyout.createDimension('time', size=None)
yearlyout.createDimension('lon', tempnc.dimensions[lonvname].__len__())
yearlyout.createDimension('lat', tempnc.dimensions[latvname].__len__())
yearlyout.createDimension('day', timedata.daysinyear)
setattr(yearlyout, "author", "Tammas Loughran")
setattr(yearlyout, "contact", "t.loughran@unsw.edu.au")
setattr(yearlyout, "source", "https://github.com/tammasloughran/ehfheatwaves")
setattr(yearlyout, "date", dt.datetime.today().strftime('%Y-%m-%d'))
setattr(yearlyout, "script", sys.argv[0])
if model:
setattr(yearlyout, "model_id", model)
setattr(yearlyout, "experiment", experiment)
setattr(yearlyout, "parent_experiment_rip", parent)
setattr(yearlyout, "realization", realization)
setattr(yearlyout, "initialization_method", initialization)
setattr(yearlyout, "physics_version", physics)
setattr(yearlyout, "period", "%s-%s"%(str(timedata.dayone.year),str(timedata.daylast.year)))
setattr(yearlyout, "base_period", "%s-%s"%(str(options.bpstart),str(options.bpend)))
setattr(yearlyout, "percentile", "%sth"%(str(options.pcntl)))
setattr(yearlyout, "definition", definition)
setattr(yearlyout, "frequency", "yearly")
setattr(yearlyout, "season", options.season)
setattr(yearlyout, "season_note", ("The year of a season is the year it starts in. SH summer: Nov-Mar. NH summer: May-Sep."))
setattr(yearlyout, "version", __version__)
setattr(yearlyout, "tmax_file", options.tmaxfile)
setattr(yearlyout, "tmin_file", options.tminfile)
if options.maskfile:
setattr(yearlyout, "mask_file", options.maskfile)
setattr(yearlyout, "quantile_method", options.qtilemethod)
setattr(yearlyout, 'Conventions', 'CF-1.7')
otime = yearlyout.createVariable('time', 'f8', 'time')
setattr(otime, 'units', 'years since 0001-06-01 00:00:00')
setattr(otime, 'standard_name', 'time')
setattr(otime, 'calendar', 'proleptic_gregorian')
olat = yearlyout.createVariable('lat', 'f8', 'lat')
setattr(olat, 'standard_name', 'latitude')
setattr(olat, 'long_name', 'Latitude')
setattr(olat, 'units', 'degrees_north')
setattr(olat, 'axis', 'Y')
olon = yearlyout.createVariable('lon', 'f8', 'lon')
setattr(olon, 'standard_name', 'longitude')
setattr(olon, 'long_name', 'Longitude')
setattr(olon, 'units', 'degrees_east')
setattr(olon, 'axis', 'X')
otpct = yearlyout.createVariable('t%spct'%(options.pcntl), 'f8', ('day','lat','lon'), fill_value=const.FILL_VAL)
setattr(otpct, 'long_name', '90th percentile')
setattr(otpct, 'units', 'degC')
setattr(otpct, 'description', '90th percentile of %s-%s'%(str(options.bpstart),str(options.bpend)))
setattr(otpct, 'missing_value', const.MISSING_VAL)
setattr(otpct, 'valid_range', (-20,100))
HWAout = yearlyout.createVariable('HWA_%s'%(definition), 'f8', ('time','lat','lon'), fill_value=const.FILL_VAL)
setattr(HWAout, 'long_name', 'Heatwave Amplitude')
if definition=='tx90pct':
setattr(HWAout, 'units', 'degC')
elif definition=='tn90pct':
setattr(HWAout, 'units', 'degC')
elif definition=='EHF':
setattr(HWAout, 'units', 'degC2')
setattr(HWAout, 'description', 'Peak of the hottest heatwave per year')
setattr(HWAout, 'missing_value', const.MISSING_VAL)
setattr(HWAout, 'valid_range', (-30, 400))
HWMout = yearlyout.createVariable('HWM_%s'%(definition), 'f8', ('time','lat','lon'), fill_value=const.FILL_VAL)
setattr(HWMout, 'long_name', 'Heatwave Magnitude')
if definition=='tx90pct':
setattr(HWMout, 'units', 'degC')
elif definition=='tn90pct':
setattr(HWMout, 'units', 'degC')
elif definition=='EHF':
setattr(HWMout, 'units', 'degC2')
setattr(HWMout, 'description', 'Average magnitude of the yearly heatwave')
setattr(HWMout, 'missing_value', const.MISSING_VAL)
setattr(HWMout, 'valid_range' ,(-30, 400))
HWNout = yearlyout.createVariable('HWN_%s'%(definition), 'f8', ('time', 'lat', 'lon'), fill_value=const.FILL_VAL)
setattr(HWNout, 'long_name', 'Heatwave Number')
setattr(HWNout, 'units','count')
setattr(HWNout, 'description', 'Number of heatwaves per year')
setattr(HWNout, 'missing_value', const.MISSING_VAL)
setattr(HWNout, 'valid_range', (0, 40))
HWFout = yearlyout.createVariable('HWF_%s'%(definition), 'f8', ('time','lat','lon'), fill_value=const.FILL_VAL)
setattr(HWFout, 'long_name','Heatwave Frequency')
setattr(HWFout, 'units', 'days')
setattr(HWFout, 'description', 'Proportion of heatwave days per season')
setattr(HWFout, 'missing_value', const.MISSING_VAL)
setattr(HWFout, 'valid_range', (0, 165))
HWDout = yearlyout.createVariable('HWD_%s'%(definition), 'f8', ('time','lat','lon'), fill_value=const.FILL_VAL)
setattr(HWDout, 'long_name', 'Heatwave Duration')
setattr(HWDout, 'units', 'days')
setattr(HWDout, 'description', 'Duration of the longest heatwave per year')
setattr(HWDout, 'missing_value', const.MISSING_VAL)
setattr(HWDout, 'valid_range', (0,165))
HWTout = yearlyout.createVariable('HWT_%s'%(definition), 'f8', ('time','lat','lon'), fill_value=const.FILL_VAL)
setattr(HWTout, 'long_name', 'Heatwave Timing')
if options.season=='summer':
setattr(HWTout, 'units', 'days since 0001-11-01 00:00:00')
elif options.season=='winter':
setattr(HWTout, 'units', 'days since 0001-05-01 00:00:00')
setattr(HWTout, 'description', 'First heat wave day of the season')
setattr(HWTout, 'missing_value', const.MISSING_VAL)
setattr(HWTout, 'valid_range', (1,366))
otime[:] = range(timedata.dayone.year, timedata.daylast.year)
olat[:] = tempnc.variables[latvname][:]
olon[:] = tempnc.variables[lonvname][:]
if options.maskfile:
dummy_array = np.ones((timedata.daysinyear,)+(len(olat),)+(len(olon),))*const.FILL_VAL
dummy_array[:,mask] = tpct
otpct[:] = dummy_array.copy()
dummy_array = np.ones((nyears,)+(len(olat),)+(len(olon),))*const.FILL_VAL
dummy_array[:,mask] = HWA
HWAout[:] = dummy_array.copy()
dummy_array = np.ones((nyears,)+(len(olat),)+(len(olon),))*const.FILL_VAL
dummy_array[:,mask] = HWM
HWMout[:] = dummy_array.copy()
dummy_array = np.ones((nyears,)+(len(olat),)+(len(olon),))*const.FILL_VAL
dummy_array[:,mask] = HWN
HWNout[:] = dummy_array.copy()
dummy_array = np.ones((nyears,)+(len(olat),)+(len(olon),))*const.FILL_VAL
dummy_array[:,mask] = HWF
HWFout[:] = dummy_array.copy()
dummy_array = np.ones((nyears,)+(len(olat),)+(len(olon),))*const.FILL_VAL
dummy_array[:,mask] = HWD
HWDout[:] = dummy_array.copy()
dummy_array = np.ones((nyears,)+(len(olat),)+(len(olon),))*const.FILL_VAL
dummy_array[:,mask] = HWT
HWTout[:] = dummy_array.copy()
else:
otpct[:] = tpct
HWAout[:] = HWA.reshape((nyears,)+space)
HWMout[:] = HWM.reshape((nyears,)+space)
HWNout[:] = HWN.reshape((nyears,)+space)
HWFout[:] = HWF.reshape((nyears,)+space)
HWDout[:] = HWD.reshape((nyears,)+space)
HWTout[:] = HWT.reshape((nyears,)+space)
tempnc.close()
yearlyout.close()
def save_daily(
exceed:np.ndarray,
event:np.ndarray,
ends:np.ndarray,
options,
timedata:TimeData,
original_shape:tuple,
mask:np.ndarray,
defn:str='EHF',
)->None:
"""save_daily saves the daily data to netcdf file. Input arrays are 2D, (time, space) and are
either reshaped or indexed with a land-sea mask.
## Arguments:
- exceed : Threshold exceedence values.
- event : Boolean indicator for whether a heatwave is occuring.
- ends : Duration of heatwave.
- options :
- timedata : TimeData object.
- original_shape : The original shape of the input data.
- mask : Land-sea mask.
- defn : The name of the heatwave definition being saved.
"""
if options.tmaxfile:
filename = options.tmaxfile
elif options.tminfile:
filename = options.tminfile
if any([(wildcard in filename) for wildcard in ['*','?','[']]):
tempnc = nc.MFDataset(filename, 'r')
else:
tempnc = nc.Dataset(filename, 'r')
try: experiment = tempnc.__getattribute__('experiment')
except AttributeError: experiment = ''
try: model = tempnc.__getattribute__('model_id')
except AttributeError: model = ''
try: parent = tempnc.__getattribute__('parent_experiment_rip')
except AttributeError: parent = ''
try: realization = tempnc.__getattribute__('realization')
except AttributeError: realization = ''
try: initialization = tempnc.__getattribute__('initialization_method')
except AttributeError: initialization = ''
try:
physics = tempnc.__getattribute__('physics_version')
rip = 'r'+str(realization)+'i'+str(initialization)+'p'+str(physics)
except AttributeError:
physics = ''
rip = ''
latkey = [vrbl in LAT_NAMES for vrbl in tempnc.variables.keys()].index(True)
latvname = list(tempnc.variables.keys())[latkey]
lonkey = [vrbl in LON_NAMES for vrbl in tempnc.variables.keys()].index(True)
lonvname = list(tempnc.variables.keys())[lonkey]
dailyout = nc.Dataset('%s_heatwaves_%s_%s_%s_daily.nc'%(defn, model, experiment, rip), mode='w')
dailyout.createDimension('time', size=None)
dailyout.createDimension('lon', tempnc.dimensions[lonvname].__len__())
dailyout.createDimension('lat', tempnc.dimensions[latvname].__len__())
setattr(dailyout, "author", "Tammas Loughran")
setattr(dailyout, "contact", "t.loughran@unsw.edu.au")
setattr(dailyout, "source", "https://github.com/tammasloughran/ehfheatwaves")
setattr(dailyout, "date", dt.datetime.today().strftime('%Y-%m-%d'))
setattr(dailyout, "script", sys.argv[0])
setattr(dailyout, "period", "%s-%s"%(str(timedata.dayone.year), str(timedata.daylast.year)))
setattr(dailyout, "base_period", "%s-%s"%(str(options.bpstart), str(options.bpend)))
setattr(dailyout, "percentile", "%sth"%(str(options.pcntl)))
if model:
setattr(dailyout, "model_id", model)
setattr(dailyout, "experiment", experiment)
setattr(dailyout, "parent_experiment_rip", parent)
setattr(dailyout, "realization", realization)
setattr(dailyout, "initialization_method", initialization)
setattr(dailyout, "physics_version", physics)
setattr(dailyout, "version", __version__)
if options.tmaxfile:
setattr(dailyout, "tmax_file", options.tmaxfile)
if options.tminfile:
setattr(dailyout, "tmin_file", options.tminfile)
if options.maskfile:
setattr(dailyout, "mask_file", str(options.maskfile))
setattr(dailyout, "quantile_method", options.qtilemethod)
setattr(dailyout, 'Conventions', 'CF-1.7')
otime = dailyout.createVariable('time', 'f8', 'time')
setattr(otime, 'units', 'days since %s-01-01'%(timedata.dayone.year))
setattr(otime, 'calendar', '365_day')
setattr(otime, 'standard_name', 'time')
olat = dailyout.createVariable('lat', 'f8', 'lat')
setattr(olat, 'standard_name', 'latitude')
setattr(olat, 'long_name', 'Latitude')
setattr(olat, 'units', 'degrees_north')
olon = dailyout.createVariable('lon', 'f8', 'lon')
setattr(olon, 'standard_name', 'longitude')
setattr(olon, 'long_name', 'Longitude')
setattr(olon, 'units', 'degrees_east')
oehf = dailyout.createVariable(defn, 'f8', ('time','lat','lon'), fill_value=const.FILL_VAL)
if defn=='EHF':
setattr(oehf, 'long_name', 'Excess Heat Factor')
setattr(oehf, 'units', 'degC2')
elif defn=='tx90pct':
setattr(oehf, 'long_name', 'Temperature Exceeding tx90pct')
setattr(oehf, 'units', 'C')
elif defn=='tn90pct':
setattr(oehf, 'long_name', 'Temperature Exceeding tn90pct')
setattr(oehf, 'units', 'C')
setattr(oehf, 'missing_value', const.MISSING_VAL)
oevent = dailyout.createVariable('event', 'f8', ('time','lat','lon'), fill_value=const.FILL_VAL)
setattr(oevent, 'long_name', 'Event indicator')
setattr(oevent, 'description', 'Indicates whether a heatwave is happening on that day')
setattr(oevent, 'missing_value', const.MISSING_VAL)
oends = dailyout.createVariable('ends', 'f8', ('time','lat','lon'), fill_value=const.FILL_VAL)
setattr(oends, 'long_name', 'Duration at start of heatwave')
setattr(oends, 'units', 'days')
setattr(oends, 'missing_value', const.MISSING_VAL)
otime[:] = range(0,original_shape[0],1)
olat[:] = tempnc.variables[latvname][:]
olon[:] = tempnc.variables[lonvname][:]
exceed[exceed.mask==True] = const.MISSING_VAL
if options.maskfile:
dummy_array = np.ones(original_shape)*const.FILL_VAL
dummy_array[:,mask] = exceed
oehf[:] = dummy_array.copy()
dummy_array = np.ones(original_shape)*const.FILL_VAL
dummy_array[:,mask] = event
oevent[:] = dummy_array.copy()
dummy_array = np.ones(original_shape)*const.FILL_VAL
dummy_array[:,mask] = ends
oends[:] = dummy_array.copy()
else:
oehf[:] = exceed
oevent[:] = event
oends[:] = ends
tempnc.close()
dailyout.close()
def save_ehi(
EHIsig:np.ndarray,
EHIaccl:np.ndarray,
options,
timedata:TimeData,
original_shape:tuple,
mask:np.ndarray,
)->None:
"""Save the daily data to netcdf file. Input arrays are 2D, (time, space) and are either
reshaped or indexed with a land-sea mask.
## Arguments:
- EHIsig : EHI significance index.
- EHIaccl : EHI acclimatisation index.
- options :
- timedata : TimeData object.
- original_shape : The original shape of the input data.
- mask : Land-sea mask.
"""
if options.tmaxfile:
filename = options.tmaxfile
elif options.tminfile:
filename = options.tminfile
if any([(wildcard in filename) for wildcard in ['*','?','[']]):
tempnc = nc.MFDataset(filename, 'r')
else:
tempnc = nc.Dataset(filename, 'r')
try: experiment = tempnc.__getattribute__('experiment')
except AttributeError: experiment = ''
try: model = tempnc.__getattribute__('model_id')
except AttributeError: model = ''
try: parent = tempnc.__getattribute__('parent_experiment_rip')
except AttributeError: parent = ''
try: realization = tempnc.__getattribute__('realization')
except AttributeError: realization = ''
try: initialization = tempnc.__getattribute__('initialization_method')
except AttributeError: initialization = ''
try:
physics = tempnc.__getattribute__('physics_version')
rip = 'r'+str(realization)+'i'+str(initialization)+'p'+str(physics)
except AttributeError:
physics = ''
rip = ''
latkey = [vrbl in LAT_NAMES for vrbl in tempnc.variables.keys()].index(True)
latvname = list(tempnc.variables.keys())[latkey]
lonkey = [vrbl in LON_NAMES for vrbl in tempnc.variables.keys()].index(True)
lonvname = list(tempnc.variables.keys())[lonkey]
defn = 'EHI'
dailyout = nc.Dataset('%s_heatwaves_%s_%s_%s_daily.nc'%(defn, model, experiment, rip), mode='w')
dailyout.createDimension('time', size=None)
dailyout.createDimension('lon', tempnc.dimensions[lonvname].__len__())
dailyout.createDimension('lat', tempnc.dimensions[latvname].__len__())
setattr(dailyout, "author", "Tammas Loughran")
setattr(dailyout, "contact", "t.loughran@unsw.edu.au")
setattr(dailyout, "source", "https://github.com/tammasloughran/ehfheatwaves")
setattr(dailyout, "date", dt.datetime.today().strftime('%Y-%m-%d'))
setattr(dailyout, "script", sys.argv[0])
setattr(dailyout, "period", "%s-%s"%(str(timedata.dayone.year), str(timedata.daylast.year)))
setattr(dailyout, "base_period", "%s-%s"%(str(options.bpstart), str(options.bpend)))
setattr(dailyout, "percentile", "%sth"%(str(options.pcntl)))
if model:
setattr(dailyout, "model_id", model)
setattr(dailyout, "experiment", experiment)
setattr(dailyout, "parent_experiment_rip", parent)
setattr(dailyout, "realization", realization)
setattr(dailyout, "initialization_method", initialization)
setattr(dailyout, "physics_version", physics)
setattr(dailyout, "version", __version__)
if options.tmaxfile:
setattr(dailyout, "tmax_file", options.tmaxfile)
if options.tminfile:
setattr(dailyout, "tmin_file", options.tminfile)
if options.maskfile:
setattr(dailyout, "mask_file", str(options.maskfile))
setattr(dailyout, "quantile_method", options.qtilemethod)
setattr(dailyout, 'Conventions', 'CF-1.7')
otime = dailyout.createVariable('time', 'f8', 'time')
setattr(otime, 'standard_name', 'time')
setattr(otime, 'units', 'days since %s-01-01'%(timedata.dayone.year))
setattr(otime, 'calendar', '365_day')
olat = dailyout.createVariable('lat', 'f8', 'lat')
setattr(olat, 'standard_name', 'latitude')
setattr(olat, 'long_name', 'Latitude')
setattr(olat, 'units', 'degrees_north')
olon = dailyout.createVariable('lon', 'f8', 'lon')
setattr(olon, 'standard_name', 'longitude')
setattr(olon, 'long_name', 'Longitude')
setattr(olon, 'units', 'degrees_east')
oehisig = dailyout.createVariable(
'EHIsig',
'f8',
('time','lat','lon'),
fill_value=const.FILL_VAL,
)
setattr(oehisig, 'long_name', 'Excess Heat Index Significance')
setattr(oehisig, 'units', 'C')
setattr(oehisig, 'missing_value', const.MISSING_VAL)
setattr(oehisig, 'valid_range', (0,100))
oehiaccl = dailyout.createVariable(
'EHIaccl',
'f8',
('time','lat','lon'),
fill_value=const.FILL_VAL,
)
setattr(oehiaccl, 'long_name', 'Excess Heat Index Acclimatisation')
setattr(oehiaccl, 'units', 'C')
setattr(oehiaccl, 'missing_value', const.MISSING_VAL)
setattr(oehiaccl, 'valid_range', (0,100))
otime[:] = range(0, original_shape[0], 1)
olat[:] = tempnc.variables[latvname][:]
olon[:] = tempnc.variables[lonvname][:]
if options.maskfile:
dummy_array = np.ones(original_shape)*const.FILL_VAL
dummy_array[:,mask] = EHIsig
oehisig[:] = dummy_array.copy()
dummy_array = np.ones(original_shape)*const.FILL_VAL
dummy_array[:,mask] = EHIaccl
oehiaccl[:] = dummy_array.copy()
else:
oehisig[:] = EHIsig
oehiaccl[:] = EHIaccl
tempnc.close()
dailyout.close()
Functions
def get_all_data(files: str, vname: str, options) ‑> tuple
-
Load all temperature data from a netcdf file.
Arguments:
- files : filename, may contain wildcards.
- vname : variable name.
- options
Returns:
- temp : data in (time, x, y) coordinates.
- lats : latitudes
Expand source code
def get_all_data(files:str, vname:str, options)->tuple: """Load all temperature data from a netcdf file. ## Arguments: - files : filename, may contain wildcards. - vname : variable name. - options ## Returns: - temp : data in (time, x, y) coordinates. - lats : latitudes """ if any([(wildcard in files) for wildcard in ['*','?','[']]): tempnc = nc.MFDataset(files, 'r') else: tempnc = nc.Dataset(files, 'r') temp = tempnc.variables[vname][:] if len(temp.shape)==4: temp = temp.squeeze() # Test for increasing latitude and flip if decreasing latkey = [vrbl in LAT_NAMES for vrbl in tempnc.variables.keys()].index(True) latvname = list(tempnc.variables.keys())[latkey] lats = tempnc.variables[latvname][:] if (lats[-1]-lats[0])<0: lats = np.flipud(lats) if tempnc.variables[vname].units=='K': temp -= 273.15 tempnc.close() return temp, lats
def get_mask(options) ‑> numpy.ndarray
-
get_mask loads the land-sea mask data from a NetCDF4 file.
Expand source code
def get_mask(options)->np.ndarray: """get_mask loads the land-sea mask data from a NetCDF4 file.""" masknc = nc.Dataset(options.maskfile, 'r') mask = masknc.variables[options.maskvname][:] if mask.max()>1: mask = mask>50 else: mask = mask>0.5 mask = mask.astype(bool) mask = np.squeeze(mask) masknc.close() if options.invertmask: mask = np.logical_not(mask) if options.flipmask: mask = np.flipud(mask) return mask
def load_bp_data(options, timedata: TimeData, variable: str = 'tmax', mask: numpy.ndarray = None) ‑> numpy.ndarray
-
Load the tmax or tmin data for the base period.
Arguments:
- options
- timedata :
TimeData
object. - variable : The variable to load.
- mask : The land-sea mask.
Returns:
- temp : Temperature data array.
Expand source code
def load_bp_data(options, timedata:TimeData, variable:str='tmax', mask:np.ndarray=None)->np.ndarray: """Load the tmax or tmin data for the base period. ## Arguments: - options - timedata : `TimeData` object. - variable : The variable to load. - mask : The land-sea mask. ## Returns: - temp : Temperature data array. """ # Determine if we need tmax or tmin and whether the data are in multiple files. if variable=='tmax': if options.bpfx:files = options.bpfx else: files = options.tmaxfile varname = options.tmaxvname elif variable=='tmin': if options.bpfn: files = options.bpfn else: files = options.tminfile varname = options.tminvname # Load ncfile depending on multi or single-file if any([(wildcard in files) for wildcard in ['*','?','[']]): tempnc = nc.MFDataset(files, 'r') bptime = tempnc.variables[options.timevname] bptime = MFTime(bptime) else: tempnc = nc.Dataset(files,'r') bptime = tempnc.variables[options.timevname] # Define the base period if tempnc.variables[options.timevname].units=='day as %Y%m%d.%f': st = str(int(bptime[0])) nd = str(int(bptime[-1])) bpdayone = dt.datetime(int(st[:4]), int(st[4:6]), int(st[6:])) bpdaylast = dt.datetime(int(nd[:4]), int(nd[4:6]), int(nd[6:])) else: bpdayone = nc.num2date(bptime[0], bptime.units, calendar=timedata.calendar) bpdaylast = nc.num2date(bptime[-1], bptime.units, calendar=timedata.calendar) if timedata.calendar=='360_day': bpdates = Calendar360(bpdayone, bpdaylast) else: bpdates = pd.period_range(str(bpdayone), str(bpdaylast)) if timedata.calendar=='365_day': bpdates = bpdates[(bpdates.month!=2)|(bpdates.day!=29)] dates_base = bpdates[(options.bpstart<=bpdates.year)&(bpdates.year<=options.bpend)] try: temp = tempnc.variables[varname][(options.bpstart<=bpdates.year)&(bpdates.year<=options.bpend)] except IndexError as FirstException: raise IndexError( "Could not index netCDF file time dimension. This could be a calendar problem. " "Please make sure your input file is daily frequency data and/or that it contains " f"the base period. You wanted {options.bpstart}-{options.bpend} and your data have " f"{bpdates.year[0]}-{bpdates.year[-1]}" ) from FirstException if len(temp.shape)==4: temp = temp.squeeze() if options.maskfile: temp = temp[:,mask] if tempnc.variables[varname].units=='K': temp -= 273.15 # Remove leap days in gregorian calendars if (timedata.calendar=='gregorian')|(timedata.calendar=='proleptic_gregorian')|(timedata.calendar=='standard'): temp = temp[(dates_base.month!=2)|(dates_base.day!=29),...] tempnc.close() return temp
def remove_leap_days(data: numpy.ndarray, dates: pandas.core.indexes.datetimes.DatetimeIndex) ‑> numpy.ndarray
-
Remove February 29th from a dataset.
Expand source code
def remove_leap_days(data:np.ndarray, dates:pd.DatetimeIndex)->np.ndarray: """Remove February 29th from a dataset.""" return data[(dates.month!=2)|(dates.day!=29),...]
def save_daily(exceed: numpy.ndarray, event: numpy.ndarray, ends: numpy.ndarray, options, timedata: TimeData, original_shape: tuple, mask: numpy.ndarray, defn: str = 'EHF') ‑> None
-
save_daily saves the daily data to netcdf file. Input arrays are 2D, (time, space) and are either reshaped or indexed with a land-sea mask.
Arguments:
- exceed : Threshold exceedence values.
- event : Boolean indicator for whether a heatwave is occuring.
- ends : Duration of heatwave.
- options :
- timedata : TimeData object.
- original_shape : The original shape of the input data.
- mask : Land-sea mask.
- defn : The name of the heatwave definition being saved.
Expand source code
def save_daily( exceed:np.ndarray, event:np.ndarray, ends:np.ndarray, options, timedata:TimeData, original_shape:tuple, mask:np.ndarray, defn:str='EHF', )->None: """save_daily saves the daily data to netcdf file. Input arrays are 2D, (time, space) and are either reshaped or indexed with a land-sea mask. ## Arguments: - exceed : Threshold exceedence values. - event : Boolean indicator for whether a heatwave is occuring. - ends : Duration of heatwave. - options : - timedata : TimeData object. - original_shape : The original shape of the input data. - mask : Land-sea mask. - defn : The name of the heatwave definition being saved. """ if options.tmaxfile: filename = options.tmaxfile elif options.tminfile: filename = options.tminfile if any([(wildcard in filename) for wildcard in ['*','?','[']]): tempnc = nc.MFDataset(filename, 'r') else: tempnc = nc.Dataset(filename, 'r') try: experiment = tempnc.__getattribute__('experiment') except AttributeError: experiment = '' try: model = tempnc.__getattribute__('model_id') except AttributeError: model = '' try: parent = tempnc.__getattribute__('parent_experiment_rip') except AttributeError: parent = '' try: realization = tempnc.__getattribute__('realization') except AttributeError: realization = '' try: initialization = tempnc.__getattribute__('initialization_method') except AttributeError: initialization = '' try: physics = tempnc.__getattribute__('physics_version') rip = 'r'+str(realization)+'i'+str(initialization)+'p'+str(physics) except AttributeError: physics = '' rip = '' latkey = [vrbl in LAT_NAMES for vrbl in tempnc.variables.keys()].index(True) latvname = list(tempnc.variables.keys())[latkey] lonkey = [vrbl in LON_NAMES for vrbl in tempnc.variables.keys()].index(True) lonvname = list(tempnc.variables.keys())[lonkey] dailyout = nc.Dataset('%s_heatwaves_%s_%s_%s_daily.nc'%(defn, model, experiment, rip), mode='w') dailyout.createDimension('time', size=None) dailyout.createDimension('lon', tempnc.dimensions[lonvname].__len__()) dailyout.createDimension('lat', tempnc.dimensions[latvname].__len__()) setattr(dailyout, "author", "Tammas Loughran") setattr(dailyout, "contact", "t.loughran@unsw.edu.au") setattr(dailyout, "source", "https://github.com/tammasloughran/ehfheatwaves") setattr(dailyout, "date", dt.datetime.today().strftime('%Y-%m-%d')) setattr(dailyout, "script", sys.argv[0]) setattr(dailyout, "period", "%s-%s"%(str(timedata.dayone.year), str(timedata.daylast.year))) setattr(dailyout, "base_period", "%s-%s"%(str(options.bpstart), str(options.bpend))) setattr(dailyout, "percentile", "%sth"%(str(options.pcntl))) if model: setattr(dailyout, "model_id", model) setattr(dailyout, "experiment", experiment) setattr(dailyout, "parent_experiment_rip", parent) setattr(dailyout, "realization", realization) setattr(dailyout, "initialization_method", initialization) setattr(dailyout, "physics_version", physics) setattr(dailyout, "version", __version__) if options.tmaxfile: setattr(dailyout, "tmax_file", options.tmaxfile) if options.tminfile: setattr(dailyout, "tmin_file", options.tminfile) if options.maskfile: setattr(dailyout, "mask_file", str(options.maskfile)) setattr(dailyout, "quantile_method", options.qtilemethod) setattr(dailyout, 'Conventions', 'CF-1.7') otime = dailyout.createVariable('time', 'f8', 'time') setattr(otime, 'units', 'days since %s-01-01'%(timedata.dayone.year)) setattr(otime, 'calendar', '365_day') setattr(otime, 'standard_name', 'time') olat = dailyout.createVariable('lat', 'f8', 'lat') setattr(olat, 'standard_name', 'latitude') setattr(olat, 'long_name', 'Latitude') setattr(olat, 'units', 'degrees_north') olon = dailyout.createVariable('lon', 'f8', 'lon') setattr(olon, 'standard_name', 'longitude') setattr(olon, 'long_name', 'Longitude') setattr(olon, 'units', 'degrees_east') oehf = dailyout.createVariable(defn, 'f8', ('time','lat','lon'), fill_value=const.FILL_VAL) if defn=='EHF': setattr(oehf, 'long_name', 'Excess Heat Factor') setattr(oehf, 'units', 'degC2') elif defn=='tx90pct': setattr(oehf, 'long_name', 'Temperature Exceeding tx90pct') setattr(oehf, 'units', 'C') elif defn=='tn90pct': setattr(oehf, 'long_name', 'Temperature Exceeding tn90pct') setattr(oehf, 'units', 'C') setattr(oehf, 'missing_value', const.MISSING_VAL) oevent = dailyout.createVariable('event', 'f8', ('time','lat','lon'), fill_value=const.FILL_VAL) setattr(oevent, 'long_name', 'Event indicator') setattr(oevent, 'description', 'Indicates whether a heatwave is happening on that day') setattr(oevent, 'missing_value', const.MISSING_VAL) oends = dailyout.createVariable('ends', 'f8', ('time','lat','lon'), fill_value=const.FILL_VAL) setattr(oends, 'long_name', 'Duration at start of heatwave') setattr(oends, 'units', 'days') setattr(oends, 'missing_value', const.MISSING_VAL) otime[:] = range(0,original_shape[0],1) olat[:] = tempnc.variables[latvname][:] olon[:] = tempnc.variables[lonvname][:] exceed[exceed.mask==True] = const.MISSING_VAL if options.maskfile: dummy_array = np.ones(original_shape)*const.FILL_VAL dummy_array[:,mask] = exceed oehf[:] = dummy_array.copy() dummy_array = np.ones(original_shape)*const.FILL_VAL dummy_array[:,mask] = event oevent[:] = dummy_array.copy() dummy_array = np.ones(original_shape)*const.FILL_VAL dummy_array[:,mask] = ends oends[:] = dummy_array.copy() else: oehf[:] = exceed oevent[:] = event oends[:] = ends tempnc.close() dailyout.close()
def save_ehi(EHIsig: numpy.ndarray, EHIaccl: numpy.ndarray, options, timedata: TimeData, original_shape: tuple, mask: numpy.ndarray) ‑> None
-
Save the daily data to netcdf file. Input arrays are 2D, (time, space) and are either reshaped or indexed with a land-sea mask.
Arguments:
- EHIsig : EHI significance index.
- EHIaccl : EHI acclimatisation index.
- options :
- timedata : TimeData object.
- original_shape : The original shape of the input data.
- mask : Land-sea mask.
Expand source code
def save_ehi( EHIsig:np.ndarray, EHIaccl:np.ndarray, options, timedata:TimeData, original_shape:tuple, mask:np.ndarray, )->None: """Save the daily data to netcdf file. Input arrays are 2D, (time, space) and are either reshaped or indexed with a land-sea mask. ## Arguments: - EHIsig : EHI significance index. - EHIaccl : EHI acclimatisation index. - options : - timedata : TimeData object. - original_shape : The original shape of the input data. - mask : Land-sea mask. """ if options.tmaxfile: filename = options.tmaxfile elif options.tminfile: filename = options.tminfile if any([(wildcard in filename) for wildcard in ['*','?','[']]): tempnc = nc.MFDataset(filename, 'r') else: tempnc = nc.Dataset(filename, 'r') try: experiment = tempnc.__getattribute__('experiment') except AttributeError: experiment = '' try: model = tempnc.__getattribute__('model_id') except AttributeError: model = '' try: parent = tempnc.__getattribute__('parent_experiment_rip') except AttributeError: parent = '' try: realization = tempnc.__getattribute__('realization') except AttributeError: realization = '' try: initialization = tempnc.__getattribute__('initialization_method') except AttributeError: initialization = '' try: physics = tempnc.__getattribute__('physics_version') rip = 'r'+str(realization)+'i'+str(initialization)+'p'+str(physics) except AttributeError: physics = '' rip = '' latkey = [vrbl in LAT_NAMES for vrbl in tempnc.variables.keys()].index(True) latvname = list(tempnc.variables.keys())[latkey] lonkey = [vrbl in LON_NAMES for vrbl in tempnc.variables.keys()].index(True) lonvname = list(tempnc.variables.keys())[lonkey] defn = 'EHI' dailyout = nc.Dataset('%s_heatwaves_%s_%s_%s_daily.nc'%(defn, model, experiment, rip), mode='w') dailyout.createDimension('time', size=None) dailyout.createDimension('lon', tempnc.dimensions[lonvname].__len__()) dailyout.createDimension('lat', tempnc.dimensions[latvname].__len__()) setattr(dailyout, "author", "Tammas Loughran") setattr(dailyout, "contact", "t.loughran@unsw.edu.au") setattr(dailyout, "source", "https://github.com/tammasloughran/ehfheatwaves") setattr(dailyout, "date", dt.datetime.today().strftime('%Y-%m-%d')) setattr(dailyout, "script", sys.argv[0]) setattr(dailyout, "period", "%s-%s"%(str(timedata.dayone.year), str(timedata.daylast.year))) setattr(dailyout, "base_period", "%s-%s"%(str(options.bpstart), str(options.bpend))) setattr(dailyout, "percentile", "%sth"%(str(options.pcntl))) if model: setattr(dailyout, "model_id", model) setattr(dailyout, "experiment", experiment) setattr(dailyout, "parent_experiment_rip", parent) setattr(dailyout, "realization", realization) setattr(dailyout, "initialization_method", initialization) setattr(dailyout, "physics_version", physics) setattr(dailyout, "version", __version__) if options.tmaxfile: setattr(dailyout, "tmax_file", options.tmaxfile) if options.tminfile: setattr(dailyout, "tmin_file", options.tminfile) if options.maskfile: setattr(dailyout, "mask_file", str(options.maskfile)) setattr(dailyout, "quantile_method", options.qtilemethod) setattr(dailyout, 'Conventions', 'CF-1.7') otime = dailyout.createVariable('time', 'f8', 'time') setattr(otime, 'standard_name', 'time') setattr(otime, 'units', 'days since %s-01-01'%(timedata.dayone.year)) setattr(otime, 'calendar', '365_day') olat = dailyout.createVariable('lat', 'f8', 'lat') setattr(olat, 'standard_name', 'latitude') setattr(olat, 'long_name', 'Latitude') setattr(olat, 'units', 'degrees_north') olon = dailyout.createVariable('lon', 'f8', 'lon') setattr(olon, 'standard_name', 'longitude') setattr(olon, 'long_name', 'Longitude') setattr(olon, 'units', 'degrees_east') oehisig = dailyout.createVariable( 'EHIsig', 'f8', ('time','lat','lon'), fill_value=const.FILL_VAL, ) setattr(oehisig, 'long_name', 'Excess Heat Index Significance') setattr(oehisig, 'units', 'C') setattr(oehisig, 'missing_value', const.MISSING_VAL) setattr(oehisig, 'valid_range', (0,100)) oehiaccl = dailyout.createVariable( 'EHIaccl', 'f8', ('time','lat','lon'), fill_value=const.FILL_VAL, ) setattr(oehiaccl, 'long_name', 'Excess Heat Index Acclimatisation') setattr(oehiaccl, 'units', 'C') setattr(oehiaccl, 'missing_value', const.MISSING_VAL) setattr(oehiaccl, 'valid_range', (0,100)) otime[:] = range(0, original_shape[0], 1) olat[:] = tempnc.variables[latvname][:] olon[:] = tempnc.variables[lonvname][:] if options.maskfile: dummy_array = np.ones(original_shape)*const.FILL_VAL dummy_array[:,mask] = EHIsig oehisig[:] = dummy_array.copy() dummy_array = np.ones(original_shape)*const.FILL_VAL dummy_array[:,mask] = EHIaccl oehiaccl[:] = dummy_array.copy() else: oehisig[:] = EHIsig oehiaccl[:] = EHIaccl tempnc.close() dailyout.close()
def save_yearly(HWA, HWM, HWN, HWF, HWD, HWT, tpct, definition: str, timedata, options, mask: numpy.ndarray) ‑> None
-
Save yearly data to netcdf file.
Input aspect arrays are 2D, timeXspace and are either reshaped or indexed with a land-sea mask.
Arguments:
- HWA :
- HWM :
- HWN :
- HWF :
- HWD :
- HWT :
- txpct : Percentile thresholds.
- definition : Name of heatwave definition.
- timedata : TimeData onbject.
- options :
- mask : Land-sea mask.
Expand source code
def save_yearly( HWA, HWM, HWN, HWF, HWD, HWT, tpct, definition:str, timedata, options, mask:np.ndarray, )->None: """Save yearly data to netcdf file. Input aspect arrays are 2D, timeXspace and are either reshaped or indexed with a land-sea mask. ## Arguments: - HWA : - HWM : - HWN : - HWF : - HWD : - HWT : - txpct : Percentile thresholds. - definition : Name of heatwave definition. - timedata : TimeData onbject. - options : - mask : Land-sea mask. """ if any([(wildcard in options.tmaxfile) for wildcard in ['*','?','[']]): tempnc = nc.MFDataset(options.tmaxfile, 'r') else: tempnc = nc.Dataset(options.tmaxfile, 'r') nyears = HWA.shape[0] try: experiment = tempnc.__getattribute__('experiment') except AttributeError: experiment = '' try: model = tempnc.__getattribute__('model_id') except AttributeError: model = '' try: parent = tempnc.__getattribute__('parent_experiment_rip') except AttributeError: parent = '' try: realization = tempnc.__getattribute__('realization') except AttributeError: realization = '' try: initialization = tempnc.__getattribute__('initialization_method') except AttributeError: initialization = '' try: physics = tempnc.__getattribute__('physics_version') rip = 'r'+str(realization)+'i'+str(initialization)+'p'+str(physics) except AttributeError: physics = '' rip = '' latkey = [vrbl in LAT_NAMES for vrbl in tempnc.variables.keys()].index(True) latvname = list(tempnc.variables.keys())[latkey] lonkey = [vrbl in LON_NAMES for vrbl in tempnc.variables.keys()].index(True) lonvname = list(tempnc.variables.keys())[lonkey] space = (tempnc.dimensions[latvname].__len__(), tempnc.dimensions[lonvname].__len__()) yearlyout = nc.Dataset('%s_heatwaves_%s_%s_%s_yearly_%s.nc'%(definition, model, experiment, rip, options.season), 'w') yearlyout.createDimension('time', size=None) yearlyout.createDimension('lon', tempnc.dimensions[lonvname].__len__()) yearlyout.createDimension('lat', tempnc.dimensions[latvname].__len__()) yearlyout.createDimension('day', timedata.daysinyear) setattr(yearlyout, "author", "Tammas Loughran") setattr(yearlyout, "contact", "t.loughran@unsw.edu.au") setattr(yearlyout, "source", "https://github.com/tammasloughran/ehfheatwaves") setattr(yearlyout, "date", dt.datetime.today().strftime('%Y-%m-%d')) setattr(yearlyout, "script", sys.argv[0]) if model: setattr(yearlyout, "model_id", model) setattr(yearlyout, "experiment", experiment) setattr(yearlyout, "parent_experiment_rip", parent) setattr(yearlyout, "realization", realization) setattr(yearlyout, "initialization_method", initialization) setattr(yearlyout, "physics_version", physics) setattr(yearlyout, "period", "%s-%s"%(str(timedata.dayone.year),str(timedata.daylast.year))) setattr(yearlyout, "base_period", "%s-%s"%(str(options.bpstart),str(options.bpend))) setattr(yearlyout, "percentile", "%sth"%(str(options.pcntl))) setattr(yearlyout, "definition", definition) setattr(yearlyout, "frequency", "yearly") setattr(yearlyout, "season", options.season) setattr(yearlyout, "season_note", ("The year of a season is the year it starts in. SH summer: Nov-Mar. NH summer: May-Sep.")) setattr(yearlyout, "version", __version__) setattr(yearlyout, "tmax_file", options.tmaxfile) setattr(yearlyout, "tmin_file", options.tminfile) if options.maskfile: setattr(yearlyout, "mask_file", options.maskfile) setattr(yearlyout, "quantile_method", options.qtilemethod) setattr(yearlyout, 'Conventions', 'CF-1.7') otime = yearlyout.createVariable('time', 'f8', 'time') setattr(otime, 'units', 'years since 0001-06-01 00:00:00') setattr(otime, 'standard_name', 'time') setattr(otime, 'calendar', 'proleptic_gregorian') olat = yearlyout.createVariable('lat', 'f8', 'lat') setattr(olat, 'standard_name', 'latitude') setattr(olat, 'long_name', 'Latitude') setattr(olat, 'units', 'degrees_north') setattr(olat, 'axis', 'Y') olon = yearlyout.createVariable('lon', 'f8', 'lon') setattr(olon, 'standard_name', 'longitude') setattr(olon, 'long_name', 'Longitude') setattr(olon, 'units', 'degrees_east') setattr(olon, 'axis', 'X') otpct = yearlyout.createVariable('t%spct'%(options.pcntl), 'f8', ('day','lat','lon'), fill_value=const.FILL_VAL) setattr(otpct, 'long_name', '90th percentile') setattr(otpct, 'units', 'degC') setattr(otpct, 'description', '90th percentile of %s-%s'%(str(options.bpstart),str(options.bpend))) setattr(otpct, 'missing_value', const.MISSING_VAL) setattr(otpct, 'valid_range', (-20,100)) HWAout = yearlyout.createVariable('HWA_%s'%(definition), 'f8', ('time','lat','lon'), fill_value=const.FILL_VAL) setattr(HWAout, 'long_name', 'Heatwave Amplitude') if definition=='tx90pct': setattr(HWAout, 'units', 'degC') elif definition=='tn90pct': setattr(HWAout, 'units', 'degC') elif definition=='EHF': setattr(HWAout, 'units', 'degC2') setattr(HWAout, 'description', 'Peak of the hottest heatwave per year') setattr(HWAout, 'missing_value', const.MISSING_VAL) setattr(HWAout, 'valid_range', (-30, 400)) HWMout = yearlyout.createVariable('HWM_%s'%(definition), 'f8', ('time','lat','lon'), fill_value=const.FILL_VAL) setattr(HWMout, 'long_name', 'Heatwave Magnitude') if definition=='tx90pct': setattr(HWMout, 'units', 'degC') elif definition=='tn90pct': setattr(HWMout, 'units', 'degC') elif definition=='EHF': setattr(HWMout, 'units', 'degC2') setattr(HWMout, 'description', 'Average magnitude of the yearly heatwave') setattr(HWMout, 'missing_value', const.MISSING_VAL) setattr(HWMout, 'valid_range' ,(-30, 400)) HWNout = yearlyout.createVariable('HWN_%s'%(definition), 'f8', ('time', 'lat', 'lon'), fill_value=const.FILL_VAL) setattr(HWNout, 'long_name', 'Heatwave Number') setattr(HWNout, 'units','count') setattr(HWNout, 'description', 'Number of heatwaves per year') setattr(HWNout, 'missing_value', const.MISSING_VAL) setattr(HWNout, 'valid_range', (0, 40)) HWFout = yearlyout.createVariable('HWF_%s'%(definition), 'f8', ('time','lat','lon'), fill_value=const.FILL_VAL) setattr(HWFout, 'long_name','Heatwave Frequency') setattr(HWFout, 'units', 'days') setattr(HWFout, 'description', 'Proportion of heatwave days per season') setattr(HWFout, 'missing_value', const.MISSING_VAL) setattr(HWFout, 'valid_range', (0, 165)) HWDout = yearlyout.createVariable('HWD_%s'%(definition), 'f8', ('time','lat','lon'), fill_value=const.FILL_VAL) setattr(HWDout, 'long_name', 'Heatwave Duration') setattr(HWDout, 'units', 'days') setattr(HWDout, 'description', 'Duration of the longest heatwave per year') setattr(HWDout, 'missing_value', const.MISSING_VAL) setattr(HWDout, 'valid_range', (0,165)) HWTout = yearlyout.createVariable('HWT_%s'%(definition), 'f8', ('time','lat','lon'), fill_value=const.FILL_VAL) setattr(HWTout, 'long_name', 'Heatwave Timing') if options.season=='summer': setattr(HWTout, 'units', 'days since 0001-11-01 00:00:00') elif options.season=='winter': setattr(HWTout, 'units', 'days since 0001-05-01 00:00:00') setattr(HWTout, 'description', 'First heat wave day of the season') setattr(HWTout, 'missing_value', const.MISSING_VAL) setattr(HWTout, 'valid_range', (1,366)) otime[:] = range(timedata.dayone.year, timedata.daylast.year) olat[:] = tempnc.variables[latvname][:] olon[:] = tempnc.variables[lonvname][:] if options.maskfile: dummy_array = np.ones((timedata.daysinyear,)+(len(olat),)+(len(olon),))*const.FILL_VAL dummy_array[:,mask] = tpct otpct[:] = dummy_array.copy() dummy_array = np.ones((nyears,)+(len(olat),)+(len(olon),))*const.FILL_VAL dummy_array[:,mask] = HWA HWAout[:] = dummy_array.copy() dummy_array = np.ones((nyears,)+(len(olat),)+(len(olon),))*const.FILL_VAL dummy_array[:,mask] = HWM HWMout[:] = dummy_array.copy() dummy_array = np.ones((nyears,)+(len(olat),)+(len(olon),))*const.FILL_VAL dummy_array[:,mask] = HWN HWNout[:] = dummy_array.copy() dummy_array = np.ones((nyears,)+(len(olat),)+(len(olon),))*const.FILL_VAL dummy_array[:,mask] = HWF HWFout[:] = dummy_array.copy() dummy_array = np.ones((nyears,)+(len(olat),)+(len(olon),))*const.FILL_VAL dummy_array[:,mask] = HWD HWDout[:] = dummy_array.copy() dummy_array = np.ones((nyears,)+(len(olat),)+(len(olon),))*const.FILL_VAL dummy_array[:,mask] = HWT HWTout[:] = dummy_array.copy() else: otpct[:] = tpct HWAout[:] = HWA.reshape((nyears,)+space) HWMout[:] = HWM.reshape((nyears,)+space) HWNout[:] = HWN.reshape((nyears,)+space) HWFout[:] = HWF.reshape((nyears,)+space) HWDout[:] = HWD.reshape((nyears,)+space) HWTout[:] = HWT.reshape((nyears,)+space) tempnc.close() yearlyout.close()
Classes
class Calendar360 (sdate, edate)
-
A basic calendar class with 360 days per year.
Arguments:
- sdate : Start date of calendar.
- edate : End date of calendar.
Expand source code
class Calendar360(): """A basic calendar class with 360 days per year.""" def __init__(self, sdate, edate): """## Arguments: - sdate : Start date of calendar. - edate : End date of calendar. """ if sdate>edate: raise DatesOrderError(sdate, edate) self.year = np.repeat(range(sdate.year, edate.year+1), 360, 0) nyears = len(range(sdate.year, edate.year+1)) self.month = np.tile(np.repeat(range(1, 12+1), 30, 0), nyears) self.day = np.tile(np.tile(range(1, 30+1), 12), nyears) if (sdate.day!=1)|(edate.month!=1): sdoyi = (sdate.month - 1)*30 + sdate.day - 1 self.year = self.year[sdoyi:] self.month = self.month[sdoyi:] self.day = self.day[sdoyi:] if (edate.day!=30)|(edate.month!=12): edoyi = (12 - edate.month)*30 + (30 - edate.day) self.year = self.year[:-edoyi] self.month = self.month[:-edoyi] self.day = self.day[:-edoyi] def __getitem__(self, i): """Return a datetime object for the provided index.""" return dt.datetime(self.year[i], self.month[i], self.day[i])
class DatesOrderError (start, end)
-
Exception to be raised when the calendar start day occurs before the end day.
Expand source code
class DatesOrderError(Exception): """Exception to be raised when the calendar start day occurs before the end day.""" def __init__(self, start, end): self.start = start self.end = end print("Calendar end date appears before the start date") print("start: ", self.start) print("end: ", self.end)
Ancestors
- builtins.Exception
- builtins.BaseException
class TimeData (filename: str, timevname: str)
-
Data class for time data from an netcdf file.
Arguments:
- filename : Input file name.
- timevarname : Name of the time variable in the input file.
Expand source code
class TimeData(object): """Data class for time data from an netcdf file.""" def __init__(self, filename:str, timevname:str): """## Arguments: - filename : Input file name. - timevarname : Name of the time variable in the input file. """ if any([(wildcard in filename) for wildcard in ['*','?','[']]): tempnc = nc.MFDataset(filename, 'r') nctime = tempnc.variables[timevname] nctime = nc.MFTime(nctime) else: tempnc = nc.Dataset(filename, 'r') nctime = tempnc.variables[timevname] try: self.calendar = nctime.calendar except: self.calendar = 'proleptic_gregorian' if not self.calendar: print('Unrecognized calendar. Using gregorian.') self.calendar = 'gregorian' if self.calendar=='360_day': self.daysinyear = 360 # 360 day season start and end indices self.SHS = (300,450) self.SHW = (120,270) self.dayone = nc.num2date(nctime[0], nctime.units, calendar=self.calendar) self.daylast = nc.num2date(nctime[-1], nctime.units, calendar=self.calendar) self.dates = Calendar360(self.dayone, self.daylast) else: self.daysinyear = 365 # 365 day season start and end indices self.SHS = (304,455) self.SHW = (120,273) if tempnc.variables[timevname].units=='day as %Y%m%d.%f': st = str(int(nctime[0])) nd = str(int(nctime[-1])) self.dayone = dt.datetime(int(st[:4]), int(st[4:6]), int(st[6:])) self.daylast = dt.datetime(int(nd[:4]), int(nd[4:6]), int(nd[6:])) else: self.dayone = nc.num2date(nctime[0], nctime.units, calendar=self.calendar) self.daylast = nc.num2date(nctime[-1], nctime.units, calendar=self.calendar) self.dates = pd.period_range(str(self.dayone), str(self.daylast)) # Remove leap days. Maybe this should be a separate function? self.noleapdates = self.dates[(self.dates.month!=2)|(self.dates.day!=29)]