Open and run in Colab (interactive) Edit on Gitlab Open and run in Kaggle (interactive)

How to use iso-probabilistic transformations to obtain weather-corrleated spot markets with a desired distribution

[ ]:
# Install hydesign if needed
import importlib
if not importlib.util.find_spec("hydesign"):
    !pip install git+https://gitlab.windenergy.dtu.dk/TOPFARM/hydesign.git
[1]:
import numpy as np
from numpy import newaxis as na
import pandas as pd
import scipy
from statsmodels.distributions.empirical_distribution import ECDF, monotone_fn_inverter
from hydesign.weather.weather import isoprob_transfrom
from hydesign.examples import examples_filepath
[2]:
inputs = pd.read_csv(examples_filepath+'Europe/GWA2/input_ts_Denmark_good_solar.csv',
                    index_col=0, parse_dates = True)
inputs
[2]:
WS_1 WS_50 WS_100 WS_150 WS_200 WD_1 WD_50 WD_100 WD_150 WD_200 temp_air_1 ghi dni dhi Price
2012-01-01 00:00:00 2.810816 6.582182 7.993129 8.112989 9.236903 166.346357 168.595724 170.888214 173.593446 175.457612 275.686735 0.0 0.0 0.0 31.558
2012-01-01 01:00:00 2.904450 6.810934 8.275190 8.356479 9.566073 169.298921 171.004601 172.747782 175.411415 176.846748 275.846921 0.0 0.0 0.0 31.558
2012-01-01 02:00:00 2.991752 6.987023 8.481921 8.563847 9.907062 170.332390 172.071325 173.846343 176.856068 178.182438 275.908146 0.0 0.0 0.0 31.558
2012-01-01 03:00:00 3.081025 7.153487 8.673896 8.668350 10.113215 172.369415 174.117329 175.900884 179.006964 180.215424 275.985910 0.0 0.0 0.0 31.558
2012-01-01 04:00:00 3.032618 7.087701 8.604443 8.657067 10.226668 176.961308 179.096749 181.275850 185.728569 187.114208 276.139611 0.0 0.0 0.0 31.558
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
2012-12-30 19:00:00 4.346459 9.731132 11.725017 11.833588 13.738749 232.070531 233.096306 234.139569 235.862549 236.381657 276.787589 0.0 0.0 0.0 34.992
2012-12-30 20:00:00 4.351285 9.781253 11.793128 11.924614 13.851638 236.280844 237.063277 237.866713 239.526916 239.919572 276.550026 0.0 0.0 0.0 33.135
2012-12-30 21:00:00 4.131801 9.349829 11.286007 11.395169 13.252576 234.027128 235.111454 236.219895 237.857080 238.418164 276.707764 0.0 0.0 0.0 32.290
2012-12-30 22:00:00 4.226335 9.590430 11.581791 11.705639 13.626405 231.547924 233.116758 234.719448 236.786499 237.542031 276.996122 0.0 0.0 0.0 30.990
2012-12-30 23:00:00 4.142047 9.416042 11.376241 11.520482 13.401961 234.196810 235.697595 237.226551 239.410625 240.209847 277.489206 0.0 0.0 0.0 29.739

8760 rows × 15 columns

1. Sample a target distrubtion, the number of samples can be different between input data and desired distribution

[3]:
#y_desired = np.random.normal(size=int(1e6)) * 5 + 35

y_desired = np.random.weibull(a=2, size=int(1e6)) * 25 + 18
[4]:
y_ISO = isoprob_transfrom(y_input=inputs.Price.values, y_desired=y_desired)
[5]:
from matplotlib import pyplot as plt
[6]:
inputs.Price.hist(bins=100, histtype='step', density=True, label = 'SM input')
plt.hist(y_desired,bins=100, histtype='step', density=True, label = 'Desired PDF')
plt.hist(y_ISO,bins=100, histtype='step', density=True, label = 'SM ISO transformed')
plt.xlabel('SM [EUR/MW.h]')
plt.ylabel('PDF(SM)')
plt.legend()
print()

