diff --git a/src/mlair/time_series.py b/src/mlair/time_series.py index 8c9986423d47c3c042da132b9ebc0bac1ecd5a3b..149b27684696b6b7fc05fe36c66436246e8958f7 100644 --- a/src/mlair/time_series.py +++ b/src/mlair/time_series.py @@ -4,17 +4,22 @@ import xarray as xr import pandas as pd import datetime as dt import pages.dashboard_translations as guitr +import pages.dashboard_constants as consts from toarstats.metrics.interface import calculate_statistics -def plot(station, language_id=0, species=0, start_date = "2018-07-18 00:00", forecast_length = 4, statistics = "dma8eu", input_dir="static/data/MLAIR", output_dir=None, figsize=(6, 3)): - # get this from outside!!! - units = "ppb" +def plot(station, language_id=0, ispec=0, start_date = "2018-07-18 00:00", forecast_length = 4, statistics = "dma8eu", input_dir="static/data/MLAIR", output_dir=None, figsize=(6, 3)): - # still needed: translation table from indices to MLAIR abbreviations of species - # now a quick hack: - if species == 0: - species = "O3" + # for our demonstrator, all units should be µg m-3! + # for future use cases you might better get this from outside + given_units = "ppb" + units = wanted_units = "µg m-3" + + species = guitr.species_options[0][ispec] + ml_species = consts.ml_names[species] + if given_units != wanted_units: + conv_factor = consts.conversion_factor[species] + species = guitr.species_options[language_id][ispec] # prepare input and output station_code, station_label = station.split(',') @@ -28,17 +33,17 @@ def plot(station, language_id=0, species=0, start_date = "2018-07-18 00:00", for d4_fc = dataset['__xarray_dataarray_variable__'].sel(index=start_date,type='nn').data[:forecast_length] # plot the values at the middle of the day --> offset 12:00 d4_fc_times = [(dt.datetime.strptime(start_date, guitr.date_format2[0][:8]) + dt.timedelta(days=i)).strftime(f'{guitr.date_format2[language_id][:8]} 12:00') for i in range(forecast_length)] - df_fc = pd.DataFrame({'fc_time': d4_fc_times, 'value': d4_fc}) + df_fc = pd.DataFrame({'fc_time': d4_fc_times, 'value': d4_fc*conv_factor}) # plot the values at the middle of the day --> give an hourly period df_fc['fc_time'] = pd.to_datetime(df_fc['fc_time'], dayfirst=True).dt.to_period('h') file_s = f"{input_dir}/{station_code}_no_no2_o3.nc" dataset = xr.open_dataset(file_s) - d4_hourly = dataset['__xarray_dataarray_variable__'].sel(datetime=slice(hourly_start_date,hourly_end_date),variables=species.lower()).data[0] + d4_hourly = dataset['__xarray_dataarray_variable__'].sel(datetime=slice(hourly_start_date,hourly_end_date),variables=ml_species.lower()).data[0] d4_hourly_times = dataset['datetime'].sel(datetime=slice(hourly_start_date,hourly_end_date)).data - df_hourly = pd.DataFrame({'hourly_time': d4_hourly_times, 'value': d4_hourly}) + df_hourly = pd.DataFrame({'hourly_time': d4_hourly_times, 'value': d4_hourly*conv_factor}) df_hourly['hourly_time'] = pd.to_datetime(df_hourly['hourly_time']) fc_length_str = "{}{}{}".format(forecast_length, guitr.day_label[language_id], guitr.day_plural_label[language_id] if forecast_length > 1 else "") - output_file_name = f"mlair_{species}_{station_code}_{start_date}_{fc_length_str}.png" + output_file_name = f"mlair_{ml_species}_{station_code}_{start_date}_{fc_length_str}.png" df_hourly_with_index = df_hourly.set_index("hourly_time") dma8eu = calculate_statistics("daily", statistics, df_hourly_with_index) dma8eu.index = dma8eu.index + pd.Timedelta('12 hour') @@ -48,14 +53,21 @@ def plot(station, language_id=0, species=0, start_date = "2018-07-18 00:00", for # plot plt.figure(figsize=figsize) #creates new figure ax = df_hourly.plot.line(x="hourly_time", y= "value", marker="o", label=f'{guitr.mlair_legend_obs_label[language_id]}', color='lightGrey') #plot observations - df_ob.plot.line(x="ob_time", y="ob_value", linestyle=" ", marker="o", ax=ax, label=f"{guitr.mlair_legend_obs_label[language_id]} [{statistics}]", color='blue') #plot observations with dma8eu - df_fc.plot.line(x="fc_time", y="value", linestyle=" ", marker="o", ax=ax, label=f'{guitr.mlair_legend_fc_label[language_id]} [{statistics}]', color='orange') #plot fc + ax.errorbar(x=df_ob["ob_time"], y=df_ob["ob_value"], xerr=pd.Timedelta(hours=4), fmt="none", elinewidth=2.0, label=f"{guitr.mlair_legend_obs_label[language_id]} [{statistics}]", color='blue') #plot observations with dma8eu + dummy = ax.legend() + ax.errorbar(x=df_fc["fc_time"], y=df_fc["value"], xerr=pd.Timedelta(hours=4), fmt="none", elinewidth=2.0, label=f'{guitr.mlair_legend_fc_label[language_id]} [{statistics}]', color='orange') #plot fc + dummy = ax.legend() + logging.error(f'{df_hourly["hourly_time"][0]=}') + logging.error(f'{df_fc["fc_time"]=}') + # extend x axis since last forecast is not plotted + nticks = 2 * forecast_length + 1 + ax.set_xlim(df_hourly["hourly_time"][0], df_fc["fc_time"][forecast_length-1] + pd.Timedelta(hours=12)) plt.title(f'{species}: {station_label}') plt.xlabel(f'{guitr.date_label[language_id]}') locs, labels = plt.xticks() #get location and label of x-Axis - xticks = [locs[0] + i * 24 for i in range(8)] #set location of labels + xticks = [locs[0] + i * 24 for i in range(nticks)] #set location of labels xlabels = [(dt.datetime.strptime(hourly_start_date, guitr.date_format2[language_id][:8]) - + dt.timedelta(days=i)).strftime(guitr.date_format2[language_id][:8]) for i in range(8)] #set labels + + dt.timedelta(days=i)).strftime(guitr.date_format2[language_id][:8]) for i in range(nticks)] #set labels plt.xticks(xticks, xlabels, rotation=45) #apply location and labels of x-Axis plt.ylabel(f'{guitr.mlair_conc_label[language_id]} ({units})') ax.grid(True) #adds grids to plot diff --git a/src/pages/dashboard.py b/src/pages/dashboard.py index 22183a0cda5d31d2c3a101390d06e65391d488c1..c7338918877e879a4852e8dae3283af57395adae 100644 --- a/src/pages/dashboard.py +++ b/src/pages/dashboard.py @@ -234,7 +234,7 @@ def generate_ml_fcast_output_body(language_id): return [ dbc.Row(dbc.Label(f"{guitr.start_date_label[language_id]}: 18.07.2018, Ozon, Nordrhein Westfalen")), dbc.Row([ - dbc.Col(dbc.Label("station:"), width=3), + dbc.Col(dbc.Label(f"{guitr.station_label[language_id]}"), width=3), dbc.Col( dcc.Dropdown(id="station-dropdown-ml-output", value=stations_list[0], @@ -244,12 +244,10 @@ def generate_ml_fcast_output_body(language_id): } for i in range(len(stations_list))], ), width=6 ), - dbc.Col(dbc.Button(f"{guitr.map_select_label[language_id]}", class_name="fzj_style_btn"), width=2) + dbc.Col(dbc.Button(f"{guitr.map_select_label[language_id]}", disabled=True, class_name="fzj_style_btn"), width=2) ], class_name="row mt-3"), dbc.Row([ dbc.Col(html.Div(id='image-container-ml-output'), width=12), - dbc.Col(dbc.Button(f"{guitr.save_label[language_id]}", class_name="fzj_style_btn"), width=6), - dbc.Col(html.Td([])), dbc.Col(dbc.Button(f"{guitr.download_label[language_id]}", class_name="fzj_style_btn"), width=6) ], class_name="row mt-3"), ] @@ -269,7 +267,7 @@ def update_mlair_image(selected_station, users_dict, job_dict): language_id = 0 job_props = get_db_job_entry(jobid) image_path = plot_ml_time_series(selected_station, language_id, - species=job_props['species'], + ispec=job_props['species'], start_date=job_props['start_date'], forecast_length=job_props['forecast_length']) image = html.Img(src=image_path, className='image-fit-container') @@ -426,8 +424,6 @@ def generate_eurad_im_output_body(language_id, context, jobnr): dbc.Row([ dbc.Col(html.Div(id='image-container-{}'.format(context)), width=6), dbc.Col([html.Div(id='image-container-timeseries-{}'.format(context)), - dbc.Col(dbc.Button(f"{guitr.save_label[language_id]}", class_name="fzj_style_btn"), width=6), - dbc.Col(html.Br()), html.Div([dbc.Button(f"{guitr.download_label[language_id]}", id="eurad_im_output_download", class_name="fzj_style_btn"), dcc.Download(id="eurad_im_download_result")]), @@ -530,8 +526,6 @@ def generate_eurad_scen_output_body(language_id, context, jobnr): dbc.Row([ dbc.Col(html.Div(id='image-container-{}'.format(context)), width=6), dbc.Col([html.Div(id='image-container-timeseries-{}'.format(context)), - dbc.Col(dbc.Button(f"{guitr.save_label[language_id]}", class_name="fzj_style_btn"), width=6), - dbc.Col(html.Br()), html.Div([dbc.Button(f"{guitr.download_label[language_id]}", id="eurad_scen_output_download", class_name="fzj_style_btn"), dcc.Download(id="eurad_scen_download_result")]), diff --git a/src/pages/dashboard_constants.py b/src/pages/dashboard_constants.py index a32e8e7382affde6f2d6ac8e33c1ad9b581b6d87..64897b62782954f2059965ebe208c497a96a492b 100644 --- a/src/pages/dashboard_constants.py +++ b/src/pages/dashboard_constants.py @@ -3,3 +3,28 @@ import datetime as dt min_date_allowed=dt.date(2017, 1, 18) max_date_allowed=dt.date(2018, 8, 10) initial_visible_month=dt.date(2017, 1, 1) + +# Explanation of conversion from concentration to mixing ratio: +# We assume that data are reported in concentration "at standard +# conditions", and we take "standard conditions" to mean 20 degC and 1013.25 hPa. +# The density of air under these conditions is 1.2041 kg m-3. +# The mole fraction of a gas X is given by mu = c(X)/dens * mw(air)/mw(X), +# hence for ozone for example, you will get +# mu = c(X)/1.2041 * 28.97 / 48.0 = c(X) * 0.50124. Note that µg m-3 is 10^9 +# times kg m-3, thus this conversion factor directly converts from µg m-3 +# to nmol mol-1. +# Useful tools: - Air density calculator (http://www.gribble.org/cycling/air_density.html) +# To use the above air density calculator, you have to set "Dew point (°C) to -45! +# You can easily calculate the air density with this formula dens(air) = p/(RT) +# p: pressure in Pa +# T: temperature in K +# R: specific gas constant (287.058 J/kg*K) +# Molecular Mass Calculator (http://www.bmrb.wisc.edu/metabolomics/mol_mass.php) +conversion_factor = { "NO": 0.80182, + "NO2": 0.52297, + "ozone": 0.50124, + "PM2.5": 1.0 } +ml_names = { "NO": "NO", + "NO2": "NO2", + "ozone": "O3", + "PM2.5": "PM2P5" } diff --git a/src/pages/dashboard_translations.py b/src/pages/dashboard_translations.py index ef64b51ca447a6b2ccbb387f94783c2cf6333a1b..5975d77670457a00b0d70646cad1637ecc2c7188 100644 --- a/src/pages/dashboard_translations.py +++ b/src/pages/dashboard_translations.py @@ -106,6 +106,7 @@ output_format_options = [["raw model output", "2D plots", "time series"], ["Rohmodellausgabe", "2D-Diagramme", "Zeitreihen"]] state_label = ["state:", "Bundesland:"] species_label = ["species:", "Spezies:"] +station_label = ["station:", "Station:"] output_metrics_label = ["output metrics:", "Ausgabemetrik:"] species_options = [["ozone", "NO", "NO2", "PM2.5"], ["Ozon", "Stickstoffmonoxid", "Stickstoffdioxid", "Feinstaub (PM2.5)"]] @@ -120,7 +121,6 @@ start_date_label = ["Start date", "Startdatum"] location_label = ["Location", "Ort"] day_label = ["day", "Tag"] day_plural_label = ["s", "e"] -save_label = ["Save Results", "Ergebnisse sichern"] download_label = ["Download Data", "Daten herunterladen"] im_download_label = ["Download Plots", "Plots herunterladen"] downscaling_label = ["Postprocessing with ML-Downscaling", "Postprocessing mit ML-Downscaling"]