def test_ops_wind(self, m_dfp, wind_dataset, tmpdir): tmp_weather_path = tmpdir.ensure_dir('operational') m_dfp.side_effect = (wind_dataset, ) wind_avg = wind_tools.calc_wind_avg_at_point( arrow.get('2016-02-02 04:25'), str(tmp_weather_path), (0, 0)) np.testing.assert_allclose(wind_avg.u, 2.5) np.testing.assert_allclose(wind_avg.v, -2.5)
def _prep_plot_data(grids_15m, tidal_predictions, weather_path): max_ssh, max_ssh_time, risk_levels = {}, {}, {} u_wind_4h_avg, v_wind_4h_avg, max_wind_avg = {}, {}, {} for name in places.TIDE_GAUGE_SITES: ssh_ts = nc_tools.ssh_timeseries_at_point(grids_15m[name], 0, 0, datetimes=True) ttide = shared.get_tides(name, tidal_predictions) max_ssh[name], max_ssh_time[name] = shared.find_ssh_max(name, ssh_ts, ttide) risk_levels[name] = stormtools.storm_surge_risk_level( name, max_ssh[name], ttide ) wind_avg = wind_tools.calc_wind_avg_at_point( arrow.get(max_ssh_time[name]), weather_path, places.PLACES[name]["wind grid ji"], avg_hrs=-4, ) u_wind_4h_avg[name], v_wind_4h_avg[name] = wind_avg max_wind_avg[name], _ = wind_tools.wind_speed_dir( u_wind_4h_avg[name], v_wind_4h_avg[name] ) plot_data = namedtuple( "PlotData", "ssh_ts, max_ssh, max_ssh_time, risk_levels, " "u_wind_4h_avg, v_wind_4h_avg, max_wind_avg", ) return plot_data( ssh_ts, max_ssh, max_ssh_time, risk_levels, u_wind_4h_avg, v_wind_4h_avg, max_wind_avg, )
def test_prepend_previous_day( self, m_dfp, wind_dataset, tmpdir, ): tmp_weather_path = tmpdir.ensure_dir('operational') wind_prev_day = nc.Dataset('wind_prev_day', 'w') wind_prev_day.createDimension('time_counter') wind_prev_day.createDimension('y', 1) wind_prev_day.createDimension('x', 1) u_wind = wind_prev_day.createVariable('u_wind', float, ('time_counter', 'y', 'x')) u_wind[:] = np.arange(5) v_wind = wind_prev_day.createVariable('v_wind', float, ('time_counter', 'y', 'x')) v_wind[:] = np.arange(0, -5, -1) time_counter = wind_prev_day.createVariable('time_counter', float, ('time_counter', )) time_counter.time_origin = '2016-FEB-01 00:00:00' time_counter[:] = np.arange(19, 24) * 60 * 60 m_dfp.side_effect = (wind_dataset, wind_prev_day) wind_avg = wind_tools.calc_wind_avg_at_point( arrow.get('2016-02-02 01:25'), str(tmp_weather_path), (0, 0)) wind_prev_day.close() os.remove('wind_prev_day') np.testing.assert_allclose(wind_avg.u, 2) np.testing.assert_allclose(wind_avg.v, -2)
def _prep_plot_data( place, grid_T_hr, grids_15m, bathy, timezone, weather_path, tidal_predictions, ): ssh_hr = grid_T_hr.variables['sossheig'] time_ssh_hr = nc_tools.timestamp( grid_T_hr, range(grid_T_hr.variables['time_counter'].size)) try: j, i = places.PLACES[place]['NEMO grid ji'] except KeyError as e: raise KeyError(f'place name or info key not found in ' f'salishsea_tools.places.PLACES: {e}') itime_max_ssh = np.argmax(ssh_hr[:, j, i]) time_max_ssh_hr = time_ssh_hr[itime_max_ssh] ssh_15m_ts = nc_tools.ssh_timeseries_at_point(grids_15m[place], 0, 0, datetimes=True) ttide = shared.get_tides(place, tidal_predictions) ssh_corr = shared.correct_model_ssh(ssh_15m_ts.ssh, ssh_15m_ts.time, ttide) max_ssh_15m, time_max_ssh_15m = shared.find_ssh_max( place, ssh_15m_ts, ttide) tides_15m = shared.interp_to_model_time(ssh_15m_ts.time, ttide.pred_all, ttide.time) residual = ssh_corr - tides_15m max_ssh_residual = residual[ssh_15m_ts.time == time_max_ssh_15m][0] wind_4h_avg = wind_tools.calc_wind_avg_at_point( arrow.get(time_max_ssh_15m), weather_path, places.PLACES[place]['wind grid ji'], avg_hrs=-4) wind_4h_avg = wind_tools.wind_speed_dir(*wind_4h_avg) plot_data = namedtuple( 'PlotData', 'ssh_max_field, time_max_ssh_hr, ssh_15m_ts, ssh_corr, ' 'max_ssh_15m, time_max_ssh_15m, residual, max_ssh_residual, ' 'wind_4h_avg, ' 'ttide, bathy') return plot_data( ssh_max_field=ssh_hr[itime_max_ssh], time_max_ssh_hr=time_max_ssh_hr.to(timezone), ssh_15m_ts=ssh_15m_ts, ssh_corr=ssh_corr, max_ssh_15m=max_ssh_15m - places.PLACES[place]['mean sea lvl'], time_max_ssh_15m=arrow.get(time_max_ssh_15m).to(timezone), residual=residual, max_ssh_residual=max_ssh_residual, wind_4h_avg=wind_4h_avg, ttide=ttide, bathy=bathy, )
def _calc_wind_4h_avg(feed, max_ssh_time, config): weather_path = config["weather"]["ops dir"] tide_gauge_stn = config["storm surge feeds"]["feeds"][feed]["tide gauge stn"] wind_avg = wind_tools.calc_wind_avg_at_point( arrow.get(max_ssh_time), weather_path, PLACES[tide_gauge_stn]["wind grid ji"], avg_hrs=-4, ) wind_vector = wind_tools.wind_speed_dir(wind_avg.u, wind_avg.v) return { "wind_speed_4h_avg": np.asscalar(wind_vector.speed), "wind_dir_4h_avg": np.asscalar(wind_vector.dir), }
def _calc_wind_4h_avg(feed, max_ssh_time, config): weather_path = config['weather']['ops dir'] tide_gauge_stn = ( config['storm surge feeds']['feeds'][feed]['tide gauge stn']) wind_avg = wind_tools.calc_wind_avg_at_point( arrow.get(max_ssh_time), weather_path, PLACES[tide_gauge_stn]['wind grid ji'], avg_hrs=-4) wind_vector = wind_tools.wind_speed_dir(wind_avg.u, wind_avg.v) return { 'wind_speed_4h_avg': np.asscalar(wind_vector.speed), 'wind_dir_4h_avg': np.asscalar(wind_vector.dir), }
def _prep_plot_data( place, ssh_fcst_dataset_url_tmpl, tidal_predictions, forecast_hrs, weather_path, bathy, grid_T_hr_path, ): # NEMO sea surface height forecast dataset ssh_forecast = _get_ssh_forecast(place, ssh_fcst_dataset_url_tmpl) # CHS water level observations dataset try: obs_1min = (data_tools.get_chs_tides( "obs", place, arrow.get(str(ssh_forecast.time.values[0])) - timedelta(seconds=5 * 60), arrow.get(str(ssh_forecast.time.values[-1])), ).to_xarray().rename({"index": "time"})) obs_10min_avg = obs_1min.resample(time="10min", loffset="5min").mean() obs = obs_10min_avg.to_dataset(name="water_level") except (AttributeError, KeyError): # No observations available obs = None shared.localize_time(ssh_forecast) try: shared.localize_time(obs) except (IndexError, AttributeError): # No observations available obs = None model_ssh_period = slice(str(ssh_forecast.time.values[0]), str(ssh_forecast.time.values[-1])) forecast_period = slice( str(ssh_forecast.time.values[-forecast_hrs * 6]), str(ssh_forecast.time.values[-1]), ) try: obs_period = slice(str(obs.time.values[0]), str(obs.time.values[-1])) except AttributeError: # No observations available obs_period = None # Predicted tide water levels dataset from ttide ttide = shared.get_tides(place, tidal_predictions) ttide.rename(columns={" pred_noshallow ": "pred_noshallow"}, inplace=True) ttide.index = pandas.to_datetime(ttide.time.values, format="%Y-%m-%d %H:%M:%S") ttide_ds = ttide.to_xarray().drop_vars(["time"]).rename({"index": "time"}) # Localize ttide dataset timezone to ssh_forecast times because ttide # extends well beyond ends of ssh_forecast period shared.localize_time(ttide_ds, local_datetime=arrow.get( str(ssh_forecast.time.values[0])).to("local")) # NEMO sea surface height dataset corrected to include unmodeled tide constituents ssh_correction = ttide_ds.pred_noshallow.sel( time=model_ssh_period) - ttide_ds.pred_8.sel(time=model_ssh_period) ssh_corrected = ssh_forecast + ssh_correction # Mean sea level and extreme water levels msl = PLACES[place]["mean sea lvl"] extreme_ssh = PLACES[place]["hist max sea lvl"] max_tides = ttide.pred_all.max() + msl mid_tides = 0.5 * (extreme_ssh - max_tides) + max_tides thresholds = (max_tides, mid_tides, extreme_ssh) max_ssh = ssh_corrected.ssh.sel(time=forecast_period) max_ssh = max_ssh.where(max_ssh == max_ssh.max(), drop=True).squeeze() # Residual differences between corrected model and observations and predicted tides model_residual = ssh_corrected - ttide_ds.pred_all.sel( time=model_ssh_period) model_residual.attrs["tz_name"] = ssh_forecast.attrs["tz_name"] max_model_residual = model_residual.max() try: obs_residual = obs - ttide_ds.pred_all.sel(time=obs_period) - msl obs_residual.attrs["tz_name"] = obs.attrs["tz_name"] except KeyError: # No observations available obs_residual = None # Wind at NEmo model time of max sea surface height wind_4h_avg = wind_tools.calc_wind_avg_at_point( arrow.get(str(max_ssh.time.values)), weather_path, PLACES[place]["wind grid ji"], avg_hrs=-4, ) wind_4h_avg = wind_tools.wind_speed_dir(*wind_4h_avg) # Model sea surface height field for contour map tracers_ds = xarray.open_dataset(grid_T_hr_path) max_ssh_time_utc = (arrow.get(str(max_ssh.time.values)).replace( tzinfo=ssh_forecast.attrs["tz_name"]).to("utc")) return SimpleNamespace( ssh_forecast=ssh_forecast, obs=obs, ttide=ttide_ds, ssh_corrected=ssh_corrected, msl=msl, thresholds=thresholds, max_ssh=max_ssh, model_residual=model_residual, max_model_residual=max_model_residual, obs_residual=obs_residual, wind_4h_avg=wind_4h_avg, bathy=bathy, max_ssh_field=tracers_ds.sossheig.sel( time_counter=max_ssh_time_utc.naive, method="nearest"), )