def _calc_results_time_series( tracer, model_time, node_depth, timezone, tracer_depths, tracer_mask, w_depths, psu_to_teos=False, ): time_series = namedtuple("TimeSeries", "var, time") if psu_to_teos: var = teos_tools.psu_teos( [ shared.interpolate_tracer_to_depths( tracer[i, :], tracer_depths, node_depth, tracer_mask, w_depths ) for i in range(tracer.shape[0]) ] ) else: var = [ shared.interpolate_tracer_to_depths( tracer[i, :], tracer_depths, node_depth, tracer_mask, w_depths ) for i in range(tracer.shape[0]) ] return time_series(var=var, time=[t.to(timezone) for t in model_time])
def nemo_sal_route(grid_T_hr, bathy, route_name, obs_sal): """Get the salanity data form the NEMO run that matches the time and locations of the ferry route by integrated distance weighted interpolation. :arg grid_T_hr: Hourly tracer results dataset from NEMO. :type grid_T_hr: :class:`netCDF4.Dataset :arg bathy: model bathymetry :type bathy: numpy array :arg route_name: name of a ferre route. HBDB, TWDP or TWSB. :type route_name: string :arg obs_sal: ferry data during route :type obs_sal: numpy array :return: model salinity array along the ferry route for two times """ # Get the salinity data sal_a, sal_b = _get_nemo_salinity(route_name, grid_T_hr) sal_a_route = np.zeros(obs_sal.shape[1]) sal_b_route = np.zeros(obs_sal.shape[1]) # Perform the IDW on each data point and put them into an array for # the whole route. for i in np.arange(obs_sal.shape[1]): sal_a_route[i], sal_b_route[i] = _model_IDW(obs_sal[:, i], bathy, grid_T_hr, sal_a, sal_b) # Convert to TEOS-10 sal_a_t = teos_tools.psu_teos(sal_a_route) sal_b_t = teos_tools.psu_teos(sal_b_route) return sal_a_t, sal_b_t
def _prep_plot_data(grid_T_hr): si, ei = 200, 610 sj, ej = 20, 370 lons = grid_T_hr.variables["nav_lon"][si:ei, sj:ej] lats = grid_T_hr.variables["nav_lat"][si:ei, sj:ej] model_depth_level = 1 # 1.5 m ## TODO: model time step for salinity contour map should be calculated from ## ferry route time model_time_step = 3 # 02:30 UTC sal_hr = grid_T_hr.variables["vosaline"] ## TODO: Use mesh mask instead of 0 for masking sal_masked = np.ma.masked_values( sal_hr[model_time_step, model_depth_level, si:ei, sj:ej], 0) timestamped_sal = namedtuple("timestamped_sal", "salinity, timestamp") sal_model = timestamped_sal(teos_tools.psu_teos(sal_masked), nc_tools.timestamp(grid_T_hr, model_time_step)) return lons, lats, sal_model, None
def ferry_salinity(ferry_data_dir, route_name, dmy, step=1): """Load ferry data and slice it to contain only the during route values. :arg str ferry_data_dir: storage file location for ONC ferry data. :arg str route_name: name of a ferre route. HBDB, TWDP or TWSB. :arg str dmy: today's date in :kbd:`ddmmmyy` format :arg int step: selecting every nth data point :returns: matrix containing time, lon, lat and salinity of ferry observations """ # Load observation ferry salinity data with locations and time date = datetime.datetime.strptime(dmy, "%d%b%y") dayf = date - datetime.timedelta(days=1) dmyf = dayf.strftime("%d%b%y").lower() obs = _get_sal_data(ferry_data_dir, route_name, dmyf) # Create datetime object for start and end of route times date = datetime.datetime.strptime(dmy, "%d%b%y") start_time = date.replace( hour=FERRY_ROUTES[route_name]["start"]["hour"], minute=FERRY_ROUTES[route_name]["start"]["minute"], ) end_time = date.replace( hour=FERRY_ROUTES[route_name]["end"]["hour"], minute=FERRY_ROUTES[route_name]["end"]["minute"], ) # Slice the observational arrays to only have "during route" data time_obs = datenum2datetime(obs[0]) df = pd.DataFrame(time_obs) j = np.logical_and(df >= start_time, df <= end_time) j = np.array(j) obs_route = obs[0:4, j] # High frequency ferry data, take every 20th value obs_slice = obs_route[:, 0:-1:step] # Convert to TEOS-10 obs_slice[3] = teos_tools.psu_teos(obs_slice[3]) return obs_slice
def onc_json_to_dataset(onc_json, teos=True): """Return an :py:class:`xarray.Dataset` object containing the data and metadata obtained from an Ocean Networks Canada (ONC) data web service API request. :arg dict onc_json: Data structure returned from an ONC data web service API request. Typically produces by calling the :py:meth:`json` method on the :py:class:`~requests.Response` object produced by calling :py:meth:`requests.get`. :arg boolean teos: Convert salinity data from PSU (Practical Salinity Units) to TEOS-10 reference salinity in g/kg. Defaults to :py:obj:`True`. :returns: Data structure containing data and metadata :rtype: :py:class:`xarray.Dataset` """ data_vars = {} for sensor in onc_json['sensorData']: if sensor['sensorName'] == 'Practical Salinity' and teos: data = teos_tools.psu_teos([d['value'] for d in sensor['data']]) sensor['sensorName'] = 'Reference Salinity' sensor['unitOfMeasure'] = 'g/kg' else: data = [d['value'] for d in sensor['data']] data_vars[sensor['sensor']] = xarray.DataArray( name=sensor['sensor'], data=data, coords={ 'sampleTime': [arrow.get(d['sampleTime']).naive for d in sensor['data']], }, dims=('sampleTime', ), attrs={ 'qaqcFlag': np.array([d['qaqcFlag'] for d in sensor['data']]), 'sensorName': sensor['sensorName'], 'unitOfMeasure': sensor['unitOfMeasure'], 'actualSamples': sensor['actualSamples'], }) return xarray.Dataset(data_vars, attrs=onc_json['serviceMetadata'])
def salinity_ferry_route( ferry_data_dir, grid_T_hr, bathy, route_name, dmy, figsize=(20, 7.5), ): """Plot daily salinity comparisons between ferry observations and model results as well as ferry route with model salinity distribution. :arg str ferry_data_dir: storage file location for ONC ferry data. :arg grid_T_hr: Hourly tracer results dataset from NEMO. :type grid_T_hr: :class:`netCDF4.Dataset :arg bathy: model bathymetry :type bathy: numpy array :arg str route_name: route name of these three ferry routes respectively :arg str dmy: date in form ddmonyy :arg 2-tuple figsize: Figure size (width, height) in inches. :returns: matplotlib figure object instance (fig). """ # Grid region to plot si, ei = 200, 610 sj, ej = 20, 370 lons = grid_T_hr.variables['nav_lon'][si:ei, sj:ej] lats = grid_T_hr.variables['nav_lat'][si:ei, sj:ej] # Salinity calculated by NEMO and observed by ONC ferry package model_depth_level = 1 # 1.5 m ## TODO: model time step for salinity contour map should be calculated from ## ferry route time model_time_step = 3 # 02:30 UTC sal_hr = grid_T_hr.variables['vosaline'] ## TODO: Use mesh mask instead of 0 for masking sal_masked = np.ma.masked_values( sal_hr[model_time_step, model_depth_level, si:ei, sj:ej], 0) sal_t = teos_tools.psu_teos(sal_masked) sal_obs = ferry_salinity(ferry_data_dir, route_name, dmy) nemo_a, nemo_b = nemo_sal_route(grid_T_hr, bathy, route_name, sal_obs) fig, axs = plt.subplots(1, 2, figsize=figsize) axs[1].set_axis_bgcolor("burlywood") viz_tools.set_aspect(axs[1], coords='map', lats=lats) cmap = plt.get_cmap('plasma') axs[1].set_xlim(-124.5, -122.5) axs[1].set_ylim(48.3, 49.6) # Plot model salinity mesh = axs[1].contourf(lons, lats, sal_t, 20, cmap=cmap) cbar = plt.colorbar(mesh, ax=axs[1]) cbar.ax.axes.tick_params(labelcolor='w') cbar.set_label('Absolute Salinity [g/kg]', color='white', **axis_font) axs[1].set_title('Ferry Route: 3am[UTC] 1.5m model result ', **title_font) axs[1].set_xlabel('Longitude [°E]', **axis_font) axs[1].set_ylabel('Latitude [°N]', **axis_font) # Plot ferry route. axs[1].plot(sal_obs[1], sal_obs[2], 'black', linewidth=4) figures.axis_colors(axs[1], 'grey') # Add locations and markers on plot for orientation bbox_args = dict(boxstyle='square', facecolor='white', alpha=0.7) places = [ FERRY_ROUTES[route_name]['start']['terminal'], FERRY_ROUTES[route_name]['end']['terminal'], 'Vancouver' ] label_offsets = [0.04, -0.4, 0.09] for stn, loc in zip(places, label_offsets): axs[1].plot(*PLACES[stn]['lon lat'], marker='D', color='white', markersize=10, markeredgewidth=2) axs[1].annotate( stn, (PLACES[stn]['lon lat'][0] + loc, PLACES[stn]['lon lat'][1]), fontsize=15, color='black', bbox=bbox_args) # Set up model part of salinity comparison plot axs[0].plot(sal_obs[1], nemo_a, 'DodgerBlue', linewidth=2, label=f'{FERRY_ROUTES[route_name]["start"]["hour"]} am [UTC]') axs[0].plot( sal_obs[1], nemo_b, 'MediumBlue', linewidth=2, label=f'{FERRY_ROUTES[route_name]["start"]["hour"]+1} am [UTC]') # Observational component of salinity comparisons plot axs[0].plot(sal_obs[1], sal_obs[3], 'DarkGreen', linewidth=2, label="Observed") axs[0].text(0.25, -0.1, 'Observations from Ocean Networks Canada', transform=axs[0].transAxes, color='white') axs[0].set_xlim(-124, -123) axs[0].set_ylim(10, 32) axs[0].set_title('Surface Salinity: ' + dmy, **title_font) axs[0].set_xlabel('Longitude', **axis_font) axs[0].set_ylabel('Absolute Salinity [g/kg]', **axis_font) axs[0].legend(loc=3) axs[0].grid(axis='both') fig.patch.set_facecolor('#2B3E50') figures.axis_colors(axs[0], 'grey') return fig
def test_psu_teos_polymorphic_sequence(psu, expected): teos = teos_tools.psu_teos(psu) np.testing.assert_allclose(teos, expected)
def test_psu_teos(psu, expected): np.testing.assert_allclose(teos_tools.psu_teos(psu), expected)