import numpy as np
from functools import partial
import pandas as pd
import netCDF4
from scipy.interpolate import interp1d
import holoviews as hv
from holoviews import opts
from holoviews.streams import Stream, param
from holoviews.plotting.links import RangeToolLink
import panel as pn
from bokeh.models.formatters import PrintfTickFormatter
from bokeh.models.renderers import GlyphRenderer
from bokeh.models import Range1d, LinearAxis
from bokeh.models import HoverTool
css = '''
app {
display: flex;
flex-direction: column;
height: 97vh;
}
menu_and_figures {
flex: 1;
}
'''
pn.extension('katex', raw_css=[css], loading_color='#000000')
hv.extension('bokeh', width=100)
from oggm.core.massbalance import MassBalanceModel, PastMassBalance, ConstantMassBalance
from oggm.utils import ncDataset, date_to_floatyear, floatyear_to_date
from oggm import cfg
from oggm.cfg import SEC_IN_YEAR, SEC_IN_MONTH
cfg.initialize_minimal()
from app_template import template
from version import __version__
analytics = pn.pane.HTML('<script defer data-domain="bokeh.oggm.org" src="https://plausible.oggm.org/js/script.js"></script>')
cfg.PARAMS['use_bias_for_run'] = False
climate_data_dir = 'climate_data/'
# set default language
language = 'en'
# help variable for setting a new base climate (needed for different languages)
climate_meta_data = ['_HEF', 1300, 4600]
# variable to keep track which figures are already updated (for each tab)
tab_figures_updated = [True, True, True]
climographs_updated = [True, True, True]
active_tab = 0
Help functions to set new language
def language_selector_function(arg=None):
global language
language = language_selector.value
change_language()
def change_language():
toogle_figures_loading()
set_climate_tab_language()
set_mb_months_language()
# change base climate name language for mb models (use filesuffix)
for i, climate_meta_data in enumerate(climate_tab_text['base_climate_meta_data']):
file_suffix = climate_meta_data[0]
if file_suffix == mb_models[0].input_filesuffix:
mb_models[0].base_climate_name = climate_tab_text['base_climate_names'][language][i]
if file_suffix == mb_models[1].input_filesuffix:
mb_models[1].base_climate_name = climate_tab_text['base_climate_names'][language][i]
set_mb_settings_tab_language()
# change headings in menu
set_tab_menu_tabs()
set_change_selected_MB_RadioButtonGroup_language()
# change headings for figures
set_figures_tab_tabs()
set_period_buttons_language()
set_select_months_language()
# update figures
tab_figures_updated = [False, False, False]
climographs_updated = [False, False, False]
update_current_figures()
toogle_figures_loading()
Actual Language Selector
from app_text import supported_languages
language_selector = pn.widgets.RadioBoxGroup(name='select your language',
options=supported_languages,
inline=True,
margin=(0, 0),
width=70,
align='center',
height=15,
)
language_selector.param.watch(language_selector_function, 'value');
# define background color of tab menu
menu_background = '#f4f4f4'
# define width for menu-tabs
panel_width = 280
# define width for whole menu
tab_menu_width = 400
# needed for some initialisations for start of app
initial_run = True
current_mb_i = 0
# Import Text for Climate Tab
from app_text import climate_tab_text
# Function to change language of Climate Tab
def set_climate_tab_language():
base_climate_select.name = climate_tab_text['base_climate_select_name'][language]
base_climate_select.options = climate_tab_text['base_climate_names'][language]
y_start_slider.name = climate_tab_text['y_start_slider_name'][language]
y_end_slider.name = climate_tab_text['y_end_slider_name'][language]
set_climate_button.name = climate_tab_text['set_climate_button_name'][language]
base_climate_select = pn.widgets.Select()
y_end_slider = pn.widgets.IntSlider(start=1902,
end=2020,
step=1,
value=2020)
def change_y_end_slider_start(event):
if y_start_slider.value > y_end_slider.value:
y_end_slider.value = y_start_slider.value + 1
if y_start_slider.value == 2019:
y_end_slider.disabled = True
else:
y_end_slider.disabled = False
y_end_slider.start = y_start_slider.value + 1
y_start_slider = pn.widgets.IntSlider(start=1902,
end=2019,
step=1,
value=1990)
y_start_slider.param.watch(change_y_end_slider_start, ['value_throttled'])
change_y_end_slider_start(None)
def set_climate_button_click(event):
global climate_meta_data
set_default_mb_settings()
# set file suffix for current selection
for i, base_name in enumerate(climate_tab_text['base_climate_names'][language]):
if base_name == base_climate_select.value:
climate_meta_data = climate_tab_text['base_climate_meta_data'][i]
set_climate_of_mb_model(current_mb_i)
tab_figures_updated[current_mb_i] = False
tab_figures_updated[2] = False
climographs_updated[current_mb_i] = False
climographs_updated[2] = False
update_current_figures()
set_climate_button = pn.widgets.Button(sizing_mode='stretch_width')
set_climate_button.on_click(set_climate_button_click)
set_climate_tab_language()
climate_tab = pn.Column(base_climate_select,
y_start_slider,
y_end_slider,
set_climate_button,
background=menu_background,
width=panel_width,
sizing_mode='stretch_height')
from app_text import mb_settings_tab_text
# Function to change language of MB Settings Tab
def set_mb_settings_tab_language():
mu_slider.name = mb_settings_tab_text['mu_slider_name'][language]
temp_bias_slider.name = mb_settings_tab_text['temp_bias_slider_name'][language]
temp_grad_slider.name = mb_settings_tab_text['temp_grad_slider_name'][language]
prcp_fac_slider.name = mb_settings_tab_text['prcp_fac_slider_name'][language]
temp_melt_slider.name = mb_settings_tab_text['temp_melt_slider_name'][language]
temp_solid_slider.name = mb_settings_tab_text['temp_solid_slider_name'][language]
temp_liquid_slider.name = mb_settings_tab_text['temp_liquid_slider_name'][language]
set_mb_settings_button.name = mb_settings_tab_text['set_mb_settings_button_name'][language]
# MB Settings
default_mu = 200
min_mu = 100
max_mu = 300
mu_unit = 'kg m⁻² °C⁻¹ mth⁻¹'
mu_slider = pn.widgets.IntSlider(start=min_mu,
end=max_mu,
step=10,
value=default_mu)
mu_slider.format = PrintfTickFormatter(format='%d ' + mu_unit)
default_temp_bias = 0
temp_bias_slider = pn.widgets.FloatSlider(start=-5,
end=5,
step=0.1,
value=default_temp_bias)
temp_bias_slider.format = PrintfTickFormatter(format='%.1f °C')
temp_grad_slider = pn.widgets.FloatSlider(start=cfg.PARAMS['temp_local_gradient_bounds'][0] * 100,
end=cfg.PARAMS['temp_local_gradient_bounds'][1] * 100,
step=0.01,
value=cfg.PARAMS['temp_default_gradient'] * 100)
temp_grad_slider.format = PrintfTickFormatter(format='%.2f °C/100m')
prcp_fac_slider = pn.widgets.FloatSlider(start=0.5,
end=5,
step=0.1,
value=cfg.PARAMS['prcp_scaling_factor'])
temp_melt_slider = pn.widgets.FloatSlider(start=-2,
end=0,
step=0.1,
value=cfg.PARAMS['temp_melt'])
temp_melt_slider.format = PrintfTickFormatter(format='%.1f °C')
temp_solid_slider = pn.widgets.FloatSlider(start=-0.5,
end=0.5,
step=0.1,
value=cfg.PARAMS['temp_all_solid'])
temp_solid_slider.format = PrintfTickFormatter(format='%.1f °C')
temp_liquid_slider = pn.widgets.FloatSlider(start=0.5,
end=3,
step=0.1,
value=cfg.PARAMS['temp_all_liq'])
temp_liquid_slider.format = PrintfTickFormatter(format='%.1f °C')
def set_mb_settings_click(event):
set_mb_settings_of_mb_model(current_mb_i)
tab_figures_updated[current_mb_i] = False
tab_figures_updated[2] = False
update_current_figures()
def set_default_mb_settings():
mu_slider.value = default_mu
temp_bias_slider.value = default_temp_bias
temp_grad_slider.value = cfg.PARAMS['temp_default_gradient'] * 100
prcp_fac_slider.value = cfg.PARAMS['prcp_scaling_factor']
temp_melt_slider.value = cfg.PARAMS['temp_melt']
temp_solid_slider.value = cfg.PARAMS['temp_all_solid']
temp_liquid_slider.value = cfg.PARAMS['temp_all_liq']
set_mb_settings_button = pn.widgets.Button(name='Set MB Settings',
sizing_mode='stretch_width')
set_mb_settings_button.on_click(set_mb_settings_click)
set_mb_settings_tab_language()
MB_settings_tab = pn.Column(mu_slider,
temp_bias_slider,
temp_grad_slider,
prcp_fac_slider,
temp_melt_slider,
temp_solid_slider,
temp_liquid_slider,
set_mb_settings_button,
width=panel_width,
sizing_mode='stretch_height')
tab_menu = pn.Tabs(('', []),
height=420,
width=tab_menu_width,
background=menu_background,
tabs_location='left')
# is necassary to change the tabs title language
def set_tab_menu_tabs():
tab_menu.clear()
tab_menu.append((climate_tab_text['heading'][language],
climate_tab))
tab_menu.append((mb_settings_tab_text['heading'][language],
MB_settings_tab))
set_tab_menu_tabs()
def toogle_figures_loading():
figures_tab.loading = not figures_tab.loading
oggm_edu_logo_link = ('<p style="margin-top: 0px;"><a href="http://edu.oggm.org">' +
'<img src="https://edu.oggm.org/en/latest/_static/logos/oggm_edu_s_alpha.png" width=170>' +
'</a></p>')
hv_logo_link = '<a href="https://holoviz.org"><img src="https://holoviz.org/assets/holoviz-logo-stacked.svg" width=80></a>'
uni_ibk_logo = '<p style="margin-top: 0px;margin-bottom: 0px;"><a href="https://www.uibk.ac.at/"><img src="https://raw.githubusercontent.com/OGGM/mb_simulator/master/img/logo_uni_innsbruck.png" width=130></a></p>'
uni_bremen_logo = '<p style="margin-top: 0px;margin-bottom: 0px;"><a href="https://www.uni-bremen.de/"><img src="https://raw.githubusercontent.com/OGGM/mb_simulator/master/img/logo_uni_bremen.png" width=130></a></p>'
uni_bristol_logo = '<p style="margin-top: 0px;margin-bottom: 0px;"><a href="https://www.bristol.ac.uk/"><img src="https://raw.githubusercontent.com/OGGM/mb_simulator/master/img/logo_uni_bristol.png" width=130></a></p>'
hv_logo = pn.panel(hv_logo_link,
margin=(0, 0),
height=75,)
uni_logos = pn.Column(pn.Pane(oggm_edu_logo_link, margin=(0, 0), align='center'),
pn.Spacer(height=35),
pn.Pane(uni_ibk_logo, margin=(0, 0), align='center'),
pn.Spacer(height=25),
pn.Pane(uni_bremen_logo, margin=(0, 0), align='center', height=40),
pn.Spacer(height=5),
pn.Pane(uni_bristol_logo, margin=(0, 0), align='center'),
)
all_logos = pn.Row(pn.Spacer(width=30),
uni_logos,
pn.Spacer(width=30),
pn.Column(pn.Spacer(height=60,
margin=(0, 0),
),
pn.Pane(hv_logo_link, margin=(0, 0), align='center')
),
height=150
)
def get_simple_text(text='', width=30, font_size='30pt'):
return pn.Column(pn.layout.VSpacer(),
pn.widgets.StaticText(value=text,
style={'font-size': font_size}),
pn.layout.VSpacer(),
width=width,
sizing_mode="stretch_height",
)
def get_latex_text(text='', width=50, font_size='40pt'):
return pn.Column(pn.layout.VSpacer(),
pn.pane.LaTeX(text,
style={'font-size': font_size}),
pn.layout.VSpacer(),
width=width,
sizing_mode="stretch_height",
)
class PastMassBalanceAdapted(PastMassBalance):
"""Mass balance during the climate data period."""
def __init__(self, min_height=0, max_height=7000,
mu_star=None, bias=None,
filename='climate_historical', input_filesuffix='',
hemisphere='nh',
fpath=climate_data_dir, t_solid=None, t_liq=None,
t_melt=None, prcp_fac=None, t_grad=None,
repeat=False, ys=None, ye=None):
"""TODO
"""
super(PastMassBalance, self).__init__()
self.valid_bounds = [min_height, max_height] # in m
self.default_heights = np.arange(self.valid_bounds[0],
self.valid_bounds[1] + 1,
100)
if mu_star is None:
mu_star = default_mu
if bias is None:
bias = 0.
self.mu_star = np.array(mu_star)
self.bias = np.array(bias)
# Parameters
if t_solid is None:
self.t_solid = np.array(cfg.PARAMS['temp_all_solid'])
else:
self.t_solid = np.array(t_solid)
if t_liq is None:
self.t_liq = np.array(cfg.PARAMS['temp_all_liq'])
else:
self.t_liq = np.array(t_liq)
if t_melt is None:
self.t_melt = np.array(cfg.PARAMS['temp_melt'])
else:
self.t_melt = np.array(t_melt)
if prcp_fac is None:
prcp_fac = np.array(cfg.PARAMS['prcp_scaling_factor'])
# check if valid prcp_fac is used
if prcp_fac <= 0:
raise InvalidParamsError('prcp_fac has to be above zero!')
if t_grad is None:
t_grad = cfg.PARAMS['temp_default_gradient']
# Public attrs
self.hemisphere = hemisphere
self.repeat = repeat
# Private attrs
# to allow prcp_fac to be changed after instantiation
# prescribe the prcp_fac as it is instantiated
self._prcp_fac = prcp_fac
# same for temp bias
self._temp_bias = 0.
# Read file
with ncDataset(fpath + filename + input_filesuffix + '.nc', mode='r') as nc:
# time
time = nc.variables['time']
try:
time = netCDF4.num2date(time[:], time.units)
except ValueError:
# This is for longer time series
time = cftime.num2date(time[:], time.units, calendar='noleap')
ny, r = divmod(len(time), 12)
if r != 0:
raise ValueError('Climate data should be N full years')
# This is where we switch to hydro float year format
# Last year gives the tone of the hydro year
self.years = np.repeat(np.arange(time[-1].year-ny+1,
time[-1].year+1), 12)
self.months = np.tile(np.arange(1, 13), ny)
# save true timestamps
self.years_true = np.array([t.year for t in time])
self.months_true = np.array([t.month for t in time])
# Read timeseries and correct it
self.temp = nc.variables['temp'][:].astype(
np.float64) + self._temp_bias
self.prcp = nc.variables['prcp'][:].astype(
np.float64) * self._prcp_fac
# This is only needed if we want to use the gradient of the dataset
# if 'gradient' in nc.variables:
# grad = nc.variables['gradient'][:].astype(np.float64)
# # Security for stuff that can happen with local gradients
# g_minmax = cfg.PARAMS['temp_local_gradient_bounds']
# grad = np.where(~np.isfinite(grad), t_grad, grad)
# grad = clip_array(grad, g_minmax[0], g_minmax[1])
# else:
# grad = self.prcp * 0 + t_grad
self.ref_hgt = nc.ref_hgt
self.ref_lon = nc.ref_pix_lon
self.ref_lat = nc.ref_pix_lat
self.ref_dis = nc.ref_pix_dis
self.climate_source = nc.climate_source
self.ys = self.years[0] if ys is None else ys
self.ye = self.years[-1] if ye is None else ye
self._grad = None
self.grad = t_grad
@property
def grad(self):
return self._grad
@grad.setter
def grad(self, new_grad):
self._grad = self.prcp * 0 + new_grad
class ConstantMassBalanceAdapted(ConstantMassBalance):
"""Constant mass-balance during a chosen period.
This is useful for equilibrium experiments.
"""
def __init__(self, min_height=0, max_height=7000, mu_star=None, bias=None,
y_start=1991, y_end=2020, filename='climate_historical',
input_filesuffix='', hemisphere='nh', **kwargs):
"""TODO
"""
super(ConstantMassBalance, self).__init__()
self.mbmod = PastMassBalanceAdapted(min_height=min_height, max_height=max_height,
mu_star=mu_star, bias=bias,
filename=filename,
input_filesuffix=input_filesuffix,
hemisphere=hemisphere,
**kwargs)
self.valid_bounds = [min_height, max_height] # in m
self.default_heights = self.mbmod.default_heights
self.hbins = self.default_heights
# Private attrs
# to keep years up to date
self._y_start = y_start
self._y_end = y_end
self.update_years()
@property
def y_start(self):
return self._y_start
@y_start.setter
def y_start(self, value):
self._y_start = y_start
self.update_years()
@property
def y_end(self):
return self._y_end
@y_end.setter
def y_end(self, value):
self._y_end = value
self.update_years()
def update_years(self):
if self._y_start == self._y_end:
self.years = np.arange(self._y_start,
self._y_end + 1)
else:
self.years = np.arange(self._y_start,
self._y_end)
# this two function are coppied of the original ConstantMassBalance Class without
# the lazy decorator to be able to change y0 and halfsize, and also include
# the heights direclty and not returning a function (interp1d)
def interp_yr(self, heights):
# annual MB
mb_on_h = self.hbins*0.
for yr in self.years:
mb_on_h += self.mbmod.get_annual_mb(self.hbins, year=yr)
return interp1d(self.hbins, mb_on_h / len(self.years))(heights)
def interp_m(self, heights):
# monthly MB
months = np.arange(12)+1
interp_m = []
for m in months:
mb_on_h = self.hbins*0.
for yr in self.years:
yr = date_to_floatyear(yr, m)
mb_on_h += self.mbmod.get_monthly_mb(self.hbins, year=yr)
interp_m.append(
interp1d(self.hbins, mb_on_h / len(self.years))(heights))
return interp_m
def get_monthly_mb(self, heights, year=None, add_climate=False, **kwargs):
yr, m = floatyear_to_date(year)
if add_climate:
t, tmelt, prcp, prcpsol = self.get_monthly_climate(
heights, year=year)
return self.interp_m(heights)[m-1], t, tmelt, prcp, prcpsol
return self.interp_m(heights)[m-1]
from app_text import climograph_plot_labels
from app_text import general_plot_labels
from app_text import base_climate_timeseries_plot_labels
from app_text import mb_curve_plot_labels
from app_text import tab_headings
class MassBalanceVis(ConstantMassBalanceAdapted):
"""Mass balance during the climate data period."""
def __init__(self,
min_height=0,
max_height=7000,
input_filesuffix='',
hemisphere='nh',
mb_model_nr=0,
t_grad=None,
base_climate_name='',
mu=200,
y_start=1991,
y_end=2020,
t_solid=None,
t_liq=None,
t_melt=None,
prcp_fac=None,
repeat=False, ys=None, ye=None):
"""TODO
"""
super(MassBalanceVis, self).__init__(min_height=min_height,
max_height=max_height,
input_filesuffix=input_filesuffix,
hemisphere=hemisphere,
t_grad=t_grad,
mu_star=mu,
y_start=y_start,
y_end=y_end,
t_solid=t_solid,
t_liq=t_liq,
t_melt=t_melt,
prcp_fac=prcp_fac,
repeat=repeat,
ys=ys,
ye=ye
)
self.base_climate_name = base_climate_name
self.input_filesuffix = input_filesuffix # only needed to change language
self.mb_model_nr = mb_model_nr
# using months in hydro year
self.months_short = {'Oct': 1,
'Nov': 2,
'Dec': 3,
'Jan': 4,
'Feb': 5,
'Mar': 6,
'Apr': 7,
'May': 8,
'Jun': 9,
'Jul': 10,
'Aug': 11,
'Sep': 12}
self.default_tools = ['pan', 'wheel_zoom', 'save']
self.active_tools = ['pan', 'wheel_zoom']
def get_climograph(self, ref_height=None, y_lim_temp=None,
y_lim_prcp_max=None, add_MB_nr_title=False,
**kwargs):
def prcp_hook(plot, element):
p = plot.state
# color yaxis of prcp axis
prcp_color = 'blue'
plot.handles['yaxis'].axis_label_text_color = prcp_color
plot.handles['yaxis'].major_label_text_color = prcp_color
plot.handles['yaxis'].axis_line_color = prcp_color
plot.handles['yaxis'].major_tick_line_color = prcp_color
plot.handles['yaxis'].minor_tick_line_color = prcp_color
# for updating hover language
plot.handles['hover'].tooltips = hover_tooltips
plot.handles['hover'].formatters = hover_formatters
def temp_hook(plot, element):
p = plot.state
temp_color = 'red'
y2_label = general_plot_labels['Temperature'][language] + ' (°C)'
# create secondary range and axis
if 'twiny' not in [t for t in p.extra_y_ranges]:
p.y_range = Range1d(start=plot.handles['y_range'].start,
end=plot.handles['y_range'].end)
p.y_range.name = 'default'
p.extra_y_ranges = {"twiny": Range1d(start=-10, end=10)}
p.add_layout(LinearAxis(axis_label=y2_label,
y_range_name="twiny",
axis_label_text_color=temp_color,
major_label_text_color=temp_color,
axis_line_color=temp_color,
major_tick_line_color=temp_color,
minor_tick_line_color=temp_color), 'right')
# set glyph y_range_name to the one we've just created
glyph = p.renderers[-1]
glyph.y_range_name = 'twiny'
# set range of second y axis
p.extra_y_ranges["twiny"].start = y_lim_temp[0]
p.extra_y_ranges["twiny"].end = y_lim_temp[1]
if ref_height is None:
ref_height = self.mbmod.ref_hgt
temp_data = []
prcp_data = []
for month_name, month_nr in self.months_short.items():
temp, tempformelt, prcp_total, prcp_sol = \
self.get_monthly_climate(
heights=[ref_height], year=month_nr/12)
temp_data = np.append(temp_data, temp - self.mbmod.temp_bias)
prcp_data = np.append(prcp_data, prcp_total / self.mbmod._prcp_fac)
if self.mbmod.hemisphere == 'sh':
temp_data = np.roll(temp_data, 6)
prcp_data = np.roll(prcp_data, 6)
total_prcp = np.repeat(np.sum(prcp_data), len(prcp_data))
mean_temp = np.repeat(np.mean(temp_data), len(temp_data))
if y_lim_prcp_max is None:
y_lim_prcp_max = max(prcp_data) * 1.1
if y_lim_temp is None:
y_add_temp_range = (max(temp_data) - min(temp_data)) * 0.05
y_lim_temp = (min(temp_data) - y_add_temp_range,
max(temp_data) + y_add_temp_range)
# this is needed to show Temperatuer and Precipitation in the same hover
df = pd.DataFrame({'Month': np.array(general_plot_labels['Months_short'][language]),
'MonthLong': np.array(general_plot_labels['Months_long'][language]),
'Precipitation': np.array(prcp_data),
'Temperature': np.array(temp_data),
'totalPrecipitation': np.array(total_prcp),
'meanTemperature': np.array(mean_temp)})
hover_tooltips = """
<div>
<span style="font-size: 12px; color: #000000;">""" + \
general_plot_labels['Month'][language] + \
""": @{MonthLong}</span>
<br />
<span style="font-size: 12px; color: #0000FF;">""" + \
general_plot_labels['Precipitation'][language] + \
""": @{Precipitation}{%d} mm</span>
<br />
<span style="font-size: 12px; color: #0000FF;">""" + \
general_plot_labels['totalPrecipitation'][language] + \
""": @{totalPrecipitation}{%d} mm</span>
<br />
<span style="font-size: 12px; color: #FF0000;">""" + \
general_plot_labels['Temperature'][language] + \
""": @{Temperature}{%0.1f} °C</span>
<br />
<span style="font-size: 12px; color: #FF0000;">""" + \
general_plot_labels['meanTemperature'][language] + \
""": @{meanTemperature}{%0.1f} °C</span>
</div>"""
hover_formatters = {'@{Precipitation}': 'printf',
'@{totalPrecipitation}': 'printf',
'@{Temperature}': 'printf',
'@{meanTemperature}': 'printf'}
hover = HoverTool(mode='vline')
tools = ['save', hover]
if add_MB_nr_title:
title_prefix = 'MB ' + str(self.mb_model_nr + 1) + ', '
else:
title_prefix = ''
return (hv.Bars(df,
kdims='Month',
vdims=['Precipitation', 'Temperature', 'MonthLong',
'totalPrecipitation', 'meanTemperature'],
).opts(color='blue',
responsive=True,
ylim=(0, y_lim_prcp_max),
ylabel=general_plot_labels['Precipitation'][language] +
' (mm)',
hooks=[prcp_hook],
xlabel='',
default_tools=tools
) *
hv.Curve(df,
vdims='Temperature',
kdims='Month'
).opts(color='red',
hooks=[temp_hook],
responsive=True,
xlabel='',
default_tools=['save'] # tools
)
).opts(title=(title_prefix +
climograph_plot_labels['Period'][language] + ': ' +
str(self.years[0]) +
' ' + climograph_plot_labels['to'][language] + ' ' +
str(self.years[-1] + 1) + ', ' +
climograph_plot_labels['Height'][language] + ': ' +
'{:d} m'.format(int(ref_height))),
xrotation=45)
def get_monthly_components(self, month, **kwargs):
heights = self.default_heights
# for conversion depending on hemisphere
if self.mbmod.hemisphere == 'nh':
add_to_month = 0
elif self.mbmod.hemisphere == 'sh':
add_to_month = 6
mb, t, tmelt, prcp, prcpsol = self.get_monthly_mb(heights=heights,
year=(
self.months_short[month] + add_to_month)/12,
add_climate=True)
month_acc = prcpsol
month_abl = - self.mbmod.mu_star * tmelt
return month_acc, month_abl
def get_annual_components(self, **kwargs):
heights = self.default_heights
mb, t, tmelt, prcp, prcpsol = self.get_annual_mb(
heights=heights, add_climate=True)
annual_acc = prcpsol
annual_abl = - self.mbmod.mu_star * tmelt
return annual_acc, annual_abl
def get_xlim_annual(self, **kwargs):
heights = self.default_heights
annual_accumulation, annual_ablation = self.get_annual_components()
total_range = np.max(annual_accumulation) - np.min(annual_ablation)
add_lim = total_range * 0.05
return (np.min(annual_ablation) - add_lim, np.max(annual_accumulation) + add_lim)
def get_annual_mb_curve(self, months=[], kdims=None, vdims=None, **kwargs):
heights = self.default_heights
mb_annual = self.get_annual_mb(
heights=heights) * SEC_IN_YEAR * self.mbmod.rho
if kdims is None:
kdims = 'MB annual ' + str(self.mb_model_nr)
if vdims is None:
vdims = 'height (m)' + str(self.mb_model_nr)
annual_mb_curve = hv.Curve((mb_annual, heights),
kdims=kdims,
vdims=vdims,
label=mb_curve_plot_labels['annual'][language],
).opts(responsive=True,
default_tools=self.default_tools,
active_tools=self.active_tools,
ylabel=(mb_curve_plot_labels['Height'][language] +
' (m)'),
xlabel=mb_curve_plot_labels['kg/m2_and_year'][language],
ylim=(
np.min(heights), np.max(heights)),
xlim=self.get_xlim_annual(),
)
annual_mb_curve.opts(opts.Curve(framewise=True))
return annual_mb_curve
def get_month_mb_curve(self, month=None, vdims=None, **kwargs):
heights = self.default_heights
# for conversion depending on hemisphere
if self.mbmod.hemisphere == 'nh':
add_to_month = 0
elif self.mbmod.hemisphere == 'sh':
add_to_month = 6
mb_month = self.get_monthly_mb(
heights=heights, year=(self.months_short[month] + add_to_month)/12) \
* SEC_IN_MONTH * self.mbmod.rho
if vdims is None:
vdims = 'height (m)' + str(self.mb_model_nr)
month_mb_curve = hv.Curve((mb_month, heights),
kdims='MB ' + month + str(self.mb_model_nr),
vdims=vdims,
label=month
).opts(responsive=True,
default_tools=self.default_tools,
active_tools=self.active_tools,
xlabel=mb_curve_plot_labels['kg/m2_and_month'][language],
ylabel=(mb_curve_plot_labels['Height'][language] +
' (m)'),
ylim=(np.min(heights),
np.max(heights)),
xlim=(np.min(mb_month) - 1,
np.max(mb_month) + 1),
)
month_mb_curve.opts(opts.Curve(framewise=True))
return month_mb_curve
def get_annual_accumulation_curve(self, kdims=None, vdims=None, **kwargs):
heights = self.default_heights
annual_accumulation, annual_ablation = self.get_annual_components()
if kdims is None:
kdims = 'Acc. annual ' + str(self.mb_model_nr)
if vdims is None:
vdims = 'height (m)' + str(self.mb_model_nr)
annual_accumulation_curve = hv.Curve((annual_accumulation, heights),
kdims=kdims,
vdims=vdims,
label=mb_curve_plot_labels['annual'][language],
).opts(responsive=True,
default_tools=self.default_tools,
active_tools=self.active_tools,
xlabel=mb_curve_plot_labels['kg/m2_and_year'][language],
ylabel=(mb_curve_plot_labels['Height'][language] +
' (m)'),
ylim=(np.min(heights),
np.max(heights)),
xlim=self.get_xlim_annual()
)
annual_accumulation_curve.opts(opts.Curve(framewise=True))
return annual_accumulation_curve
def get_annual_ablation_curve(self, kdims=None, vdims=None, **kwargs):
heights = self.default_heights
annual_accumulation, annual_ablation = self.get_annual_components()
if kdims is None:
kdims = 'Abl. annual ' + str(self.mb_model_nr)
if vdims is None:
vdims = 'height (m)' + str(self.mb_model_nr)
annual_ablation_curve = hv.Curve((annual_ablation, heights),
kdims=kdims,
vdims=vdims,
label=mb_curve_plot_labels['annual'][language],
).opts(responsive=True,
default_tools=self.default_tools,
active_tools=self.active_tools,
xlabel=mb_curve_plot_labels['kg/m2_and_year'][language],
ylabel=(mb_curve_plot_labels['Height'][language] +
' (m)'),
ylim=(np.min(heights),
np.max(heights)),
xlim=self.get_xlim_annual()
)
annual_ablation_curve.opts(opts.Curve(framewise=True))
return annual_ablation_curve
def get_month_accumulation_curve(self, month=None, vdims=None, **kwargs):
heights = self.default_heights
month_accumulation, month_ablation = self.get_monthly_components(
heights=heights, month=month)
if vdims is None:
vdims = 'height (m)' + str(self.mb_model_nr)
month_accumulation_curve = hv.Curve((month_accumulation, heights),
kdims='Acc. ' + month +
str(self.mb_model_nr),
vdims=vdims,
label=month
).opts(responsive=True,
default_tools=self.default_tools,
active_tools=self.active_tools,
xlabel=mb_curve_plot_labels['kg/m2_and_month'][language],
ylabel=(mb_curve_plot_labels['Height'][language] +
' (m)'),
ylim=(np.min(heights),
np.max(heights)),
xlim=self.get_xlim_annual()
)
month_accumulation_curve.opts(opts.Curve(framewise=True))
return month_accumulation_curve
def get_month_ablation_curve(self, month=None, vdims=None, **kwarg):
heights = self.default_heights
month_accumulation, month_ablation = self.get_monthly_components(
heights=heights, month=month)
if vdims is None:
vdims = 'height (m)' + str(self.mb_model_nr)
month_ablation_curve = hv.Curve((month_ablation, heights),
kdims='Abl. ' + month +
str(self.mb_model_nr),
vdims=vdims,
label=month
).opts(responsive=True,
default_tools=self.default_tools,
active_tools=self.active_tools,
xlabel=mb_curve_plot_labels['kg/m2_and_month'][language],
ylabel=(mb_curve_plot_labels['Height'][language] +
' (m)'),
ylim=(np.min(heights),
np.max(heights)),
xlim=self.get_xlim_annual()
)
month_ablation_curve.opts(opts.Curve(framewise=True))
return month_ablation_curve
def get_ELA_curve(self, kdims=None, vdims=None, xlim=None, **kwarg):
heights = self.default_heights
ELA = self.get_ela()
if xlim is None:
xlim = self.get_xlim_annual()
if vdims is None:
vdims = 'height (m)' + str(self.mb_model_nr)
ELA_curve = hv.Curve((list(xlim),
[ELA, ELA]),
kdims=kdims + str(self.mb_model_nr),
vdims=vdims,
label='ELA'
).opts(responsive=True,
color='black',
line_dash='dashed',
default_tools=self.default_tools,
active_tools=self.active_tools,
ylim=(np.min(heights), np.max(heights)),
xlim=xlim,
ylabel=(mb_curve_plot_labels['Height'][language] +
' (m)'),
)
ELA_curve.opts(opts.Curve(framewise=True))
return ELA_curve
def get_vline_with_hover(self, position=0, months=[], var='mb'):
heights = self.default_heights
if var == 'mb':
first_var = self.get_annual_mb(
heights=heights) * SEC_IN_YEAR * self.mbmod.rho
else:
annual_accumulation, annual_ablation = self.get_annual_components()
if var == 'abl':
first_var = annual_ablation
elif var == 'acc':
first_var = annual_accumulation
data = {'height' + str(self.mb_model_nr): heights,
'annual' + str(self.mb_model_nr): first_var,
'position' + str(self.mb_model_nr): np.repeat(position, len(heights))}
vdims = ['height' + str(self.mb_model_nr),
'annual' + str(self.mb_model_nr)]
kdims = ['position' + str(self.mb_model_nr)]
hover_tooltips = '''
<div>
<span style="font-size: 12px; color: #000000;">''' + \
mb_curve_plot_labels['Height'][language] + \
''': @{height''' + str(self.mb_model_nr) + '''}{%0.0f} m</span>
<br />
<span style="font-size: 12px; color: #000000;">''' + \
mb_curve_plot_labels['annual'][language] + \
''': @{annual''' + str(self.mb_model_nr) + '''}{%0.0f} ''' + \
mb_curve_plot_labels['kg/m2_and_year'][language] + \
'''</span>
'''
hover_formatters = {'@{height' + str(self.mb_model_nr) + '}': 'printf',
'@{annual' + str(self.mb_model_nr) + '}': 'printf'}
# for conversion depending on hemisphere
if self.mbmod.hemisphere == 'nh':
add_to_month = 0
elif self.mbmod.hemisphere == 'sh':
add_to_month = 6
for month in general_plot_labels['Months_short'][language]:
if var == 'mb':
data[month] = self.get_monthly_mb(heights=heights,
year=(self.months_short[month] + add_to_month)/12) \
* SEC_IN_MONTH * self.mbmod.rho
else:
month_accumulation, month_ablation = self.get_monthly_components(
heights=heights, month=month)
if var == 'abl':
data[month] = month_ablation
elif var == 'acc':
data[month] = month_accumulation
for month in months:
vdims.append(month)
hover_tooltips += ('<br />' +
'<span style="font-size: 12px; color: #000000;">' +
month + ': @{' + month + '}{%0.0f} ' +
mb_curve_plot_labels['kg/m2_and_month'][language] + '</span>')
hover_formatters['@{' + month + '}'] = 'printf'
hover_tooltips += '</div>'
hover = HoverTool(mode='hline')
def hover_hook(plot, element):
plot.handles['hover'].tooltips = hover_tooltips
plot.handles['hover'].formatters = hover_formatters
vline_curve = hv.Curve(pd.DataFrame(data),
kdims=kdims,
vdims=vdims,
).opts(responsive=True,
default_tools=self.default_tools +
[hover],
color='gray',
line_width=1,
line_alpha=0.5,
ylabel=(mb_curve_plot_labels['Height'][language] +
' (m)'),
xlabel=mb_curve_plot_labels['kg/m2_and_year_or_month'][language],
ylim=(
np.min(heights), np.max(heights)),
xlim=self.get_xlim_annual(),
hooks=[hover_hook]
)
vline_curve.opts(opts.Curve(framewise=True))
return vline_curve
def get_climate_timeseries(self):
def prcp_hook(plot, element):
p = plot.state
# color yaxis of prcp axis
prcp_color = 'blue'
plot.handles['yaxis'].axis_label_text_color = prcp_color
plot.handles['yaxis'].major_label_text_color = prcp_color
plot.handles['yaxis'].axis_line_color = prcp_color
plot.handles['yaxis'].major_tick_line_color = prcp_color
plot.handles['yaxis'].minor_tick_line_color = prcp_color
def temp_hook(plot, element):
p = plot.state
temp_color = 'red'
y2_label = general_plot_labels['Temperature'][language] + ' (°C)'
# create secondary range and axis
if 'twiny' not in [t for t in p.extra_y_ranges]:
p.y_range = Range1d(start=plot.handles['y_range'].start,
end=plot.handles['y_range'].end)
p.y_range.name = 'default'
p.extra_y_ranges = {"twiny": Range1d(start=-10, end=10)}
p.add_layout(LinearAxis(axis_label=y2_label,
y_range_name="twiny",
axis_label_text_color=temp_color,
major_label_text_color=temp_color,
axis_line_color=temp_color,
major_tick_line_color=temp_color,
minor_tick_line_color=temp_color), 'right')
# set glyph y_range_name to the one we've just created
glyph = p.renderers[-1]
glyph.y_range_name = 'twiny'
if 'Temperature' in glyph.data_source.data:
vals = glyph.data_source.data['Temperature']
# set range of second y axis
y_add_range = (vals.max() - vals.min()) * 0.05
p.extra_y_ranges["twiny"].start = vals.min() - y_add_range * 4
p.extra_y_ranges["twiny"].end = vals.max() + y_add_range
# this function is needed as the names are defined with 0 = October
def get_mth_name_nr(real_mth):
ny, r = divmod(real_mth + 2, 12)
return r
# this is needed to show Temperatuer and Precipitation in the same hover
df = pd.DataFrame({'Month': np.array([general_plot_labels['Months_long'][language][get_mth_name_nr(i)]
for i in self.mbmod.months_true]),
'Year_real': self.mbmod.years_true,
'Year_hydro': self.mbmod.years,
'Precipitation': self.mbmod.prcp / self.mbmod._prcp_fac,
'Temperature': self.mbmod.temp - self.mbmod.temp_bias},
index=pd.to_datetime(
pd.DataFrame({'year': self.mbmod.years_true,
'month': self.mbmod.months_true,
'day': np.repeat(1, len(self.mbmod.years_true))}
)
)
)
df.index.name = 'Time'
# calculate yearly means
df_mean_yr = df.groupby('Year_hydro').mean()
df['Temperature_yr_mean'] = np.repeat(
df_mean_yr['Temperature'], 12).values
df['Precipitation_yr_mean'] = np.repeat(
df_mean_yr['Precipitation'], 12).values
# calculate total means
df_mean_total = df.mean(numeric_only=True)
df['Temperature_total_mean'] = np.repeat(
df_mean_total['Temperature'], len(df))
df['Precipitation_total_mean'] = np.repeat(
df_mean_total['Precipitation'], len(df))
hover = HoverTool(tooltips="""
<div>
<span style="font-size: 12px; color: #000000;">""" +
base_climate_timeseries_plot_labels['Year'][language] +
""": @{Year_real}</span>
<br />
<span style="font-size: 12px; color: #000000;">""" +
general_plot_labels['Month'][language] +
""": @{Month}</span>
<br />
<span style="font-size: 12px; color: #000000;">""" +
base_climate_timeseries_plot_labels['Hydro-Year'][language] +
""": @{Year_hydro}</span>
<br />
<span style="font-size: 12px; color: #0000FF;">""" +
general_plot_labels['Precipitation'][language] +
""": @{Precipitation}{%d} mm</span>
<br />
<span style="font-size: 12px; color: #0000FF;">""" +
base_climate_timeseries_plot_labels['Yearly Mean Prcp'][language] +
""": @{Precipitation_yr_mean}{%d} mm</span>
<br />
<span style="font-size: 12px; color: #FF0000;">""" +
general_plot_labels['Temperature'][language] +
""": @{Temperature}{%0.1f} °C</span>
<br />
<span style="font-size: 12px; color: #FF0000;">""" +
base_climate_timeseries_plot_labels['Yearly Mean Temp'][language] +
""": @{Temperature_yr_mean}{%0.1f} °C</span>
</div>""",
formatters={'@{Precipitation}': 'printf',
'@{Temperature}': 'printf',
'@{Temperature_yr_mean}': 'printf',
'@{Precipitation_yr_mean}': 'printf'},
mode='vline')
tools = ['save', 'xwheel_zoom', 'xpan', 'reset']
active_tools = ['xwheel_zoom', 'xpan']
temp_curve = hv.Curve(df,
vdims='Temperature',
kdims='Time'
).opts(color='red',
alpha=0.5,
hooks=[temp_hook],
responsive=True,
xlabel='',
default_tools=tools
)
temp_yr_mean_curve = hv.Curve(df,
vdims=['Temperature_yr_mean'],
kdims='Time'
).opts(color='red',
hooks=[temp_hook],
responsive=True,
line_dash='dashed',
xlabel='',
default_tools=tools
)
temp_total_mean_curve = hv.Curve(df,
vdims=['Temperature_total_mean', 'Temperature',
'Precipitation', 'Year_real', 'Month', 'Year_hydro',
'Temperature_yr_mean', 'Precipitation_yr_mean'],
kdims='Time',
label=('Total mean Temp (' +
str(df['Temperature_total_mean'].values[0]) +
' °C')
).opts(color='red',
hooks=[temp_hook],
responsive=True,
xlabel='',
default_tools=tools + [hover],
active_tools=active_tools
)
prcp_curve = hv.Area(df,
kdims='Time',
vdims='Precipitation',
).opts(color='blue',
line_color=None,
fill_alpha=0.5,
responsive=True,
ylim=(0, max(self.mbmod.prcp /
self.mbmod._prcp_fac) * 1.5),
ylabel=general_plot_labels['Precipitation'][language] +
' (mm)',
hooks=[prcp_hook],
xlabel='',
default_tools=tools
)
prcp_yr_mean_curve = hv.Curve(df,
vdims=['Precipitation_yr_mean'],
kdims='Time'
).opts(color='blue',
hooks=[prcp_hook],
responsive=True,
line_dash='dashed',
ylim=(
0, max(self.mbmod.prcp / self.mbmod._prcp_fac) * 1.5),
ylabel=general_plot_labels['Precipitation'][language] +
' (mm)',
xlabel='',
default_tools=tools
)
prcp_total_mean_curve = hv.Curve(df,
vdims='Precipitation_total_mean',
kdims='Time',
label='Total mean Prcp'
).opts(color='blue',
hooks=[prcp_hook],
responsive=True,
ylim=(
0, max(self.mbmod.prcp / self.mbmod._prcp_fac) * 1.5),
ylabel=general_plot_labels['Precipitation'][language] +
' (mm)',
xlabel='',
default_tools=tools
)
timeseries_width = 1000
tgt_temp = temp_curve.relabel('').opts(
xlabel='',)
src_temp = temp_curve.opts(
height=100,
yaxis=None,
default_tools=[],
active_tools=[])
tgt_temp_yr_mean = temp_yr_mean_curve.relabel(
base_climate_timeseries_plot_labels['Yearly Mean Temp'][language]).opts(
xlabel='',)
src_temp_yr_mean = temp_yr_mean_curve.opts(
height=100,
yaxis=None,
default_tools=[],
active_tools=[])
tgt_temp_total_mean = temp_total_mean_curve.relabel(
base_climate_timeseries_plot_labels['Total Mean Temp'][language] +
' ({:.1f} °C)'.format(
df['Temperature_total_mean'].values[0])).opts(
xlabel='',)
src_temp_total_mean = temp_total_mean_curve.opts(
height=100,
yaxis=None,
default_tools=[],
active_tools=[])
tgt_prcp = prcp_curve.relabel('').opts(
labelled=['y'])
src_prcp = prcp_curve.opts(
height=100,
yaxis=None,
default_tools=[],
active_tools=[])
tgt_prcp_yr_mean = prcp_yr_mean_curve.relabel(
base_climate_timeseries_plot_labels['Yearly Mean Prcp'][language]).opts(
xlabel='',)
src_prcp_yr_mean = prcp_yr_mean_curve.opts(
# width=timeseries_width,
height=100,
yaxis=None,
default_tools=[],
active_tools=[])
tgt_prcp_total_mean = prcp_total_mean_curve.relabel(
base_climate_timeseries_plot_labels['Total Mean Prcp'][language] +
' ({:.0f} mm)'.format(
df['Precipitation_total_mean'].values[0])).opts(
xlabel='',)
src_prcp_total_mean = prcp_total_mean_curve.opts(
height=100,
yaxis=None,
default_tools=[],
active_tools=[])
# RangeToolLink(src_temp, tgt_temp)
RangeToolLink(src_prcp, tgt_prcp)
def source_hook(plot, element):
p = plot.state
p.yaxis.visible = False
def tgt_hook(plot, element):
self.timeseries_x_range_handle = plot.handles['x_range']
# range month depend on hemisphere (start of hydrological year)
if self.mbmod.hemisphere == 'nh':
hydro_month_range = [10, 9]
elif self.mbmod.hemisphere == 'sh':
hydro_month_range = [4, 3]
if len(self.years) == 1:
xlim = pd.to_datetime(pd.DataFrame({'year': [self.years[0] - 1, self.years[0]],
'month': hydro_month_range,
'day': [1, 1]}
))
else:
xlim = pd.to_datetime(pd.DataFrame({'year': [self.years[0] - 1, self.years[-1]],
'month': hydro_month_range,
'day': [1, 1]}
))
self.timeseries_xlim = xlim
layout = ((tgt_prcp * tgt_prcp_yr_mean * tgt_prcp_total_mean *
tgt_temp * tgt_temp_yr_mean * tgt_temp_total_mean
).opts(legend_position='left',
legend_offset=(5, 10),
xlim=tuple(xlim.values),
hooks=[tgt_hook],
) +
(src_prcp * src_prcp_yr_mean * src_prcp_total_mean *
src_temp * src_temp_yr_mean * src_temp_total_mean
).opts(hooks=[source_hook],
show_legend=False)
).cols(1)
layout.opts(opts.Layout(shared_axes=False, merge_tools=False))
return layout
mb_models = [MassBalanceVis(
min_height=climate_tab_text['base_climate_meta_data'][0][1],
max_height=climate_tab_text['base_climate_meta_data'][0][2],
input_filesuffix=climate_tab_text['base_climate_meta_data'][0][0],
hemisphere=climate_tab_text['base_climate_meta_data'][0][3],
mb_model_nr=0,
t_grad=temp_grad_slider.value / 100,
base_climate_name=climate_tab_text['base_climate_names'][language][0],
mu=mu_slider.value,
y_start=y_start_slider.value,
y_end=y_end_slider.value,
t_solid=temp_solid_slider.value,
t_liq=temp_liquid_slider.value,
t_melt=temp_melt_slider.value,
prcp_fac=prcp_fac_slider.value),
MassBalanceVis(
min_height=climate_tab_text['base_climate_meta_data'][1][1],
max_height=climate_tab_text['base_climate_meta_data'][1][2],
input_filesuffix=climate_tab_text['base_climate_meta_data'][1][0],
hemisphere=climate_tab_text['base_climate_meta_data'][1][3],
mb_model_nr=1,
t_grad=temp_grad_slider.value / 100,
base_climate_name=climate_tab_text['base_climate_names'][language][1],
mu=mu_slider.value,
y_start=y_start_slider.value,
y_end=y_end_slider.value,
t_solid=temp_solid_slider.value,
t_liq=temp_liquid_slider.value,
t_melt=temp_melt_slider.value,
prcp_fac=prcp_fac_slider.value)]
def set_mb_months_language():
months_dict = {month: i + 1 for i,
month in enumerate(general_plot_labels['Months_short'][language])}
mb_models[0].months_short = months_dict
mb_models[1].months_short = months_dict
def set_climate_of_mb_model(mb_i):
global mb_models
global climate_meta_data
# initialise new MB with new climate settings
mb_models[mb_i] = MassBalanceVis(
min_height=climate_meta_data[1],
max_height=climate_meta_data[2],
input_filesuffix=climate_meta_data[0],
hemisphere=climate_meta_data[3],
mb_model_nr=mb_i,
t_grad=temp_grad_slider.value / 100,
base_climate_name=base_climate_select.value,
mu=mu_slider.value,
y_start=y_start_slider.value,
y_end=y_end_slider.value,
t_solid=temp_solid_slider.value,
t_liq=temp_liquid_slider.value,
t_melt=temp_melt_slider.value,
prcp_fac=prcp_fac_slider.value)
mb_models[mb_i].temp_bias = temp_bias_slider.value
def set_climate_menu_with_selected_mb_model(mb_i):
base_climate_select.value = mb_models[mb_i].base_climate_name
y_start_slider.value = int(mb_models[mb_i].y_start)
y_end_slider.value = int(mb_models[mb_i].y_end)
change_y_end_slider_start(None)
def set_mb_settings_of_mb_model(mb_i):
mb_models[mb_i].mbmod.mu_star = np.array(mu_slider.value)
mb_models[mb_i].mbmod.t_melt = np.array(temp_melt_slider.value)
mb_models[mb_i].mbmod.t_solid = np.array(temp_solid_slider.value)
mb_models[mb_i].mbmod.t_liq = np.array(temp_liquid_slider.value)
mb_models[mb_i].prcp_fac = np.array(prcp_fac_slider.value)
mb_models[mb_i].temp_bias = np.array(temp_bias_slider.value)
mb_models[mb_i].mbmod.grad = np.array(temp_grad_slider.value / 100)
def set_mb_settings_menu_with_selected_mb_model(mb_i):
mu_slider.value = int(mb_models[mb_i].mbmod.mu_star)
temp_melt_slider.value = float(mb_models[mb_i].mbmod.t_melt)
temp_solid_slider.value = float(mb_models[mb_i].mbmod.t_solid)
temp_liquid_slider.value = float(mb_models[mb_i].mbmod.t_liq)
prcp_fac_slider.value = float(mb_models[mb_i].mbmod.prcp_fac)
temp_bias_slider.value = float(mb_models[mb_i].mbmod.temp_bias)
temp_grad_slider.value = float(mb_models[mb_i].mbmod.grad[0]) * 100
def dyn_overlay_accumulation(months, update, mb_i=0):
overlay = mb_models[mb_i].get_ELA_curve(kdims='Acc.' + str(mb_i))
overlay *= mb_models[mb_i].get_vline_with_hover(months=months, var='acc')
overlay *= mb_models[mb_i].get_annual_accumulation_curve()
for month in list(mb_models[mb_i].months_short.keys()):
if month in months:
overlay *= mb_models[mb_i].get_month_accumulation_curve(
month=month)
else:
overlay *= hv.Curve((0, 0), label='').opts(responsive=True,
default_tools=mb_models[mb_i].default_tools,
active_tools=mb_models[mb_i].active_tools,
xlabel=mb_curve_plot_labels['kg/m2_and_month'][language],
ylim=(np.min(mb_models[mb_i].default_heights),
np.max(mb_models[mb_i].default_heights))
)
return overlay.opts(title=mb_curve_plot_labels['Accumulation'][language],
toolbar='above',
show_legend=True,
legend_position='top_left',
xlabel=mb_curve_plot_labels['kg/m2_and_year_or_month'][language]
)
def dyn_overlay_ablation(months, update, mb_i=0):
overlay = mb_models[mb_i].get_ELA_curve(kdims='Abl.' + str(mb_i))
overlay *= mb_models[mb_i].get_vline_with_hover(months=months, var='abl')
overlay *= mb_models[mb_i].get_annual_ablation_curve()
for month in list(mb_models[mb_i].months_short.keys()):
if month in months:
overlay *= mb_models[mb_i].get_month_ablation_curve(month=month)
else:
overlay *= hv.Curve((0, 0), label='').opts(responsive=True,
default_tools=mb_models[mb_i].default_tools,
active_tools=mb_models[mb_i].active_tools,
xlabel=mb_curve_plot_labels['kg/m2_and_month'][language],
ylim=(np.min(mb_models[mb_i].default_heights),
np.max(mb_models[mb_i].default_heights))
)
return overlay.opts(title=mb_curve_plot_labels['Ablation'][language],
ylabel='',
toolbar='above',
show_legend=False,
xlabel=mb_curve_plot_labels['kg/m2_and_year_or_month'][language]
)
def dyn_overlay_total_mb(months, update, mb_i=0):
overlay = mb_models[mb_i].get_ELA_curve(kdims='MB' + str(mb_i))
overlay *= mb_models[mb_i].get_vline_with_hover(months=months, var='mb')
overlay *= mb_models[mb_i].get_annual_mb_curve(months=months)
for month in list(mb_models[mb_i].months_short.keys()):
if month in months:
overlay *= mb_models[mb_i].get_month_mb_curve(
month=month).opts(xlim=(None, None))
else:
overlay *= hv.Curve((0, 0), label='').opts(responsive=True,
default_tools=mb_models[mb_i].default_tools,
active_tools=mb_models[mb_i].active_tools,
xlabel=mb_curve_plot_labels['kg/m2_and_month'][language],
ylim=(np.min(mb_models[mb_i].default_heights),
np.max(mb_models[mb_i].default_heights))
)
return overlay.opts(title=mb_curve_plot_labels['Mass-Balance'][language],
ylabel='',
toolbar='above',
show_legend=False,
xlabel=mb_curve_plot_labels['kg/m2_and_year_or_month'][language]
)
select_months_mb_plot_0 = pn.widgets.CheckBoxGroup(
name='month selection',
value=[],
inline=False,
width=50)
select_months_mb_plot_1 = pn.widgets.CheckBoxGroup(
name='month selection',
value=[],
inline=False,
width=50)
select_months_mb_plot_both = [select_months_mb_plot_0,
select_months_mb_plot_1]
def set_select_months_language():
select_months_mb_plot_both[0].options = general_plot_labels['Months_short'][language]
select_months_mb_plot_both[1].options = general_plot_labels['Months_short'][language]
set_select_months_language()
mb_plot_updater_0 = pn.widgets.Checkbox(name='Update MB Plot 0')
mb_plot_updater_1 = pn.widgets.Checkbox(name='Update MB Plot 1')
mb_plot_updater_both = [mb_plot_updater_0,
mb_plot_updater_1]
dmap_accumulation_both = [hv.DynamicMap(
pn.bind(partial(dyn_overlay_accumulation, mb_i=0),
months=select_months_mb_plot_0,
update=mb_plot_updater_0)),
hv.DynamicMap(
pn.bind(partial(dyn_overlay_accumulation, mb_i=1),
months=select_months_mb_plot_1,
update=mb_plot_updater_1)), ]
dmap_ablation_both = [hv.DynamicMap(
pn.bind(partial(dyn_overlay_ablation, mb_i=0),
months=select_months_mb_plot_0,
update=mb_plot_updater_0)),
hv.DynamicMap(
pn.bind(partial(dyn_overlay_ablation, mb_i=1),
months=select_months_mb_plot_1,
update=mb_plot_updater_1)), ]
dmap_total_mb_both = [hv.DynamicMap(
pn.bind(partial(dyn_overlay_total_mb, mb_i=0),
months=select_months_mb_plot_0,
update=mb_plot_updater_0)),
hv.DynamicMap(
pn.bind(partial(dyn_overlay_total_mb, mb_i=1),
months=select_months_mb_plot_1,
update=mb_plot_updater_1)), ]
def get_complete_mb_figure(mb_i):
return pn.Row(pn.Column(pn.layout.VSpacer(),
select_months_mb_plot_both[mb_i],
pn.layout.VSpacer(),
width=50,
sizing_mode="stretch_height",
),
pn.Row(dmap_accumulation_both[mb_i],
get_simple_text('+'),
dmap_ablation_both[mb_i],
get_simple_text('='),
dmap_total_mb_both[mb_i],
sizing_mode='stretch_both'
),
sizing_mode='stretch_both',
)
mb_figures = [get_complete_mb_figure(0),
get_complete_mb_figure(1)]
def toogle_mb_plot_disabled():
select_months_mb_plot_0.disabled = not select_months_mb_plot_0.disabled
select_months_mb_plot_1.disabled = not select_months_mb_plot_1.disabled
color_mb_0 = 'blue'
color_mb_1 = 'red'
def get_xlim_comparison():
xlim_0 = mb_models[0].get_xlim_annual()
xlim_1 = mb_models[1].get_xlim_annual()
return (min(xlim_0[0], xlim_1[0]), max(xlim_0[1], xlim_1[1]))
def get_heights_comparison():
heights_0 = mb_models[0].default_heights
heights_1 = mb_models[1].default_heights
return np.arange(min(heights_0[0], heights_1[0]),
max(heights_0[-1], heights_1[-1]) + 1,
100)
def get_vline_hover_comparison(position=0, var='mb'):
heights = get_heights_comparison()
var_0 = np.empty(len(heights))
var_0[:] = np.NaN
ind_0 = np.isin(heights, mb_models[0].default_heights)
var_1 = np.empty(len(heights))
var_1[:] = np.NaN
ind_1 = np.isin(heights, mb_models[1].default_heights)
if var == 'mb':
var_0[ind_0] = mb_models[0].get_annual_mb(
heights=mb_models[0].default_heights) * SEC_IN_YEAR * mb_models[0].mbmod.rho
var_1[ind_1] = mb_models[1].get_annual_mb(
heights=mb_models[1].default_heights) * SEC_IN_YEAR * mb_models[1].mbmod.rho
else:
annual_accumulation_0, annual_ablation_0 = mb_models[0].get_annual_components(
)
annual_accumulation_1, annual_ablation_1 = mb_models[1].get_annual_components(
)
if var == 'abl':
var_0[ind_0] = annual_ablation_0
var_1[ind_1] = annual_ablation_1
elif var == 'acc':
var_0[ind_0] = annual_accumulation_0
var_1[ind_1] = annual_accumulation_1
data = {'heightComp': heights,
'MB1': var_0,
'MB2': var_1,
'positionComp' + var: np.repeat(position, len(heights))}
vdims = ['heightComp', 'MB1', 'MB2']
kdims = ['positionComp' + var]
hover_tooltips = '''
<div>
<span style="font-size: 12px; color: #000000;">''' + \
mb_curve_plot_labels['Height'][language] + \
''': @{heightComp}{%0.0f} m</span>
<br />
<span style="font-size: 12px; color: #0000FF;">MB Model 1: @{MB1}{%0.0f} ''' + \
mb_curve_plot_labels['kg/m2_and_year'][language] + \
'''</span>
<br />
<span style="font-size: 12px; color: #FF0000;">MB Model 2: @{MB2}{%0.0f} ''' + \
mb_curve_plot_labels['kg/m2_and_year'][language] + \
'''</span>
</div>
'''
hover_formatters = {'@{heightComp}': 'printf',
'@{MB1}': 'printf',
'@{MB2}': 'printf'}
hover = HoverTool(mode='hline')
def hover_hook(plot, element):
plot.handles['hover'].tooltips = hover_tooltips
plot.handles['hover'].formatters = hover_formatters
vline_curve = hv.Curve(pd.DataFrame(data),
kdims=kdims,
vdims=vdims,
).opts(responsive=True,
default_tools=mb_models[0].default_tools +
[hover],
color='gray',
line_width=1,
line_alpha=0.5,
ylabel=mb_curve_plot_labels['Height'][language] +
' (m)',
xlabel=mb_curve_plot_labels['kg/m2_and_year'][language],
ylim=(
np.min(heights), np.max(heights)),
xlim=get_xlim_comparison(),
hooks=[hover_hook],
)
vline_curve.opts(opts.Curve(framewise=True))
return vline_curve
def dyn_overlay_comp_acc(update):
overlay = get_vline_hover_comparison(position=0, var='acc')
overlay *= mb_models[0].get_ELA_curve(kdims='Acc. Comp',
vdims='h Comp',
xlim=get_xlim_comparison()
).relabel('ELA 1').opts(color=color_mb_0)
overlay *= mb_models[0].get_annual_accumulation_curve(kdims='Acc. Comp',
vdims='h Comp',
).relabel('MB 1').opts(color=color_mb_0)
overlay *= mb_models[1].get_ELA_curve(kdims='Acc. Comp',
vdims='h Comp',
xlim=get_xlim_comparison()
).relabel('ELA 2').opts(color=color_mb_1)
overlay *= mb_models[1].get_annual_accumulation_curve(kdims='Acc. Comp',
vdims='h Comp',
).relabel('MB 2').opts(color=color_mb_1)
return overlay.opts(title=mb_curve_plot_labels['Accumulation'][language],
toolbar='above',
show_legend=True,
legend_position='top_left',
xlim=get_xlim_comparison()
)
def dyn_overlay_comp_abl(update):
overlay = get_vline_hover_comparison(position=0, var='abl')
overlay *= mb_models[0].get_ELA_curve(kdims='Abl. Comp',
vdims='h Comp',
xlim=get_xlim_comparison()
).relabel('ELA 1').opts(color=color_mb_0)
overlay *= mb_models[1].get_ELA_curve(kdims='Abl. Comp',
xlim=get_xlim_comparison()
).relabel('ELA 2').opts(color=color_mb_1)
overlay *= mb_models[0].get_annual_ablation_curve(kdims='Abl. Comp',
vdims='h Comp',
).relabel('MB 1').opts(color=color_mb_0)
overlay *= mb_models[1].get_annual_ablation_curve(kdims='Abl. Comp',
vdims='h Comp',
).relabel('MB 2').opts(color=color_mb_1)
return overlay.opts(title=mb_curve_plot_labels['Ablation'][language],
ylabel='',
toolbar='above',
show_legend=False,
xlim=get_xlim_comparison()
)
def dyn_overlay_comp_total_mb(update):
overlay = get_vline_hover_comparison(position=0, var='mb')
overlay *= mb_models[0].get_ELA_curve(kdims='MB Comp',
vdims='h Comp',
xlim=get_xlim_comparison()
).relabel('ELA 1').opts(color=color_mb_0)
overlay *= mb_models[1].get_ELA_curve(kdims='MB Comp',
vdims='h Comp',
xlim=get_xlim_comparison()
).relabel('ELA 2').opts(color=color_mb_1)
overlay *= mb_models[0].get_annual_mb_curve(kdims='MB Comp',
vdims='h Comp',
).relabel('MB 1').opts(color=color_mb_0)
overlay *= mb_models[1].get_annual_mb_curve(kdims='MB Comp',
vdims='h Comp',
).relabel('MB 2').opts(color=color_mb_1)
return overlay.opts(title=mb_curve_plot_labels['Mass-Balance'][language],
ylabel='',
toolbar='above',
show_legend=False,
xlim=get_xlim_comparison()
)
mb_comp_plot_updater = pn.widgets.Checkbox(name='Update Comp MB Plot')
dmap_comp_acc = hv.DynamicMap(
pn.bind(dyn_overlay_comp_acc,
update=mb_comp_plot_updater))
dmap_comp_ablation = hv.DynamicMap(
pn.bind(dyn_overlay_comp_abl,
update=mb_comp_plot_updater))
dmap_comp_total_mb_both = hv.DynamicMap(
pn.bind(dyn_overlay_comp_total_mb,
update=mb_comp_plot_updater))
compare_mb_figure = pn.Row(dmap_comp_acc,
get_simple_text('+'),
dmap_comp_ablation,
get_simple_text('='),
dmap_comp_total_mb_both,
sizing_mode='stretch_both'
)
def get_climograph(change, mb_i=0):
return mb_models[mb_i].get_climograph().opts(toolbar='above')
climograph_updater_0 = pn.widgets.Checkbox(name='Update Climograph 0')
climograph_updater_1 = pn.widgets.Checkbox(name='Update Climograph 1')
climograph_updater_both = [climograph_updater_0,
climograph_updater_1]
climographs = [hv.DynamicMap(
pn.bind(partial(get_climograph, mb_i=0),
change=climograph_updater_0)),
hv.DynamicMap(
pn.bind(partial(get_climograph, mb_i=1),
change=climograph_updater_1)), ]
def get_y_lims_climograph(ref_height):
temp_0_data = []
temp_1_data = []
prcp_0_data = []
prcp_1_data = []
for month_name, month_nr in mb_models[0].months_short.items():
mb_model = mb_models[0]
temp, tempformelt, prcp_total, prcp_sol = \
mb_model.get_monthly_climate(
heights=[ref_height], year=month_nr/12)
temp_0_data = np.append(temp_0_data, temp)
prcp_0_data = np.append(
prcp_0_data, prcp_total / mb_model.mbmod._prcp_fac)
mb_model = mb_models[1]
temp, tempformelt, prcp_total, prcp_sol = \
mb_model.get_monthly_climate(
heights=[ref_height], year=month_nr/12)
temp_1_data = np.append(temp_1_data, temp)
prcp_1_data = np.append(
prcp_1_data, prcp_total / mb_model.mbmod._prcp_fac)
y_lim_prcp_max = max(np.append(prcp_0_data, prcp_1_data)) * 1.1
y_add_temp_0_range = (max(temp_0_data) - min(temp_0_data)) * 0.05
y_lim_temp_0 = (min(temp_0_data) - y_add_temp_0_range,
max(temp_0_data) + y_add_temp_0_range)
y_add_temp_1_range = (max(temp_1_data) - min(temp_1_data)) * 0.05
y_lim_temp_1 = (min(temp_1_data) - y_add_temp_1_range,
max(temp_1_data) + y_add_temp_1_range)
y_lim_temp = (min([y_lim_temp_0[0], y_lim_temp_1[0]]),
max([y_lim_temp_0[1], y_lim_temp_1[1]]))
return y_lim_temp, y_lim_prcp_max
def get_climographs_comp(change):
ref_height = min(mb_models[0].mbmod.ref_hgt, mb_models[1].mbmod.ref_hgt)
y_lim_temp, y_lim_prcp_max = get_y_lims_climograph(ref_height)
return (mb_models[0].get_climograph(ref_height=ref_height,
y_lim_temp=y_lim_temp,
y_lim_prcp_max=y_lim_prcp_max,
add_MB_nr_title=True,
).opts(toolbar='above',
) +
mb_models[1].get_climograph(ref_height=ref_height,
y_lim_temp=y_lim_temp,
y_lim_prcp_max=y_lim_prcp_max,
add_MB_nr_title=True,
).opts(toolbar='above',
)
)
climograph_comp_updater = pn.widgets.Checkbox(name='Update Climograph comp')
climographs_comp = hv.DynamicMap(
pn.bind(get_climographs_comp,
change=climograph_comp_updater))
def get_table_values(mb_i, include_base_name=False):
if include_base_name:
values = [mb_models[mb_i].base_climate_name]
else:
values = []
values.append(np.round(mb_models[mb_i].temp_bias, 1))
values.append(np.round(mb_models[mb_i].mbmod.grad[0] * 100, 2))
values.append(np.round(mb_models[mb_i].mbmod.prcp_fac, 1))
values.append(mb_models[mb_i].mbmod.mu_star)
values.append(np.round(mb_models[mb_i].mbmod.t_melt, 1))
values.append(np.round(mb_models[mb_i].mbmod.t_solid, 1))
values.append(np.round(mb_models[mb_i].mbmod.t_liq, 1))
return values
def get_info_table(mb_i=0):
variables = ['T_bias', 'T_grad', 'Prcp_fac',
'µ', 'T_melt', 'T_solid', 'T_liquid']
units = ['°C', '°C/100m', ' ',
mu_unit, '°C', '°C', '°C']
values = get_table_values(mb_i, include_base_name=False)
def table_hook(plot, element):
plot.handles['table'].autosize_mode = "none"
plot.handles['table'].columns[0].width = 50
plot.handles['table'].columns[1].width = 40
plot.handles['table'].columns[2].width = 100
return hv.Table({'Variable': variables, 'Value': values, 'Unit': units},
['Variable'], ['Value', 'Unit'],
).opts(index_position=None,
width=190,
height=210,
hooks=[table_hook]
)
# hook for hv.Table do not work correctly when used with DynamicMap (see https://github.com/holoviz/holoviews/issues/4343)
# therefore make a hard refresh in update function ()
info_tables = [pn.Column(pn.layout.VSpacer(),
get_info_table(0),
pn.layout.VSpacer(),
width=190,
sizing_mode='stretch_height'),
pn.Column(pn.layout.VSpacer(),
get_info_table(1),
pn.layout.VSpacer(),
width=190,
sizing_mode='stretch_height')]
def get_compare_table(change):
variables = ['Base Climate', 'T_bias', 'T_grad', 'Prcp_fac',
'µ', 'T_melt', 'T_solid', 'T_liquid']
units = ['', '°C', '°C/100m', ' ',
mu_unit, '°C', '°C', '°C']
values_0 = get_table_values(mb_i=0, include_base_name=True)
values_1 = get_table_values(mb_i=1, include_base_name=True)
def table_hook(plot, element):
plot.handles['table'].sizing_mode = 'stretch_width'
return hv.Table({'Variable': variables,
'Values MB 1': values_0,
'Values MB 2': values_1,
'Unit': units},
['Variable'], ['Values MB 1', 'Values MB 2', 'Unit'],
).opts(index_position=None,
height=230,
hooks=[table_hook])
compare_table_updater = pn.widgets.Checkbox(name='Update Compare table')
compare_mb_table = hv.DynamicMap(
pn.bind(get_compare_table,
change=compare_table_updater))
def set_period_buttons_language():
button_name = base_climate_timeseries_plot_labels['set_period_buttons_name'][language]
set_period_buttons[0].name = button_name
set_period_buttons[1].name = button_name
set_period_buttons = [pn.widgets.Button(width=106,
height=83),
pn.widgets.Button(width=106,
height=83)]
set_period_buttons_language()
def set_new_period(event):
global current_mb_i
if type(mb_models[current_mb_i].timeseries_x_range_handle.start) == np.datetime64:
period_start = pd.to_datetime(
mb_models[current_mb_i].timeseries_x_range_handle.start)
else:
period_start = (pd.to_datetime(pd.DataFrame({'year': [1970],
'month': [1],
'day': [1]})) +
pd.Timedelta(mb_models[current_mb_i].timeseries_x_range_handle.start,
'milliseconds'))[0]
if type(mb_models[current_mb_i].timeseries_x_range_handle.end) == np.datetime64:
period_end = pd.to_datetime(
mb_models[current_mb_i].timeseries_x_range_handle.end)
else:
period_end = (pd.to_datetime(pd.DataFrame({'year': [1970],
'month': [1],
'day': [1]})) +
pd.Timedelta(mb_models[current_mb_i].timeseries_x_range_handle.end,
'milliseconds'))[0]
# depending on hemisphere different start_mth
if mb_models[current_mb_i].mbmod.hemisphere == 'nh':
start_mth = 10
elif mb_models[current_mb_i].mbmod.hemisphere == 'sh':
start_mth = 4
if period_start.month >= start_mth:
y_start_slider.value = period_start.year + 1
else:
y_start_slider.value = period_start.year
change_y_end_slider_start(None)
if period_end.month >= start_mth:
y_end_slider.value = period_end.year + 1
else:
y_end_slider.value = period_end.year
set_climate_button_click(None)
set_period_buttons[0].on_click(set_new_period)
set_period_buttons[1].on_click(set_new_period)
def get_timeseries(mb_i=0):
return mb_models[mb_i].get_climate_timeseries()
timeseries = [pn.Row(get_timeseries(mb_i=0),
sizing_mode='stretch_both'),
pn.Row(get_timeseries(mb_i=1),
sizing_mode='stretch_both')]
def update_current_figures():
if active_tab in [0, 1]:
if not tab_figures_updated[active_tab]:
toogle_figures_loading()
mb_plot_updater_both[active_tab].value = not mb_plot_updater_both[active_tab].value
info_tables[active_tab].objects = [
pn.layout.VSpacer(), get_info_table(mb_i=active_tab), pn.layout.VSpacer()]
tab_figures_updated[active_tab] = True
if not climographs_updated[active_tab]:
climograph_updater_both[active_tab].value = not climograph_updater_both[active_tab].value
timeseries[active_tab].objects = [get_timeseries(mb_i=active_tab)]
climographs_updated[active_tab] = True
toogle_figures_loading()
elif active_tab == 2:
if not tab_figures_updated[active_tab]:
toogle_figures_loading()
compare_table_updater.value = not compare_table_updater.value
mb_comp_plot_updater.value = not mb_comp_plot_updater.value
tab_figures_updated[active_tab] = True
if not climographs_updated[active_tab]:
climograph_comp_updater.value = not climograph_comp_updater.value
climographs_updated[active_tab] = True
toogle_figures_loading()
def get_mb_tab(i):
global mb_models
return pn.Column(pn.Tabs((tab_headings['Current Settings and Climograph'][language],
pn.Row(info_tables[i],
climographs[i],
sizing_mode='stretch_both')),
(tab_headings['Base Climate Timeseries'][language],
pn.Row(timeseries[i],
pn.Column(pn.layout.VSpacer(),
set_period_buttons[i]),
sizing_mode='stretch_both')),
sizing_mode='stretch_both',
tabs_location='above',
dynamic=False
),
pn.layout.Divider(margin=(0, 0, 0, 0)),
pn.Row(mb_figures[i],
sizing_mode='stretch_both'),
sizing_mode='stretch_both')
Buttons to change MBs on compariosion plot
from app_text import compare_tab_text
change_selected_MB_options = []
def change_selected_MB_click(event):
global current_mb_i
if event.new == change_selected_MB_options[0]:
current_mb_i = 0
set_climate_menu_with_selected_mb_model(0)
set_mb_settings_menu_with_selected_mb_model(0)
elif event.new == change_selected_MB_options[1]:
current_mb_i = 1
set_climate_menu_with_selected_mb_model(1)
set_mb_settings_menu_with_selected_mb_model(1)
change_selected_MB = pn.widgets.RadioButtonGroup(name='choose MB to change',
button_type='default')
change_selected_MB.param.watch(change_selected_MB_click, ['value'])
def set_change_selected_MB_RadioButtonGroup_language():
global change_selected_MB_options
change_selected_MB_options = [
compare_tab_text['Activate to change MB-Model'][language] + ' 1',
compare_tab_text['Activate to change MB-Model'][language] + ' 2']
change_selected_MB.options = change_selected_MB_options
set_change_selected_MB_RadioButtonGroup_language()
change_selected_MB.value = compare_tab_text['Activate to change MB-Model'][language] + ' 1'
def get_compare_tab():
return pn.Column(pn.Tabs((tab_headings['Compare Climographs'][language],
pn.Row(climographs_comp,
sizing_mode='stretch_both')),
(tab_headings['Compare MB Settings'][language],
pn.Column(pn.layout.VSpacer(),
compare_mb_table,
pn.layout.VSpacer(),
sizing_mode='stretch_both')),
tabs_location='above'),
change_selected_MB,
pn.layout.Divider(margin=(0, 0, 0, 0)),
pn.Row(compare_mb_figure,
sizing_mode='stretch_both'),
sizing_mode='stretch_both')
def change_figures_tab(event):
global current_mb_i
global active_tab
if event.new == 0:
current_mb_i = 0
active_tab = 0
change_selected_MB.value = change_selected_MB_options[0]
set_climate_menu_with_selected_mb_model(0)
set_mb_settings_menu_with_selected_mb_model(0)
update_current_figures()
elif event.new == 1:
current_mb_i = 1
active_tab = 1
change_selected_MB.value = change_selected_MB_options[1]
set_climate_menu_with_selected_mb_model(1)
set_mb_settings_menu_with_selected_mb_model(1)
update_current_figures()
elif event.new == 2:
active_tab = 2
update_current_figures()
figures_tab = pn.Tabs(('', []),
dynamic=False)
figures_tab.param.watch(change_figures_tab, ['active'])
def set_figures_tab_tabs():
figures_tab.clear()
figures_tab.append((tab_headings['Mass Balance Model'][language] + ' 1',
get_mb_tab(0)))
figures_tab.append((tab_headings['Mass Balance Model'][language] + ' 2',
get_mb_tab(1)))
figures_tab.append((tab_headings['Compare Mass Balance Models'][language],
get_compare_tab()))
set_figures_tab_tabs()
initial_run = False
app = pn.Template(template)
app.add_panel('header', pn.Row(analytics,
pn.layout.HSpacer(),
language_selector,
pn.Spacer(width=10),
pn.pane.Markdown(object='<p style="margin-top: 0px;">' + __version__ + '</p>',
height=20,
margin=(0, 0),
align='end',),
sizing_mode='stretch_width'))
app.add_panel('menu_and_figures', pn.Row(pn.Column(tab_menu,
all_logos,
),
figures_tab,
sizing_mode='stretch_both'))
app.servable(title='Mass Balance Simulator ' + __version__)
As long as you are running this notebook "live" (in Jupyter, not viewing a website or a static copy), the above notebook cell should contain the fully operational dashboard here in the notebook. You can also launch the dashboard at a separate port that shows up in a new browser tab, either by changing .servable() to .show() above and re-executing that cell, or by leaving the cell as it is and running bokeh serve --show simulator.ipynb.