Prado EFO PVA
time.py
Go to the documentation of this file.
1 # -*- coding: utf-8 -*-
2 """
3 Created on Tue Apr 28 14:56:51 2020
4 
5 @author: cd
6 """
7 
8 import abc
9 import numpy as np
10 from pandas import DatetimeIndex
11 import datetime as dt
12 from datetime import timedelta
13 from copy import copy
14 
15 class TimeBase(metaclass=abc.ABCMeta):
16  def __init__(self, name):
17  self.namename = name
18  self._observers_observers = []
19  super().__init__()
20  # @abc.abstractmethod
21  # def calc_qout(self, student):
22  # pass
23  @classmethod
24  def __subclasshook__(cls,C):
25  if cls is TimeBase:
26  attrs = set(dir(C))
27  if set(cls.__abstractmethods__) <= attrs:
28  return True
29  return NotImplemented
30 
31  def bind_to(self, callback):
32  self._observers_observers.append(callback)
33 
34 
35 
36 # TODO: Is there a way you could allow the user to modify the start and end
37 # dates to allow changing sim period. There would have to be some sort of sampling
38 # method that is run for each input that resamples based on updated dates.
40  def __init__(self, name, simBgn, simEnd, timeUnit='h', nHrs=1):
41  # Call super class constructor
42  super().__init__(name)
43  # Set subclass properties
44  self.simBgnsimBgn = simBgn
45  self.simEndsimEnd = simEnd
46  self.timeUnittimeUnit = timeUnit
47  # TODO: Not sure the nHrs property is necessary
48  if timeUnit == 'D':
49  self.nHrsnHrs = 24
50  else:
51  self.nHrsnHrs = int(nHrs)
52  # TODO: Look into using np.datetime64 for tDelta (needs to be compative with lookup classes)
53  # self.tDelta = np.timedelta64(self.nHrs, 'h')
54  self.tDeltatDelta = timedelta(hours=self.nHrsnHrs)
55  # TODO: The format of this should be more readable
56  # TODO: These properties could possibly be private
57  self.dateTimedateTime = DatetimeIndex(
58  np.arange(np.datetime64(self.simBgnsimBgn),
59  np.datetime64(self.simEndsimEnd)+np.timedelta64(self.nHrsnHrs, 'h'),
60  np.timedelta64(self.nHrsnHrs, 'h'), dtype='datetime64[h]').astype('datetime64[h]'))
61  self.nStepsnSteps = self.dateTimedateTime.size
62  self.endend = self.nStepsnSteps - 1
63  self.stepssteps = np.arange(self.nStepsnSteps, dtype=int)
64  self._step_step = self.stepssteps[0]
65  self._curDT_curDT = self.dateTimedateTime[0]
66 
67  @property
68  def step(self):
69  return self._step_step
70 
71  @step.setter
72  def step(self, i):
73  self._step_step = self.stepssteps[i].item()
74  self._curDT_curDT = copy(self.dateTimedateTime[i])
75  for callback in self._observers_observers:
76  callback(self._step_step, self._curDT_curDT)
77 
78  @property
79  def curDT(self):
80  return self._curDT_curDT
81 
82  def get_datetime_offset(self, tsOffset):
83  return self.dateTimedateTime[min(self.nStepsnSteps-1, self.stepstepstep + tsOffset)]
84 
85  def get_period(self, dateTimeBgn, dateTimeEnd):
86  pass
87  # Matlab code
88  # iPer = this.nDate > nDateBgn - this.roundFactor ...
89  # & this.nDate < nDateEnd + this.roundFactor;
90 
91  def get_vdate(self):
92  vDate = np.full((len(self.dateTime), 4), np.nan)
93  # TODO: I think you can remove this for loop by just concatenating arrays vertically
94  for i, curDt in enumerate(self.dateTime):
95  vDate[i, :] = [curDt.year, curDt.month, curDt.day, curDt.hour]
96  return vDate
97 
98  def get_ndate(self):
99  return self.dateTimedateTime.astype(np.int64)
100 
101 
103  def __init__(self, name, Tcont, fcstFreq=24, fcstHoriz=360, tFcst=None, fcstDate=None):
104  # Call super class constructor
105  super().__init__(name)
106  # TODO: Maybe build a new Tcont instead of having it passed
107  self.TcontTcont = Tcont
108  self.nHrsnHrs = Tcont.nHrs
109  self.timeUnittimeUnit = Tcont.timeUnit
110  self.tDeltatDelta = Tcont.tDelta
111  # self.nSteps = self.Tcont.nSteps
112  self.nStepsnSteps = int(fcstHoriz/self.TcontTcont.nHrs + 1)
113  self.endend = self.nStepsnSteps - 1
114  self.stepssteps = np.arange(self.nStepsnSteps)
115  self.fcstFreqfcstFreq = int(fcstFreq)
116  self.nFcstsnFcsts = int(np.ceil(self.TcontTcont.nSteps*self.nHrsnHrs/self.fcstFreqfcstFreq))
117  self._rowCurFcst_rowCurFcst = 0
118  self.TcontTcont.bind_to(self.update_current_tFcstupdate_current_tFcst)
119  tDeltaFcst = np.timedelta64(self.fcstFreqfcstFreq, 'h')
120  if fcstDate is None:
121  # tDeltaStep = np.timedelta64(self.nHrs, 'h')
122  # self.fcstDate = DatetimeIndex(
123  # np.arange(np.datetime64(self.Tcont.simBgn) + tDeltaStep,
124  # np.datetime64(self.Tcont.simEnd) + tDeltaStep + tDeltaFcst,
125  # tDeltaFcst, dtype='datetime64[h]').astype('datetime64[h]'))
126  self.fcstDatefcstDate = DatetimeIndex(
127  np.arange(np.datetime64(self.TcontTcont.simBgn),
128  np.datetime64(self.TcontTcont.simEnd) + (tDeltaFcst-1),
129  tDeltaFcst, dtype='datetime64[h]').astype('datetime64[h]'))
130  # self.fcstDate = DatetimeIndex(
131  # np.arange(np.datetime64(self.Tcont.simBgn),
132  # np.datetime64(self.Tcont.simEnd),
133  # tDeltaFcst, dtype='datetime64[h]').astype('datetime64[h]'))
134  if tFcst is None:
135  tFcst = np.empty(self.nFcstsnFcsts, dtype=TimeCont)
136  # TODO: This loop assumes that each step is a day. Should be user specified
137  for i in range(0, self.nFcstsnFcsts):
138  # tBgn = np.datetime64(Tcont.dateTime[i]+np.timedelta64(self.nHrs, 'h'))
139  # tBgn = Tcont.dateTime[max(0, i-1)]
140  idxTcontBgn = max(0, np.where(Tcont.dateTime==self.fcstDatefcstDate[i])[0][0] - 1)
141  tBgn = Tcont.dateTime[idxTcontBgn]
142  tEnd = np.datetime64(tBgn+np.timedelta64(fcstHoriz, 'h'))
143  # Alternative using Pandas:
144  # tEnd = Timestamp(Tcont.dateTime[i]+Timedelta(fcstHoriz, unit='h'))
145  tFcst[i] = TimeCont('timeFcst'+str(i), tBgn, tEnd, Tcont.timeUnit)
146  self.tFcsttFcst = tFcst
147  self._tFcstCur_tFcstCur = self.tFcsttFcst[0]
148 
149  @property
150  def step(self):
151  return self._step_step
152 
153  @step.setter
154  def step(self, i):
155  self._step_step = self._tFcstCur_tFcstCur.steps[i].item()
156  self._curDT_curDT = copy(self._tFcstCur_tFcstCur.dateTime[i])
157  self.get_fcst_timeget_fcst_time().step = self.stepstepstep
158 
159  @property
160  def curDT(self):
161  return self._curDT_curDT
162 
163  def update_current_tFcst(self, step, dateTime):
164  chkCurRow = np.where(self.fcstDatefcstDate<=dateTime)[0][-1]
165  if chkCurRow != self._rowCurFcst_rowCurFcst:
166  self._rowCurFcst_rowCurFcst = chkCurRow
167  self._tFcstCur_tFcstCur = copy(self.tFcsttFcst[self._rowCurFcst_rowCurFcst])
168  for callback in self._observers_observers:
169  callback(self._rowCurFcst_rowCurFcst, dateTime)
170 
171 
172  def get_fcst_time(self):
173  return self._tFcstCur_tFcstCur
174 
175  def get_fcst_step(self):
176  return np.where(self._tFcstCur_tFcstCur.dateTime==self.TcontTcont.curDT)[0][0]
177 
178  def get_datetime_offset(self, tsOffset):
179  return self._tFcstCur_tFcstCur.get_datetime_offset(tsOffset)
180 
181  # def check_fcst_date(self):
182  # return np.any(self.fcstDate == self.Tcont.curDT)
183 
184 
def __subclasshook__(cls, C)
Definition: time.py:24
def __init__(self, name)
Definition: time.py:16
def bind_to(self, callback)
Definition: time.py:31
def curDT(self)
Definition: time.py:79
def get_datetime_offset(self, tsOffset)
Definition: time.py:82
def get_vdate(self)
Definition: time.py:91
def get_period(self, dateTimeBgn, dateTimeEnd)
Definition: time.py:85
def __init__(self, name, simBgn, simEnd, timeUnit='h', nHrs=1)
Definition: time.py:40
def step(self, i)
Definition: time.py:72
def get_ndate(self)
Definition: time.py:98
def step(self)
Definition: time.py:68
def get_fcst_step(self)
Definition: time.py:175
def curDT(self)
Definition: time.py:160
def update_current_tFcst(self, step, dateTime)
Definition: time.py:163
def get_datetime_offset(self, tsOffset)
Definition: time.py:178
def step(self)
Definition: time.py:150
def __init__(self, name, Tcont, fcstFreq=24, fcstHoriz=360, tFcst=None, fcstDate=None)
Definition: time.py:103
def get_fcst_time(self)
Definition: time.py:172
def step(self, i)
Definition: time.py:154