../_images/notebooks_Example_ISO_prob_8_1.png
[7]:
inputs['SM_ISO'] = y_ISO
[8]:
inputs.loc[:,['Price','SM_ISO']].describe()
[8]:
Price SM_ISO
count 8760.000000 8760.000000
mean 39.597426 40.174787
std 14.988624 11.607516
min 3.946000 19.538660
25% 32.804000 31.435186
50% 35.428500 38.823464
75% 41.028500 47.430262
max 127.491000 123.075336
[9]:
inputs.Price.plot(label = 'SM input')
inputs.SM_ISO.plot(alpha=0.5, label = 'SM ISO transformed')
plt.ylabel('SM [EUR/MW.h]')
plt.xlabel('time')
plt.legend()
print()

../_images/notebooks_Example_ISO_prob_11_1.png
[10]:
inputs.loc[:,['WS_100','Price','SM_ISO']].corr()
[10]:
WS_100 Price SM_ISO
WS_100 1.000000 -0.272191 -0.289774
Price -0.272191 1.000000 0.914459
SM_ISO -0.289774 0.914459 1.000000
[11]:
inputs['ws_bin'] = pd.cut(inputs.WS_100, bins=np.arange(0,20,1))

inputs_grp = inputs.groupby('ws_bin').mean()
C:\Users\mikf\AppData\Local\Temp\ipykernel_25112\3396294946.py:3: FutureWarning: The default of observed=False is deprecated and will be changed to True in a future version of pandas. Pass observed=False to retain current behavior or observed=True to adopt the future default and silence this warning.
  inputs_grp = inputs.groupby('ws_bin').mean()
[12]:

plt.plot(inputs_grp.WS_100.values, inputs_grp.Price.values, label = 'SM input') plt.plot(inputs_grp.WS_100.values, inputs_grp.SM_ISO.values, label = 'SM ISO transformed') plt.legend() plt.ylabel(r'E(SM|WS_10) [EUR/MW.h]') plt.xlabel('WS_10 [m/s]') print()

../_images/notebooks_Example_ISO_prob_14_1.png
[13]:
import scipy.stats as st
def kde_plot(
    x,y,
    ax,
    c='k',
    levels = [1e-5, 5e-5, 1e-4, 5e-4],
    fmt = '%1.0e',
    label = None
    ):
    xmin = np.min(x)
    xmax = np.max(x)
    ymin = np.min(y)
    ymax = np.max(y)

    # Peform the kernel density estimate
    xx, yy = np.mgrid[xmin:xmax:100j, ymin:ymax:100j]
    positions = np.vstack([xx.ravel(), yy.ravel()])
    values = np.vstack([x, y])
    kernel = st.gaussian_kde(values)
    f = np.reshape(kernel(positions).T, xx.shape)

    ax.set_xlim(xmin, xmax)
    ax.set_ylim(ymin, ymax)

    # Contour plot
    cset = ax.contour(xx, yy, f, levels=levels, colors=c)
    if label != None:
        cset.collections[0].set_label(label)
    # iso-prob labels on lines
    ax.clabel(cset, inline=1, fontsize=10, fmt = fmt)
[14]:
fig = plt.figure()
ax = fig.add_subplot(111)
kde_plot(x=inputs.WS_100.values, y=inputs.Price.values, ax=ax, levels = [1e-5, 1e-4, 1e-3], c='C0', label='SM ISO transformed')
kde_plot(x=inputs.WS_100.values, y=inputs.SM_ISO.values, ax=ax, levels = [1e-5, 1e-4, 1e-3], c='C1', label='SM input')
plt.legend()
plt.title('Iso probability countours')
plt.ylabel('SM [EUR/MW.h]')
plt.xlabel('WS_10 [m/s]')
C:\Users\mikf\AppData\Local\Temp\ipykernel_25112\211853886.py:28: MatplotlibDeprecationWarning: The collections attribute was deprecated in Matplotlib 3.8 and will be removed in 3.10.
  cset.collections[0].set_label(label)
C:\Users\mikf\AppData\Local\Temp\ipykernel_25112\3444667699.py:5: UserWarning: No artists with labels found to put in legend.  Note that artists whose label start with an underscore are ignored when legend() is called with no argument.
  plt.legend()
[14]:
Text(0.5, 0, 'WS_10 [m/s]')
../_images/notebooks_Example_ISO_prob_16_2.png