Source code for rws_py.load_curve_simple

"""

Generate simple energy load curves for analysis of weather data

Either as step function or as sine, differences for weekdays and weekends
included.

B. Atakan, Uni Duisburg-Essen, Germany

SPP2403

2025-01-20

"""

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

[docs] def generate_seasonal_modulation(time_index, amplitude=0.05): """ Generate a seasonal modulation function using a cosine wave. Parameters ---------- time_index : pandas DateTimeIndex The period for which values are generated. amplitude : float, optional The amplitude of the seasonal variation. Default is 0.05. Returns ------- pandas Series Seasonal modulation factor for each timestamp. """ # Umwandlung der Zeitstempel in den Tagesindex innerhalb eines Jahres (0 bis 364 oder 365) day_of_year = time_index.day_of_year # Kosinusfunktion für die jahreszeitliche Modulation seasonal_factor = 1 + amplitude * np.cos(2 * np.pi * day_of_year / 365) return pd.Series(seasonal_factor, index=time_index)
[docs] def generate_load_curve(time_index, step=True, load_parameters=None): """ Generate simple load curves either step function or sine, with an additional seasonal modulation. Parameters ---------- time_index : pandas DateTimeIndex The period for which values are generated. step : Boolean, optional If True, it is a step function, else a sine. Default is True. load_parameters : dictionary, optional Values for min/max and weekend/workdays and intermediate values. Returns ------- pandas dataframe With the load values. """ if load_parameters is None: load_parameters = { 'min_weekday': 0.650, 'max_weekday': 1.0, 'min_weekend': 0.650, 'max_weekend': 0.85, 'intermediate_fraction': 0.80, 'multiplier': 1.0, 'offset': 0.0, 'normalize': True, 'amplitude_season':0.05, # seasonal } load_curve = pd.DataFrame(index=time_index) load_curve['load'] = 0.0 def generate_sinusoidal_load(t, wert_min, wert_max): amplitude = (wert_max - wert_min) / 2 offset = wert_min + amplitude phase_shift = 6 return offset + amplitude * np.sin((2 * np.pi / 24) * (t - phase_shift)) for timestamp in load_curve.index: hour = timestamp.hour is_weekend = timestamp.weekday() >= 5 if is_weekend: if step: if hour < 6: load_curve.at[timestamp, 'load'] = load_parameters['min_weekend'] elif hour < 7: load_curve.at[timestamp, 'load'] = load_parameters['intermediate_fraction'] elif hour < 22: load_curve.at[timestamp, 'load'] = load_parameters['max_weekend'] elif hour < 23: load_curve.at[timestamp, 'load'] = load_parameters['intermediate_fraction'] else: load_curve.at[timestamp, 'load'] = load_parameters['min_weekend'] else: load_curve.at[timestamp, 'load'] = generate_sinusoidal_load( hour, load_parameters['min_weekend'], load_parameters['max_weekend'] ) else: if step: if hour < 6: load_curve.at[timestamp, 'load'] = load_parameters['min_weekday'] elif hour < 7: load_curve.at[timestamp, 'load'] = load_parameters['intermediate_fraction'] elif hour < 19: load_curve.at[timestamp, 'load'] = load_parameters['max_weekday'] elif hour < 22: load_curve.at[timestamp, 'load'] = load_parameters['intermediate_fraction'] else: load_curve.at[timestamp, 'load'] = load_parameters['min_weekday'] else: load_curve.at[timestamp, 'load'] = generate_sinusoidal_load( hour, load_parameters['min_weekday'], load_parameters['max_weekday'] ) # Offset und Skalierung load_curve['load'] -= load_parameters['offset'] load_curve['load'] *= load_parameters['multiplier'] # Normierung (falls gewünscht) l_mean = load_curve['load'].mean() if load_parameters['normalize']: load_curve["load not normalized"] = load_curve['load'] load_curve['load'] /= l_mean if load_parameters['multiplier']!=1.0: print('WARNING, load-multiplier neglected, due to normalization!') # Saisonale Modulation load_curve['seasonal_factor'] = generate_seasonal_modulation(time_index, amplitude=load_parameters["amplitude_season"]) load_curve['load'] *= load_curve['seasonal_factor'] load_curve['load'] = load_curve['load'] /load_curve['load'].mean() return load_curve
def _generate_sinusoidal_load(t, wert_min, wert_max): """ Generiert einen sinusförmigen Verlauf mit einer Periode von 24 Stunden, einer Phasenverschiebung von 6 Stunden, sowie einem bestimmten minimalen und maximalen Wert. :param t: Array von Zeitpunkten (in Stunden), von 0 bis 24. :param wert_min: Minimalwert der Lastkurve. :param wert_max: Maximalwert der Lastkurve. :return: Sinusförmige Lastkurve. """ # Definiere die Amplitude und den Offset amplitude = (wert_max - wert_min) / 2 offset = wert_min + amplitude # Phasenverschiebung von 6 Stunden für die Sinusfunktion phase_shift = 6 # Generiere die sinusförmige Lastkurve load_curve = offset + amplitude * \ np.sin((2 * np.pi / 24) * (t - phase_shift)) return load_curve if __name__ == '__main__': time_ind = pd.date_range(start='2021-01-01', end='2022-12-31', freq="h") # Beispielaufruf der Funktion load_params = { 'min_weekday': 0.650, 'max_weekday': 1.0, 'min_weekend': 0.650, 'max_weekend': 0.85, 'intermediate_fraction': 0.80, 'multiplier': 50.0, 'offset':0.0, 'normalize': True, 'amplitude_season':0.05, # seasonal } # For step function load_curve_step = generate_load_curve(time_ind, step=True, load_parameters=load_params) # For sinusoidal function load_curve_sine = generate_load_curve(time_ind, step=False, load_parameters=load_params) # Plot der ersten zwei Wochen N_H = 24*14 plt.figure(figsize=(12, 6)) plt.plot(load_curve_step.index[:N_H], load_curve_step['load'][:N_H], label='Stufenfunktion', color='blue') # N_H Stunden = 14 Tage plt.plot(load_curve_sine.index[:N_H], load_curve_sine['load'] [:N_H], label='Sinusfunktion', color='orange') plt.title('Lastkurve: Stufenfunktion vs. Sinusfunktion (Erste zwei Wochen)') plt.xlabel('Datum') plt.ylabel('Last (Skaliert)') plt.grid() plt.legend() plt.show() int_step = load_curve_step["load"].sum() int_sine = load_curve_sine["load"].sum() print(f"Sum/Integral, sine: {int_sine}, setep: {int_step}") print("step:\n", load_curve_step.describe()) print("sine:\n", load_curve_sine.describe())