baynes_sound_agrif
Figure Module¶Development of functions for nowcast.figures.research.baynes_sound_agrif
web site figure module.
Goal: A 4 panel figures showing surface values of temperature, salinity, diatoms biomass, and nitrate concentration at 12:30 Pacific time. Each panel to show all of the Baynes Sound sub-grid as well as a fringe of the full domain on the 3 non-land sides. Ideally the axes tick labels will be lon/lat with angled grid lines.
from pathlib import Path
from types import SimpleNamespace
import arrow
import cmocean
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy
from salishsea_tools import viz_tools
import xarray
from nowcast.figures import shared
import nowcast.figures.website_theme
%matplotlib inline
_prep_plot_data()
Function¶def _prep_plot_data(sstracers_path, bs_phys_path, bs_bio_path, run_date, ss_grid_url, bs_grid_path):
SS_BAYNES_SOUND_X, SS_BAYNES_SOUND_Y = slice(112, 166), slice(550, 699)
ss_grid = xarray.open_dataset(ss_grid_url, mask_and_scale=False) \
.sel(gridX=SS_BAYNES_SOUND_X, gridY=SS_BAYNES_SOUND_Y)
ss_water_mask = (ss_grid.bathymetry!=0).values
bs_grid = xarray.open_dataset(bs_grid_path, mask_and_scale=False)
bs_water_mask = (bs_grid.Bathymetry!=0).values
ss_tracers = xarray.open_dataset(ss_tracers_path)
shared.localize_time(ss_tracers, time_coord='time_counter')
bs_phys = xarray.open_dataset(bs_phys_path)
bs_bio = xarray.open_dataset(bs_bio_path)
for dataset in (bs_phys, bs_bio):
shared.localize_time(dataset, time_coord='time_counter')
ss_temperature = _get_data_array(ss_tracers.votemper, ss_water_mask, run_date)
bs_temperature = _get_data_array(bs_phys.votemper, bs_water_mask, run_date)
bs_temperature.attrs['long_name'] = 'Conservative Temperature'
ss_salinity = _get_data_array(ss_tracers.vosaline, ss_water_mask, run_date)
bs_salinity = _get_data_array(bs_phys.vosaline, bs_water_mask, run_date)
bs_salinity.attrs['long_name'] = 'Reference Salinity'
ss_diatoms = _get_data_array(ss_tracers.diatoms, ss_water_mask, run_date)
bs_diatoms = _get_data_array(bs_bio.diatoms, bs_water_mask, run_date)
ss_nitrate = _get_data_array(ss_tracers.nitrate, ss_water_mask, run_date)
bs_nitrate = _get_data_array(bs_bio.nitrate, bs_water_mask, run_date)
return SimpleNamespace(
ss_temperature=ss_temperature,
bs_temperature=bs_temperature,
ss_salinity=ss_salinity,
bs_salinity=bs_salinity,
ss_diatoms=ss_diatoms,
bs_diatoms=bs_diatoms,
ss_nitrate=ss_nitrate,
bs_nitrate=bs_nitrate,
run_date=run_date,
tz_name=bs_phys.attrs['tz_name'],
ss_grid=ss_grid,
bs_grid=bs_grid,
)
def _get_data_array(ds_var, water_mask, run_date):
try:
return ds_var \
.isel(nearsurface_T=0) \
.sel(time_counter=run_date.format('YYYY-MM-DD 12:30')) \
.where(water_mask)
except ValueError:
return ds_var \
.isel(deptht=0) \
.sel(time_counter=run_date.format('YYYY-MM-DD 12:30')) \
.where(water_mask)
_prep_fig_axes()
Function¶def _prep_fig_axes(figsize, plot_data, theme):
fig, axs = plt.subplots(
1, 4, figsize=figsize, facecolor=theme.COLOURS['figure']['facecolor']
)
map_params = SimpleNamespace(
ll_lon=-124.68, ur_lon=-124.86,
ll_lat=49.25, ur_lat=49.925,
lon_0_offset=37.9,
meridians=numpy.arange(-125.1, -124.3, 0.1),
parallels=numpy.arange(49.2, 50, 0.1),
)
central_lon = (
(map_params.ur_lon - map_params.ll_lon)/2 + map_params.ll_lon
+ map_params.lon_0_offset
)
central_lat = (
(map_params.ur_lat - map_params.ll_lat)/2 + map_params.ll_lat
)
for ax in axs:
m = Basemap(
ax=ax, projection='lcc',
lon_0=central_lon, lat_0=central_lat,
llcrnrlon=map_params.ll_lon, urcrnrlon=map_params.ur_lon,
llcrnrlat=map_params.ll_lat, urcrnrlat=map_params.ur_lat,
)
# lon/lat grid
m.drawmeridians(
map_params.meridians, labels=(False, False, False, True),
textcolor=theme.COLOURS['text']['axis'],
fontproperties=theme.FONTS['axis small'],
)
m.drawparallels(
map_params.parallels, labels=(True, False, False, False),
textcolor=theme.COLOURS['text']['axis'],
fontproperties=theme.FONTS['axis small'],)
ss_x, ss_y = m(plot_data.ss_grid.longitude.values, plot_data.ss_grid.latitude.values)
bs_x, bs_y = m(plot_data.bs_grid.nav_lon.values, plot_data.bs_grid.nav_lat.values)
grids = SimpleNamespace(ss_x=ss_x, ss_y=ss_y, bs_x=bs_x, bs_y=bs_y)
return fig, axs, grids
_plot_surface_fields()
Function¶def _plot_surface_fields(axs, plot_data, grids, theme):
vars = [
(plot_data.ss_temperature, plot_data.bs_temperature, cmocean.cm.thermal),
(plot_data.ss_salinity, plot_data.bs_salinity, cmocean.cm.haline),
(plot_data.ss_diatoms, plot_data.bs_diatoms, cmocean.cm.algae),
(plot_data.ss_nitrate, plot_data.bs_nitrate, cmocean.cm.matter),
]
for i, (ss_var, bs_var, cmap) in enumerate(vars):
_plot_surface_field(axs[i], ss_var, bs_var, cmap, grids, plot_data.ss_grid.bathymetry, plot_data.bs_grid.Bathymetry, theme)
def _plot_surface_field(ax, ss_var, bs_var, cmap, grids, ss_bathy, bs_bathy, theme):
cmap = plt.get_cmap(cmap)
clevels = numpy.linspace(
numpy.floor(bs_var.where(bs_var>0).min()),
numpy.ceil(bs_var.where(bs_var>0).max()),
20
)
contour_set = ax.contourf(grids.ss_x, grids.ss_y, ss_var, cmap=cmap, levels=clevels, extend='max')
contour_set = ax.contourf(grids.bs_x, grids.bs_y, bs_var, cmap=cmap, levels=clevels, extend='max')
cbar = plt.colorbar(contour_set, ax=ax)
cbar.ax.axes.tick_params(labelcolor=theme.COLOURS['cbar']['tick labels'])
cbar.set_label(
f'{bs_var.attrs["long_name"]} [{bs_var.attrs["units"]}]',
color=theme.COLOURS['text']['axis'],
fontproperties=theme.FONTS['axis'],
)
isobath = ax.contour(
grids.bs_x, grids.bs_y, bs_bathy, (25,),
colors=theme.COLOURS['contour lines']['Baynes Sound entrance'],
)
plt.clabel(isobath, fmt={isobath.levels[0]: f'{isobath.levels[0]:.0f} m'})
ax.set_axis_bgcolor(theme.COLOURS['dark land'])
theme.set_axis_colors(ax)
make_figure()
Function¶This is is the function that will be called by the nowcast.workers.make_plots
worker to return a matplotlib.figure.Figure
object.
def make_figure(
ss_tracers_path, bs_phys_path, bs_bio_path,
run_date,
ss_grid_url, bs_grid_path,
figsize=(22, 9),
theme=nowcast.figures.website_theme):
plot_data = _prep_plot_data(ss_tracers_path, bs_phys_path, bs_bio_path, run_date, ss_grid_url, bs_grid_path)
fig, axs, grids = _prep_fig_axes(figsize, plot_data, theme)
_plot_surface_fields(axs, plot_data, grids, theme)
time = plot_data.bs_temperature.time_counter
year = numpy.asscalar(time.dt.year.values)
month = numpy.asscalar(time.dt.month.values)
day = numpy.asscalar(time.dt.day.values)
hour = numpy.asscalar(time.dt.hour.values)
minute = numpy.asscalar(time.dt.minute.values)
fig.suptitle(
f'{year}-{month:02d}-{day:02d} {hour:02d}:{minute:02d} {plot_data.tz_name}',
color=theme.COLOURS['text']['figure title'],
fontproperties=theme.FONTS['figure title'],
fontsize=theme.FONTS['figure title'].get_size(),
)
return fig
The %%timeit
cell magic lets us keep an eye on how log the figure takes to process.
Setting -n1 -r1
prevents it from processing the figure more than once
as it might try to do to generate better statistics.
#%%timeit -n1 -r1
from importlib import reload
from nowcast.figures import website_theme
reload(website_theme)
agrif_results_archive = Path('/results/SalishSea/nowcast-agrif/')
run_date = arrow.get('2018-09-03')
ddmmmyy = run_date.format('DDMMMYY').lower()
yyyymmdd = run_date.format('YYYYMMDD')
ss_tracers_path = agrif_results_archive/ddmmmyy/'BaynesSoundSurface_grid_T.nc'
bs_phys_path = agrif_results_archive/ddmmmyy/f'1_SalishSea_1h_{yyyymmdd}_{yyyymmdd}_grid_T.nc'
bs_bio_path = agrif_results_archive/ddmmmyy/f'1_SalishSea_1h_{yyyymmdd}_{yyyymmdd}_ptrc_T.nc'
ss_grid_url = 'https://salishsea.eos.ubc.ca/erddap/griddap/ubcSSnBathymetryV17-02'
bs_grid_path = Path('/media/doug/warehouse/MEOPAR/grid/subgrids/BaynesSound/bathymetry_201702_BS.nc')
fig = make_figure(ss_tracers_path, bs_phys_path, bs_bio_path, run_date, ss_grid_url, bs_grid_path)
/media/doug/warehouse/conda_envs/nowcast-fig-dev/lib/python3.6/site-packages/numpy/ma/core.py:6385: MaskedArrayFutureWarning: In the future the default for ma.minimum.reduce will be axis=0, not the current None, to match np.minimum.reduce. Explicitly pass 0 or None to silence this warning. return self.reduce(a) /media/doug/warehouse/conda_envs/nowcast-fig-dev/lib/python3.6/site-packages/numpy/ma/core.py:6385: MaskedArrayFutureWarning: In the future the default for ma.maximum.reduce will be axis=0, not the current None, to match np.maximum.reduce. Explicitly pass 0 or None to silence this warning. return self.reduce(a)