class RunningAverage(Environment): ''' Defines a running average time series for a wind or tide ''' _schema = RunningAverageSchema def __init__(self, wind=None, timeseries=None, past_hours_to_average=3, **kwargs): """ Initializes a running average object from a wind and past hours to average If no wind is given, timeseries gets initialized as: timeseries = np.zeros((1,), dtype=basic_types.datetime_value_2d) units = 'mps' (note: probably should be an error) All other keywords are optional. Optional parameters (kwargs): :param past_hours_to_average: default is 3 """ self.units = 'mps' self.format = 'uv' self._past_hours_to_average = past_hours_to_average self.wind = wind if (wind is None and timeseries is None): mvg_timeseries = np.array([(sec_to_date(zero_time()), [0.0, 0.0])], dtype=basic_types.datetime_value_2d) moving_ts = self._convert_to_time_value_pair(mvg_timeseries) elif wind is not None: moving_ts = (wind.ossm.create_running_average( self._past_hours_to_average)) else: self.wind = Wind(timeseries, units='mps', format='uv') moving_ts = (self.wind.ossm.create_running_average( self._past_hours_to_average)) self.ossm = CyTimeseries(timeseries=moving_ts) super(RunningAverage, self).__init__(**kwargs) def __repr__(self): self_ts = self.timeseries.__repr__() return ('{0.__class__.__module__}.{0.__class__.__name__}(' 'timeseries={1})'.format(self, self_ts)) def __str__(self): return ("Running Average ( " "timeseries=RunningAverage.get_timeseries('uv'), " "format='uv')") @property def past_hours_to_average(self): return self._past_hours_to_average @past_hours_to_average.setter def past_hours_to_average(self, value): """ How many hours for running average """ # may want a check on value self._past_hours_to_average = value @property def timeseries(self): return self.get_timeseries() def _convert_to_time_value_pair(self, datetime_value_2d): ''' fmt datetime_value_2d so it is a numpy array with dtype=basic_types.time_value_pair as the C++ code expects ''' # following fails for 0-d objects so make sure we have a 1-D array # to work with datetime_value_2d = np.asarray(datetime_value_2d, dtype=basic_types.datetime_value_2d) if datetime_value_2d.shape == (): datetime_value_2d = np.asarray([datetime_value_2d], dtype=basic_types.datetime_value_2d) timeval = to_time_value_pair(datetime_value_2d, "uv") return timeval def get_timeseries(self, datetime=None): """ Returns the timeseries in the requested format. If datetime=None, then the original timeseries that was entered is returned. If datetime is a list containing datetime objects, then the wind value for each of those date times is determined by the underlying CyOSSMTime object and the timeseries is returned. The output format is defined by the strings 'r-theta', 'uv' :param datetime: [optional] datetime object or list of datetime objects for which the value is desired :type datetime: datetime object :returns: numpy array containing dtype=basic_types.datetime_value_2d. Contains user specified datetime and the corresponding values in 'm/s' and 'uv' format """ if datetime is None: datetimeval = to_datetime_value_2d(self.ossm.timeseries, 'uv') else: datetime = np.asarray(datetime, dtype='datetime64[s]').reshape(-1) timeval = np.zeros((len(datetime), ), dtype=basic_types.time_value_pair) timeval['time'] = date_to_sec(datetime) timeval['value'] = self.ossm.get_time_value(timeval['time']) datetimeval = to_datetime_value_2d(timeval, 'uv') return datetimeval def prepare_for_model_run(self, model_time): """ Make sure we are up to date with the referenced time series """ if self.wind is None: msg = "wind object not defined for WindMover" raise ReferencedObjectNotSet(msg) model_time = date_to_sec(model_time) self.create_running_average_timeseries(self._past_hours_to_average, model_time) def prepare_for_model_step(self, model_time): """ Make sure we are up to date with the referenced time series """ model_time = date_to_sec(model_time) if self.ossm.check_time_in_range(model_time): return self.create_running_average_timeseries(self._past_hours_to_average, model_time) def create_running_average_timeseries(self, past_hours_to_average, model_time=0): """ Creates the timeseries of the RunningAverage object :param past_hours_to_average: amount of data to use in the averaging """ # first get the time series from the C++ function # self.timeseries = wind.ossm.create_running_average(past_hours) # do we need to dispose of old one here? moving_timeseries = (self.wind.ossm.create_running_average( past_hours_to_average, model_time)) # here should set the timeseries since the CyOSSMTime # should already exist self.ossm.timeseries = moving_timeseries def get_value(self, time): ''' Return the value at specified time and location. Timeseries are independent of location; however, a gridded datafile may require location so this interface may get refactored if it needs to support different types of data. It assumes the data in SI units (m/s) and 'uv' format .. note:: It invokes get_timeseries(..) function ''' if self.ossm.timeseries is None: self.create_running_average_timeseries(self.past_hours_to_average) # if check on time range here: # self.create_running_average_timeseries(self.past_hours, # 'm/s', 'uv') data = self.get_timeseries(time) return tuple(data[0]['value'])
class RunningAverage(Environment, Serializable): ''' Defines a running average time series for a wind or tide ''' _update = [] # used to create new obj or as readonly parameter _create = [] _create.extend(_update) _state = copy.deepcopy(Environment._state) _state += [Field('wind', save=True, update=True, save_reference=True)] _state.add(save=_create, update=_update) _schema = RunningAverageSchema # _state.add_field([serializable.Field('timeseries', save=True, # update=True) # ]) # _state['name'].test_for_eq = False # list of valid velocity units for timeseries # valid_vel_units = _valid_units('Velocity') def __init__(self, wind=None, timeseries=None, past_hours_to_average=3, **kwargs): """ Initializes a running average object from a wind and past hours to average If no wind is given, timeseries gets initialized as: timeseries = np.zeros((1,), dtype=basic_types.datetime_value_2d) units = 'mps' (note: probably should be an error) All other keywords are optional. Optional parameters (kwargs): :param past_hours_to_average: default is 3 """ self.units = 'mps' self.format = 'uv' self._past_hours_to_average = past_hours_to_average self.wind = wind if (wind is None and timeseries is None): mvg_timeseries = np.array([(sec_to_date(zero_time()), [0.0, 0.0])], dtype=basic_types.datetime_value_2d) moving_timeseries = self._convert_to_time_value_pair(mvg_timeseries) else: if wind is not None: moving_timeseries = wind.ossm.create_running_average(self._past_hours_to_average) else: self.wind = Wind(timeseries, units='mps', format='uv') moving_timeseries = self.wind.ossm.create_running_average(self._past_hours_to_average) # print "moving_timeseries" # print moving_timeseries self.ossm = CyTimeseries(timeseries=moving_timeseries) super(RunningAverage, self).__init__(**kwargs) def __repr__(self): self_ts = self.timeseries.__repr__() return ('{0.__class__.__module__}.{0.__class__.__name__}(' 'timeseries={1}' ')').format(self, self_ts) def __str__(self): return ("Running Average ( " "timeseries=RunningAverage.get_timeseries('uv'), " "format='uv')") @property def past_hours_to_average(self): return self._past_hours_to_average @past_hours_to_average.setter def past_hours_to_average(self, value): """ How many hours for running average """ # may want a check on value self._past_hours_to_average = value @property def timeseries(self): return self.get_timeseries() def _convert_to_time_value_pair(self, datetime_value_2d): ''' fmt datetime_value_2d so it is a numpy array with dtype=basic_types.time_value_pair as the C++ code expects ''' # following fails for 0-d objects so make sure we have a 1-D array # to work with datetime_value_2d = np.asarray(datetime_value_2d, dtype=basic_types.datetime_value_2d) if datetime_value_2d.shape == (): datetime_value_2d = np.asarray([datetime_value_2d], dtype=basic_types.datetime_value_2d) # self._check_units(units) # self._check_timeseries(datetime_value_2d, units) # datetime_value_2d['value'] = \ # self._convert_units(datetime_value_2d['value'], # fmt, units, 'meter per second') timeval = to_time_value_pair(datetime_value_2d, "uv") return timeval def get_timeseries(self, datetime=None): """ Returns the timeseries in the requested format. If datetime=None, then the original timeseries that was entered is returned. If datetime is a list containing datetime objects, then the wind value for each of those date times is determined by the underlying CyOSSMTime object and the timeseries is returned. The output format is defined by the strings 'r-theta', 'uv' :param datetime: [optional] datetime object or list of datetime objects for which the value is desired :type datetime: datetime object :returns: numpy array containing dtype=basic_types.datetime_value_2d. Contains user specified datetime and the corresponding values in 'm/s' and 'uv' format """ if datetime is None: datetimeval = to_datetime_value_2d(self.ossm.timeseries, 'uv') else: datetime = np.asarray(datetime, dtype='datetime64[s]').reshape(-1) timeval = np.zeros((len(datetime), ), dtype=basic_types.time_value_pair) timeval['time'] = date_to_sec(datetime) timeval['value'] = self.ossm.get_time_value(timeval['time']) datetimeval = to_datetime_value_2d(timeval, 'uv') return datetimeval def prepare_for_model_run(self, model_time): """ Make sure we are up to date with the referenced time series """ if self.wind is None: msg = "wind object not defined for WindMover" raise ReferencedObjectNotSet(msg) model_time = date_to_sec(model_time) self.create_running_average_timeseries(self._past_hours_to_average, model_time) def prepare_for_model_step(self, model_time): """ Make sure we are up to date with the referenced time series """ model_time = date_to_sec(model_time) if self.ossm.check_time_in_range(model_time): return else: if self.wind.ossm.check_time_in_range(model_time): # there is wind data for this time so create # a new running average self.create_running_average_timeseries(self._past_hours_to_average, model_time) self.create_running_average_timeseries(self._past_hours_to_average, model_time) def create_running_average_timeseries(self, past_hours_to_average, model_time=0): """ Creates the timeseries of the RunningAverage object :param past_hours_to_average: amount of data to use in the averaging """ # first get the time series from the C++ function # self.timeseries = wind.ossm.create_running_average(past_hours) # do we need to dispose of old one here? moving_timeseries = self.wind.ossm.create_running_average(past_hours_to_average, model_time) # here should set the timeseries since the CyOSSMTime # should already exist self.ossm.timeseries = moving_timeseries # self.ossm = CyOSSMTime(timeseries=moving_timeseries) def get_value(self, time): ''' Return the value at specified time and location. Timeseries are independent of location; however, a gridded datafile may require location so this interface may get refactored if it needs to support different types of data. It assumes the data in SI units (m/s) and 'uv' format .. note:: It invokes get_timeseries(..) function ''' if self.ossm.timeseries is None: self.create_running_average_timeseries(self.past_hours_to_average) # if check on time range here: # self.create_running_average_timeseries(self.past_hours, # 'm/s', 'uv') data = self.get_timeseries(time) return tuple(data[0]['value']) def serialize(self, json_='webapi'): """ Since 'wind' property is saved as references in save file need to add appropriate node to WindMover schema for 'webapi' """ toserial = self.to_serialize(json_) schema = self.__class__._schema() if json_ == 'webapi': if self.wind: # add wind schema schema.add(WindSchema(name='wind')) serial = schema.serialize(toserial) return serial @classmethod def deserialize(cls, json_): """ append correct schema for wind object """ schema = cls._schema() if 'wind' in json_: schema.add(WindSchema(name='wind')) _to_dict = schema.deserialize(json_) return _to_dict
class RunningAverage(Environment): ''' Defines a running average time series for a wind or tide ''' _schema = RunningAverageSchema def __init__(self, wind=None, timeseries=None, past_hours_to_average=3, **kwargs): """ Initializes a running average object from a wind and past hours to average If no wind is given, timeseries gets initialized as: timeseries = np.zeros((1,), dtype=basic_types.datetime_value_2d) units = 'mps' (note: probably should be an error) All other keywords are optional. Optional parameters (kwargs): :param past_hours_to_average: default is 3 """ self.units = 'mps' self.format = 'uv' self._past_hours_to_average = past_hours_to_average self.wind = wind if (wind is None and timeseries is None): mvg_timeseries = np.array([(sec_to_date(zero_time()), [0.0, 0.0])], dtype=basic_types.datetime_value_2d) moving_ts = self._convert_to_time_value_pair(mvg_timeseries) elif wind is not None: moving_ts = (wind.ossm .create_running_average(self._past_hours_to_average)) else: self.wind = Wind(timeseries, units='mps', coord_sys='uv') moving_ts = (self.wind.ossm .create_running_average(self._past_hours_to_average)) self.ossm = CyTimeseries(timeseries=moving_ts) super(RunningAverage, self).__init__(**kwargs) def __repr__(self): self_ts = self.timeseries.__repr__() return ('{0.__class__.__module__}.{0.__class__.__name__}(' 'timeseries={1})' .format(self, self_ts)) def __str__(self): return ("Running Average ( " "timeseries=RunningAverage.get_timeseries('uv'), " "format='uv')") @property def past_hours_to_average(self): return self._past_hours_to_average @past_hours_to_average.setter def past_hours_to_average(self, value): """ How many hours for running average """ # may want a check on value self._past_hours_to_average = value @property def timeseries(self): return self.get_timeseries() def _convert_to_time_value_pair(self, datetime_value_2d): ''' fmt datetime_value_2d so it is a numpy array with dtype=basic_types.time_value_pair as the C++ code expects ''' # following fails for 0-d objects so make sure we have a 1-D array # to work with datetime_value_2d = np.asarray(datetime_value_2d, dtype=basic_types.datetime_value_2d) if datetime_value_2d.shape == (): datetime_value_2d = np.asarray([datetime_value_2d], dtype=basic_types.datetime_value_2d) timeval = to_time_value_pair(datetime_value_2d, "uv") return timeval def get_timeseries(self, datetime=None): """ Returns the timeseries in the requested format. If datetime=None, then the original timeseries that was entered is returned. If datetime is a list containing datetime objects, then the wind value for each of those date times is determined by the underlying CyOSSMTime object and the timeseries is returned. The output format is defined by the strings 'r-theta', 'uv' :param datetime: [optional] datetime object or list of datetime objects for which the value is desired :type datetime: datetime object :returns: numpy array containing dtype=basic_types.datetime_value_2d. Contains user specified datetime and the corresponding values in 'm/s' and 'uv' format """ if datetime is None: datetimeval = to_datetime_value_2d(self.ossm.timeseries, 'uv') else: datetime = np.asarray(datetime, dtype='datetime64[s]').reshape(-1) timeval = np.zeros((len(datetime), ), dtype=basic_types.time_value_pair) timeval['time'] = date_to_sec(datetime) timeval['value'] = self.ossm.get_time_value(timeval['time']) datetimeval = to_datetime_value_2d(timeval, 'uv') return datetimeval def prepare_for_model_run(self, model_time): """ Make sure we are up to date with the referenced time series """ if self.wind is None: msg = "wind object not defined for WindMover" raise ReferencedObjectNotSet(msg) model_time = date_to_sec(model_time) self.create_running_average_timeseries(self._past_hours_to_average, model_time) def prepare_for_model_step(self, model_time): """ Make sure we are up to date with the referenced time series """ model_time = date_to_sec(model_time) if self.ossm.check_time_in_range(model_time): return self.create_running_average_timeseries(self._past_hours_to_average, model_time) def create_running_average_timeseries(self, past_hours_to_average, model_time=0): """ Creates the timeseries of the RunningAverage object :param past_hours_to_average: amount of data to use in the averaging """ # first get the time series from the C++ function # self.timeseries = wind.ossm.create_running_average(past_hours) # do we need to dispose of old one here? moving_timeseries = (self.wind.ossm .create_running_average(past_hours_to_average, model_time)) # here should set the timeseries since the CyOSSMTime # should already exist self.ossm.timeseries = moving_timeseries def get_value(self, time): ''' Return the value at specified time and location. Timeseries are independent of location; however, a gridded datafile may require location so this interface may get refactored if it needs to support different types of data. It assumes the data in SI units (m/s) and 'uv' format .. note:: It invokes get_timeseries(..) function ''' if self.ossm.timeseries is None: self.create_running_average_timeseries(self.past_hours_to_average) # if check on time range here: # self.create_running_average_timeseries(self.past_hours, # 'm/s', 'uv') data = self.get_timeseries(time) return tuple(data[0]['value'])