def __init__(self, name=None, units='kg/m^3', temperature=None, salinity=None): if (temperature is None or salinity is None or not isinstance(temperature, TemperatureTS) or not isinstance(salinity, SalinityTS)): raise ValueError('Must provide temperature and salinity ' 'time series Environment objects') if len(temperature.time.time) > len(salinity.time.time): density_times = temperature.time else: density_times = salinity.time dummy_pt = np.array([[0, 0], ]) import gsw from gnome import constants data = [gsw.rho(salinity.at(dummy_pt, t), temperature.at(dummy_pt, t, units='C'), constants.atmos_pressure * 0.0001) for t in density_times.time] TimeseriesData.__init__(self, name, units, time=density_times, data=data)
def __init__(self, name=None, units='kg/m^3', temperature=None, salinity=None): if (temperature is None or salinity is None or not isinstance(temperature, TemperatureTS) or not isinstance(salinity, SalinityTS)): raise ValueError('Must provide temperature and salinity ' 'time series Environment objects') if len(temperature.time.time) > len(salinity.time.time): density_times = temperature.time else: density_times = salinity.time dummy_pt = np.array([ [0, 0], ]) import gsw from gnome import constants data = [ gsw.rho(salinity.at(dummy_pt, t), temperature.at(dummy_pt, t, units='C'), constants.atmos_pressure * 0.0001) for t in density_times.time ] TimeseriesData.__init__(self, name, units, time=density_times, data=data)
def constant(cls, name='', speed=0, direction=0, units='m/s'): """ utility to create a constant wind "timeseries" :param speed: speed of wind :param direction: direction -- degrees True, direction wind is from (degrees True) :param units='m/s': units for speed, as a string, i.e. "knots", "m/s", "cm/s", etc. .. note:: The time for a constant wind timeseries is irrelevant. This function simply sets it to datetime.now() accurate to hours. """ direction = direction * -1 - 90 u = speed * np.cos(direction * np.pi / 180) v = speed * np.sin(direction * np.pi / 180) u = TimeseriesData.constant('u', units, u) v = TimeseriesData.constant('v', units, v) return super(VelocityTS, cls).constant(name, units, variables=[u, v])
def build_test_instance(self, dates, series_data, series_data2): times = Time(dates) return TimeseriesVector(variables=[ TimeseriesData(name='u', time=times, data=series_data), TimeseriesData(name='v', time=times, data=series_data2) ], units='m/s')
def __init__(self, name=None, units='K', time=None, data=None, **kwargs): if 'timeseries' in kwargs: ts = kwargs['timeseries'] time = map(lambda e: e[0], ts) data = np.array(map(lambda e: e[1], ts)) TimeseriesData.__init__(self, name, units, time, data=data)
def get_tsv_instance(self, dates, series_data, series_data2): _t = Time(dates) return TimeseriesVector(variables=[ TimeseriesData(name='u', time=_t, data=series_data), TimeseriesData(name='v', time=_t, data=series_data2) ], units='m/s')
def demo(cls): _t = Time(dates()) tsv = TimeseriesVector(variables=[ TimeseriesData(name='u', time=_t, data=series_data()), TimeseriesData(name='v', time=_t, data=series_data2()) ], units='m/s') return DemoObj(variable=tsv, variables=[tsv, tsv.variables[0]])
def generate_release_timeseries(self, num_ts, max_release, ts): ''' Release timeseries describe release behavior as a function of time. _release_ts describes the number of LEs that should exist at time T _pos_ts describes the spill position at time T All use TimeseriesData objects. ''' t = None if num_ts == 1: #This is a special case, when the release is short enough a single #timestep encompasses the whole thing. if self.release_duration == 0: t = Time([ self.release_time, self.end_release_time + timedelta(seconds=1) ]) else: t = Time([self.release_time, self.end_release_time]) else: t = Time([ self.release_time + timedelta(seconds=ts * step) for step in range(0, num_ts + 1) ]) t.data[-1] = self.end_release_time self._release_ts = TimeseriesData(name=self.name + '_release_ts', time=t, data=np.linspace( 0, max_release, num_ts + 1).astype(int)) lon_ts = TimeseriesData(name=self.name + '_lon_ts', time=t, data=np.linspace(self.start_position[0], self.end_position[0], num_ts + 1)) lat_ts = TimeseriesData(name=self.name + '_lat_ts', time=t, data=np.linspace(self.start_position[1], self.end_position[1], num_ts + 1)) z_ts = TimeseriesData(name=self.name + '_z_ts', time=t, data=np.linspace(self.start_position[2], self.end_position[2], num_ts + 1)) self._pos_ts = TimeseriesVector(name=self.name + '_pos_ts', time=t, variables=[lon_ts, lat_ts, z_ts])
def test_save_load(self, dates, series_data, series_data2): times = Time(dates) tsv = TimeseriesVector(variables=[ TimeseriesData(name='u', time=times, data=series_data), TimeseriesData(name='v', time=times, data=series_data2) ], units='m/s') inst = DemoObj(filename=None, variable=tsv, variables=[tsv, tsv.variables[0]]) saveloc = tempfile.mkdtemp() _json_, zipfile_, _refs = inst.save(saveloc=saveloc) loaded = DemoObj.load(zipfile_) assert inst == loaded
def test_serialization(self, dates, series_data, series_data2): filename = 'foo.nc' times = Time(dates) tsv = TimeseriesVector(variables=[ TimeseriesData(name='u', time=times, data=series_data), TimeseriesData(name='v', time=times, data=series_data2) ], units='m/s') inst = DemoObj(filename=filename, variable=tsv, variables=[tsv, tsv.variables[0]]) serial = inst.serialize() deser = DemoObj.deserialize(serial) assert deser.variable == inst.variable assert deser.variables == inst.variables assert deser.filename == 'foo.nc'
def test_serialization_options(self, dates, series_data, series_data2): times = Time(dates) tsv = TimeseriesVector(variables=[ TimeseriesData(name='u', time=times, data=series_data), TimeseriesData(name='v', time=times, data=series_data2) ], units='m/s') # kludge for platform differences # It should work for the platform the test is running on: if os.name == 'posix': filename = 'some/random/path/foo.nc' else: # if not posix, should be windows filename = os.path.normpath('C:\\foo.nc') inst = DemoObj(filename=filename, variable=tsv, variables=[tsv, tsv.variables[0]]) serial = inst.serialize(options={'raw_paths': False}) assert serial['filename'] == 'foo.nc'
def __init__(self, angle=None, **kwargs): """ :param angle: scalar field of cell rotation angles (for rotated/distorted grids) """ if 'variables' in kwargs: variables = kwargs['variables'] if len(variables) == 2: variables.append( TimeseriesData(name='constant w', data=[0.0], time=Time.constant_time(), units='m/s')) kwargs['variables'] = variables if angle is None: df = None if kwargs.get('dataset', None) is not None: df = kwargs['dataset'] elif kwargs.get('grid_file', None) is not None: df = gridded.utilities.get_dataset(kwargs['grid_file']) if df is not None and 'angle' in df.variables.keys(): # Unrotated ROMS Grid! self.angle = Variable(name='angle', units='radians', time=Time.constant_time(), grid=kwargs['grid'], data=df['angle']) else: self.angle = None else: self.angle = angle super(VelocityGrid, self).__init__(**kwargs)
class PointLineRelease(Release): """ The primary spill source class -- a release of floating non-weathering particles, can be instantaneous or continuous, and be released at a single point, or over a line. """ _schema = PointLineReleaseSchema def __init__(self, release_time=None, start_position=None, num_elements=None, num_per_timestep=None, end_release_time=None, end_position=None, release_mass=0, **kwargs): """ Required Arguments: :param release_time: time the LEs are released (datetime object) :type release_time: datetime.datetime :param start_position: initial location the elements are released :type start_position: 3-tuple of floats (long, lat, z) Optional arguments: .. note:: Either num_elements or num_per_timestep must be given. If both are None, then it defaults to num_elements=1000. If both are given a TypeError is raised because user can only specify one or the other, not both. :param num_elements: total number of elements to be released :type num_elements: integer :param num_per_timestep: fixed number of LEs released at each timestep :type num_elements: integer :param end_release_time=None: optional -- for a time varying release, the end release time. If None, then release is instantaneous :type end_release_time: datetime.datetime :param end_position=None: optional. For moving source, the end position If None, then release from a point source :type end_position: 3-tuple of floats (long, lat, z) :param release_mass=0: optional. This is the mass released in kilograms. :type release_mass: integer num_elements and release_time passed to base class __init__ using super See base :class:`Release` documentation """ self._num_elements = self._num_per_timestep = None if num_elements is None and num_per_timestep is None: num_elements = 1000 super(PointLineRelease, self).__init__(release_time=release_time, num_elements=num_elements, release_mass=release_mass, **kwargs) if num_elements is not None and num_per_timestep is not None: msg = ('Either num_elements released or a release rate, defined by' ' num_per_timestep must be given, not both') raise TypeError(msg) self._num_per_timestep = num_per_timestep # initializes internal variables: _end_release_time, _start_position, # _end_position self.end_release_time = asdatetime(end_release_time) self.start_position = start_position self.end_position = end_position def __repr__(self): return ('{0.__class__.__module__}.{0.__class__.__name__}(' 'release_time={0.release_time!r}, ' 'num_elements={0.num_elements}, ' 'start_position={0.start_position!r}, ' 'end_position={0.end_position!r}, ' 'end_release_time={0.end_release_time!r}' ')'.format(self)) @property def is_pointsource(self): ''' if end_position - start_position == 0, point source otherwise it is a line source :returns: True if point source, false otherwise ''' if self.end_position is None: return True if np.all(self.end_position == self.start_position): return True return False @property def release_duration(self): ''' duration over which particles are released in seconds ''' if self.end_release_time is None: return 0 else: return (self.end_release_time - self.release_time).total_seconds() @property def end_release_time(self): if self._end_release_time is None: return self.release_time else: return self._end_release_time @end_release_time.setter def end_release_time(self, val): ''' Set end_release_time. If end_release_time is None or if end_release_time == release_time, it is an instantaneous release. Also update reference to set_newparticle_positions - if this was previously an instantaneous release but is now timevarying, we need to update this method ''' val = asdatetime(val) if val is not None and self.release_time > val: raise ValueError('end_release_time must be greater than ' 'release_time') self._end_release_time = val @property def num_per_timestep(self): return self._num_per_timestep @num_per_timestep.setter def num_per_timestep(self, val): ''' Defines fixed number of LEs released per timestep Setter does the following: 1. sets num_per_timestep attribute 2. sets num_elements to None since total elements depends on duration and timestep 3. invokes _reference_to_num_elements_to_release(), which updates the method referenced by num_elements_to_release ''' self._num_per_timestep = val if val is not None or val < 0: self._num_elements = None @Release.num_elements.setter def num_elements(self, val): ''' over ride base class setter. Makes num_per_timestep None since only one can be set at a time ''' if val is None: self._num_elements = val if self._num_per_timestep is None: self._num_per_timestep = 1 elif val < 0: raise ValueError('number of elements cannot be less than 0') else: self._num_elements = val if val is not None: self._num_per_timestep = None @property def start_position(self): return self._start_position @start_position.setter def start_position(self, val): ''' set start_position and also make _delta_pos = None so it gets recomputed when model runs - it should be updated ''' self._start_position = np.array(val, dtype=world_point_type).reshape( (3, )) @property def end_position(self): if self._end_position is None: return self.start_position else: return self._end_position @end_position.setter def end_position(self, val): ''' set end_position and also make _delta_pos = None so it gets recomputed - it should be updated :param val: Set end_position to val. This can be None if release is a point source. ''' if val is not None: val = np.array(val, dtype=world_point_type).reshape((3, )) self._end_position = val def LE_timestep_ratio(self, ts): ''' Returns the ratio ''' if self.num_elements is None and self.num_per_timestep is not None: return self.num_per_timestep return 1.0 * self.num_elements / self.get_num_release_time_steps(ts) def generate_release_timeseries(self, num_ts, max_release, ts): ''' Release timeseries describe release behavior as a function of time. _release_ts describes the number of LEs that should exist at time T _pos_ts describes the spill position at time T All use TimeseriesData objects. ''' t = None if num_ts == 1: #This is a special case, when the release is short enough a single #timestep encompasses the whole thing. if self.release_duration == 0: t = Time([ self.release_time, self.end_release_time + timedelta(seconds=1) ]) else: t = Time([self.release_time, self.end_release_time]) else: t = Time([ self.release_time + timedelta(seconds=ts * step) for step in range(0, num_ts + 1) ]) t.data[-1] = self.end_release_time if self.release_duration == 0: self._release_ts = TimeseriesData(name=self.name + '_release_ts', time=t, data=np.full( t.data.shape, max_release).astype(int)) else: self._release_ts = TimeseriesData(name=self.name + '_release_ts', time=t, data=np.linspace( 0, max_release, num_ts + 1).astype(int)) lon_ts = TimeseriesData(name=self.name + '_lon_ts', time=t, data=np.linspace(self.start_position[0], self.end_position[0], num_ts + 1)) lat_ts = TimeseriesData(name=self.name + '_lat_ts', time=t, data=np.linspace(self.start_position[1], self.end_position[1], num_ts + 1)) z_ts = TimeseriesData(name=self.name + '_z_ts', time=t, data=np.linspace(self.start_position[2], self.end_position[2], num_ts + 1)) self._pos_ts = TimeseriesVector(name=self.name + '_pos_ts', time=t, variables=[lon_ts, lat_ts, z_ts]) def rewind(self): self._prepared = False self._mass_per_le = 0 self._release_ts = None self._pos_ts = None def prepare_for_model_run(self, ts): ''' :param ts: integer seconds :param amount: integer kilograms ''' if self._prepared: self.rewind() if self.LE_timestep_ratio(ts) < 1: raise ValueError('Not enough LEs: Number of LEs must at least \ be equal to the number of timesteps in the release') num_ts = self.get_num_release_time_steps(ts) max_release = 0 if self.num_per_timestep is not None: max_release = self.num_per_timestep * num_ts else: max_release = self.num_elements self.generate_release_timeseries(num_ts, max_release, ts) self._prepared = True self._mass_per_le = self.release_mass * 1.0 / max_release def num_elements_after_time(self, current_time, time_step): ''' Returns the number of elements expected to exist at current_time+time_step. Returns 0 if prepare_for_model_run has not been called. :param ts: integer seconds :param amount: integer kilograms ''' if not self._prepared: return 0 if current_time < self.release_time: return 0 return int( math.ceil( self._release_ts.at(None, current_time + timedelta(seconds=time_step), extrapolate=True))) def initialize_LEs(self, to_rel, data, current_time, time_step): ''' Initializes the mass and position for num_released new LEs. current_time = datetime.datetime time_step = integer seconds ''' if (time_step == 0): time_step = 1 #to deal with initializing position in instantaneous release case sl = slice(-to_rel, None, 1) start_position = self._pos_ts.at(None, current_time, extrapolate=True) end_position = self._pos_ts.at(None, current_time + timedelta(seconds=time_step), extrapolate=True) data['positions'][sl, 0] = \ np.linspace(start_position[0], end_position[0], to_rel) data['positions'][sl, 1] = \ np.linspace(start_position[1], end_position[1], to_rel) data['positions'][sl, 2] = \ np.linspace(start_position[2], end_position[2], to_rel) data['mass'][sl] = self._mass_per_le data['init_mass'][sl] = self._mass_per_le
def get_tsd_instance(self, dates, series_data): return TimeseriesData(time=Time(dates), data=series_data, units='m')