def daily_to_daytime_hourly_trapezoid( latitude, statistic='mean', input_ts='-', start_date=None, end_date=None, float_format='%g', print_input=''): ''' Daily to hourly disaggregation based on a trapezoidal shape. ''' from tstoolbox import tstoolbox tsd = tsutils.common_kwds(tsutils.read_iso_ts(input_ts), start_date=start_date, end_date=end_date, pick=None) lrad = latitude*np.pi/180.0 ad = 0.40928*np.cos(0.0172141*(172 - tsd.index.dayofyear)) ss = np.sin(lrad)*np.sin(ad) cs = np.cos(lrad)*np.cos(ad) x2 = -ss/cs delt = 7.6394*(np.pi/2.0 - np.arctan(x2/np.square(1 - x2**2))) sunr = 12.0 - delt/2.0 #develop hourly distribution given sunrise, #sunset and length of day (DELT) dtr2 = delt / 2.0 dtr4 = delt / 4.0 crad = 2.0/3.0/dtr2/60 # using minutes... tr2 = sunr + dtr4 tr3 = tr2 + dtr2 tr4 = tr3 + dtr4 sdate = datetime.datetime(tsd.index[0].year, tsd.index[0].month, tsd.index[0].day) edate = datetime.datetime(tsd.index[-1].year, tsd.index[-1].month, tsd.index[-1].day) + datetime.timedelta(days=1) - datetime.timedelta(hours=1) datevalue = pandas.DatetimeIndex(start=sdate, end=edate, freq='MIN') fdata = pandas.Series([np.nan]*(len(datevalue)), index=datevalue) fdata[0] = 0.0 fdata[-1] = 0.0 for index in range(len(sunr)): cdate = tsd.index[index] fdata[datetime.datetime(cdate.year, cdate.month, cdate.day, int(sunr[index]), int((sunr[index] - int(sunr[index]))*60))] = 0.0 fdata[datetime.datetime(cdate.year, cdate.month, cdate.day, int(tr4[index]), int((tr4[index] - int(tr4[index]))*60))] = 0.0 fdata[datetime.datetime(cdate.year, cdate.month, cdate.day, int(tr2[index]), int((tr2[index] - int(tr2[index]))*60))] = crad[index] fdata[datetime.datetime(cdate.year, cdate.month, cdate.day, int(tr3[index]), int((tr3[index] - int(tr3[index]))*60))] = crad[index] fdata = fdata.interpolate('linear') fdata = fdata.fillna(0.0) fdata = tstoolbox.aggregate(statistic=statistic, agg_interval='H', input_ts=fdata ) return tsutils.print_input(print_input, tsd, fdata, None, float_format=float_format)
def PET_hargreaves(latitude, tmax=None, tmin=None, input_ts='-', start_date=None, end_date=None, float_format='%g', print_input=''): ''' Calculate potential evaporation as expected from a shallow body of water using the Hargreaves equation which needs the daily maximum and minimum temperatures. Input is degrees C. and returns mm/day. :param -i, --input_ts <str>: Filename with data in 'ISOdate,value' format or '-' for stdin. :param -s, --start_date <str>: The start_date of the series in ISOdatetime format, or 'None' for beginning. :param -e, --end_date <str>: The end_date of the series in ISOdatetime format, or 'None' for end. :param latitude <float>: Latitude of the station. :param tmax: Specify comma separated column names or column numbers that represent the daily maximum temperature in degrees C. The default is None, and if None there has to be an even number of columns and tmax is the first half of the columns, and tmin is the last half of the columns. :param tmin: Specify comma separated column names or column numbers that represent the daily minimum temperature in degrees C. The default is None, and if None there has to be an even number of columns and tmax is the first half of the columns, and tmin is the last half of the columns. ''' tsd = tsutils.common_kwds(tsutils.read_iso_ts(input_ts), start_date=start_date, end_date=end_date, pick=None) if tmax is None and tmin is None: tmaxcol = tsd.columns[:len(tsd.columns)/2] tmincol = tsd.columns[len(tsd.columns)/2:] if len(tsd.columns) % 2 == 1: raise ValueError(''' * * Without explicitly defining tmax and tmin columns in the input dataset, the * first half of the columns are tmax and the second half are tmin, which * means that you need an even number of columns. Instead there are {0}. * '''.format(len(tsd.columns))) elif tmax is None or tmin is None: raise ValueError(''' * * Either tmax and tmin must be None OR tmax and tmin must have the same * number of comma separated columns names/numbers. * ''') else: tmaxcol = tmax.split(',') tmincol = tmin.split(',') tmaxdf = tsd[tmaxcol] tmindf = tsd[tmincol] if pd.np.any(tmindf.values > tmaxdf.values): raise ValueError(''' * * There is at least one day where the minimum temperature is greater than the * maximum temperature. * ''') # 'Roll-out' the distribution from day to day. jday = pd.np.arange(1, 367) # calculate right ascension dec = 0.4102*pd.np.sin((2.0*pd.np.pi*(jday - 80.0))/365.0) lrad = latitude*pd.np.pi/180.0 s = pd.np.arccos(-pd.np.tan(dec)*pd.np.tan(lrad)) ra = 118.0/pd.np.pi*(s*pd.np.sin(lrad)*pd.np.sin(dec) + pd.np.cos(lrad)*pd.np.cos(dec)*pd.np.sin(s)) # ra just covers 1 year - need to map onto all years... newra = tmindf.copy() for day in jday: newra[newra.index.dayofyear == day] = ra[day - 1] tsavg = (tmaxdf.values + tmindf.values)/2.0 tsdiff = tmaxdf.values - tmindf.values # Copy tsavg in order to get all of the time components correct. pe = tmaxdf.copy() pe.values[:] = 0.408*0.0023*newra*(tsavg + 17.8)*pd.np.sqrt(abs(tsdiff)) pe.columns = ['PET_hargreaves_{0}'.format(i) for i in range(len(pe.columns))] return tsutils.print_input(print_input, tsd, pe, None, float_format=float_format)
def evaporation( method: Literal["trap", "fixed"], source_units, input_ts="-", columns=None, start_date=None, end_date=None, dropna="no", clean=False, round_index=None, skiprows=None, index_type="datetime", names=None, target_units=None, print_input=False, lat: Optional[FloatLatitude] = None, ): """Disaggregate daily to hourly data.""" target_units = single_target_units(source_units, target_units) pd.options.display.width = 60 if method == "trap" and lat is None: raise ValueError( tsutils.error_wrapper(""" The "trap" method requires latitude with the `lat` keyword. You gave "{lat}". """.format(**locals()))) tsd = tsutils.common_kwds( tsutils.read_iso_ts(input_ts, skiprows=skiprows, names=names, index_type=index_type), start_date=start_date, end_date=end_date, pick=columns, round_index=round_index, dropna=dropna, source_units=source_units, target_units=target_units, clean=clean, ) ntsd = tsd.append( pd.DataFrame(columns=tsd.columns, index=[tsd.index[-1] + datetime.timedelta(days=1)])) ndata = ntsd.resample("H").ffill() fdata = pd.DataFrame(columns=ndata.columns, index=ndata.index, dtype="f") if method == "trap": lrad = lat * np.pi / 180.0 ad = 0.40928 * np.cos(0.0172141 * (172 - tsd.index.dayofyear)) ss = np.sin(lrad) * np.sin(ad) cs = np.cos(lrad) * np.cos(ad) x2 = -ss / cs delt = 7.6394 * (np.pi / 2.0 - np.arctan(x2 / np.square(1 - x2**2))) sunr = 12.0 - delt / 2.0 # develop hourly distribution given sunrise, # sunset and length of day (DELT) dtr2 = delt / 2.0 dtr4 = delt / 4.0 tr2 = sunr + dtr4 tr3 = tr2 + dtr2 tr4 = tr3 + dtr4 for index, toss in enumerate(sunr): cdate = ntsd.index[index] fdata.ix[datetime.datetime(cdate.year, cdate.month, cdate. day, int(sunr[index])), :, ] = 0.0 fdata.ix[datetime.datetime(cdate.year, cdate.month, cdate.day, int(tr4[index]) + 1), :, ] = 0.0 fdata.ix[datetime.datetime(cdate.year, cdate.month, cdate. day, int(round(tr2[index]))), :, ] = 1.0 fdata.ix[datetime.datetime(cdate.year, cdate.month, cdate. day, int(round(tr3[index]))), :, ] = 1.0 fdata.ix[0, :] = 0.0 fdata.ix[-1, :] = 0.0 fdata = fdata.interpolate("linear") fdata = fdata.fillna(0.0) fdata = fdata / fdata.groupby( pd.Grouper(freq="D")).sum().resample("H").ffill() fdata = fdata * ndata fdata = fdata.ix[:-1, :] elif method == "fixed": # DATA EVAPDIST / 0.000,0.000,0.000,0.000,0.000,0.000,0.019,0.041, # $ 0.067,0.088,0.102,0.110,0.110,0.110,0.105,0.095, # $ 0.081,0.055,0.017,0.000,0.000,0.000,0.000,0.000 fdata = fdata.fillna(0.0) fdata[fdata.index.hour == 7] = 0.019 fdata[fdata.index.hour == 8] = 0.041 fdata[fdata.index.hour == 9] = 0.067 fdata[fdata.index.hour == 10] = 0.088 fdata[fdata.index.hour == 11] = 0.102 fdata[fdata.index.hour == 12] = 0.110 fdata[fdata.index.hour == 13] = 0.110 fdata[fdata.index.hour == 14] = 0.110 fdata[fdata.index.hour == 15] = 0.105 fdata[fdata.index.hour == 16] = 0.095 fdata[fdata.index.hour == 17] = 0.081 fdata[fdata.index.hour == 18] = 0.055 fdata[fdata.index.hour == 19] = 0.017 fdata = fdata * ndata fdata = fdata.ix[:-1, :] return tsutils.print_input(print_input, tsd, fdata, None)