Select Git revision
input_profile_processor.py NaN GiB
"""
MIT License
Copyright (c) 2023 RWTH Aachen University
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
"""
from Model_Library.dynamics import Profile
from Tooling.demand_generator.electrical_demand.ElectricalDemand import ElectricalDemand
from Tooling.demand_generator.thermal_demand.ThermalDemand import ThermalDemand
from Tooling.input_profile_processor.calc_irradiance import generate_g_t_series
from Tooling.modifier import Modifier
from datetime import datetime, timedelta
import pandas as pd
def process_input_profiles(input_profile_dict, t_start, dynamic):
d_step_min = min(dynamic.step_size(index) for index in dynamic.time_steps())
# This entire process assumes four things:
# 1. d_step_min devides a day into an integer amount of steps
# 2. d_step_min devides the intervall from YYYY.MM.DD 00:00:00 of the day containing t_start to t_start into an integer amount of steps
# 3. The timestamps of the desired timeseries either line up the with timestamps provided by the profiles in the files or the other way around
# 4. The sizes of all time steps in the given dynamic are devisible by
input_profiles = {}
for input_profile_name, input_profile_config in input_profile_dict.items():
if 'file' in input_profile_config:
profile = load_profile(input_profile_name, input_profile_config['file'], t_start, dynamic, d_step_min)
elif 'generate' in input_profile_config:
profile = generate_profile(input_profile_name, input_profile_config['type'], input_profile_config['generate'], input_profiles, t_start, dynamic, d_step_min)
elif 'modify' in input_profile_config:
profile = modify_profile(input_profile_name, input_profile_config['type'], input_profile_config['modify'], input_profiles, t_start, dynamic, d_step_min)
input_profiles[input_profile_name] = (input_profile_config['type'], profile)
for input_profile_name, (input_profile_type, input_profile) in input_profiles.items():
input_profile.drop(pd.date_range(input_profile.index[0], t_start - timedelta(hours = d_step_min), freq = str(d_step_min) + 'H'), inplace = True)
input_profile.drop(pd.date_range(t_start + pd.Timedelta(hours = sum(dynamic.step_size(index) for index in dynamic.time_steps())), input_profile.index[-1], freq = str(d_step_min) + 'H'), inplace = True)
if len(input_profile) != dynamic.number_of_steps():
index = 0
drop = []
for index in dynamic.time_steps():
if dynamic.step_size(index) != d_step_min:
number_of_steps = int(dynamic.step_size(index) / d_step_min)
input_profile.iloc[index] = sum(d_step_min * input_profile.iloc[index + i] for i in range(number_of_steps)) / dynamic.step_size(index)
drop.extend(index + i + 1 for i in range(number_of_steps - 1))
index += number_of_steps
else:
index += 1
input_profile.drop((input_profile.index[i] for i in drop), inplace = True)
if input_profile_type == 'irradiance':
lambda_1 = 14.122
lambda_st = 15
phi = 52.21
psi_f = 0
beta = 30
input_profile = generate_g_t_series(input_profile, beta, psi_f, phi, lambda_st, lambda_1, t_start, dynamic)
input_profile = input_profile.squeeze()
input_profile.set_axis(list(range(dynamic.number_of_steps())), inplace = True)
input_profiles[input_profile_name] = Profile(input_profile, dynamic)
return input_profiles
def resample_profile(name, profile, t_start, dynamic, d_step_min, t_last):
profile = profile.resample(str(d_step_min) + 'H').mean().interpolate('linear')
if t_start < profile.index[0]:
missing_indices = pd.date_range(t_start, profile.index[0] - timedelta(hours = d_step_min), freq = str(d_step_min) + 'H')
print(f'For input profile {name} {len(missing_indices)} {"values are" if len(missing_indices) > 1 else "value is"} extrapolated to the beginning of the profile!')
profile = pd.concat([pd.DataFrame([profile.iloc[0]], index = missing_indices), profile])
if profile.index[-1] < t_last:
missing_indices = pd.date_range(profile.index[-1] + timedelta(hours = d_step_min), t_last, freq = str(d_step_min) + 'H')
print(f'For input profile {name} {len(missing_indices)} {"values are" if len(missing_indices) > 1 else "value is"} extrapolated to the end of the profile!')
profile = pd.concat([profile, pd.DataFrame([profile.iloc[-1]], index = missing_indices)])
return profile
def expand_profile_to_year(profile, year, t_step):
if datetime(year, 1, 1, 0) < profile.index[0]:
missing_indices = pd.date_range(datetime(year, 1, 1, 0), profile.index[0] - timedelta(hours = t_step), freq = str(t_step) + 'H')
profile = pd.concat([pd.DataFrame([profile.iloc[0]], index =missing_indices), profile])
if profile.index[-1] < datetime(year + 1, 1, 1, 0) - timedelta(hours = t_step):
missing_indices = pd.date_range(profile.index[-1] + timedelta(hours = t_step), datetime(year + 1, 1, 1, 0) - timedelta(hours = t_step), freq = str(t_step) + 'H')
profile = pd.concat([profile, pd.DataFrame([profile.iloc[-1]], index = missing_indices)])
return profile
def load_profile(name, file, t_start, dynamic, d_step_min):
t_last = t_start + pd.Timedelta(hours = sum(dynamic.step_size(index) for index in dynamic.time_steps()) - 1)
profile = pd.read_csv(file, index_col = 0)
try:
file_start = pd.to_datetime(profile.index[0], format = '%d-%m-%Y %H:%M:%S')
format = '%d-%m-%Y %H:%M:%S'
except ValueError:
format = '%Y-%m-%d %H:%M:%S'
# left_index = 0
# right_index = len(profile) - 1
# middle_index = left_index + int((right_index - left_index) / 2)
# middle_time = pd.to_datetime(profile.index[middle_index], format = format)
# while True:
# if right_index - left_index == 1:
# # We now know that t_start is strictly larger than the time at left_index and smaller or equal than the time at right_index
# # So we have to test if t_start is equal to the time at right_index and return right_index if this is the case
# # Otherwise return left_index
# right_time = pd.to_datetime(profile.index[right_index], format = format)
# if right_time <= t_start:
# first_index = right_index
# else:
# first_index = left_index
# break
# if middle_time < t_start:
# left_index = middle_index
# else:
# right_index = middle_index
# middle_index = left_index + int((right_index - left_index) / 2)
# middle_time = pd.to_datetime(profile.index[middle_index], format = format)
# left_index = 0
# right_index = len(profile) - 1
# middle_index = left_index + int((right_index - left_index) / 2)
# middle_time = pd.to_datetime(profile.index[middle_index], format = format)
# while True:
# if right_index - left_index == 1:
# # We now know that t_last is lager or equal than the time at left_index and strictly smaller than the time at right_index
# # So we have to test if t_last is equal to the time at left_index and return left_index if this is the case
# # Otherwise return right_index
# left_time = pd.to_datetime(profile.index[left_index], format = format)
# if t_last <= left_time:
# last_index = left_index
# else:
# last_index = right_index
# break
# if t_last < middle_time:
# right_index = middle_index
# else:
# left_index = middle_index
# middle_index = left_index + int((right_index - left_index) / 2)
# middle_time = pd.to_datetime(profile.index[middle_index], format = format)
# first_index = 0
# last_index = len(profile) - 1
# profile = profile[first_index:last_index + 1]
profile.index = pd.to_datetime(profile.index, format = format)
return resample_profile(name, profile, t_start, dynamic, d_step_min, t_last)
def generate_profile(name, profile_type, parameters, input_profiles, t_start, dynamic, d_step_min):
t_last = t_start + pd.Timedelta(hours = sum(dynamic.step_size(index) for index in list(dynamic.time_steps())[:-1]))
years = range(t_start.year, t_last.year + 1)
if profile_type == 'elec_demand':
profiles = []
for year in years:
year_profile = ElectricalDemand(year).get_profile(parameters['yearly_demand'], 'h0', True) # True: with smoothing function for household profiles, False: no smoothing function for household profiles
profiles.append(year_profile)
profile = pd.concat(profiles)
return resample_profile(name, profile, t_start, dynamic, d_step_min, t_last)
elif profile_type == 'therm_demand':
profiles = []
for year in years:
demand_df = pd.DataFrame(index=pd.date_range(datetime(year, 1, 1, 0),
periods=8760 / d_step_min,
freq=str(d_step_min) + 'H'))
# Fixme: non-residential building only have building_class = 0
# residential building could varies from 1 to 11
# same problem for hot water demand.
# temporary fix
if len(input_profiles[parameters['temperature']][1]) < len(demand_df.index):
temp = list(input_profiles[parameters['temperature']])
temp[1] = expand_profile_to_year(input_profiles[parameters['temperature']][1], year, d_step_min)
input_profiles[parameters['temperature']] = tuple(temp)
year_profile = ThermalDemand(demand_df.index,
shlp_type='EFH',
temperature=input_profiles[parameters['temperature']][1],
building_class=1,
wind_class=1,
annual_heat_demand=parameters['yearly_demand'],
name='HeatDemand_EFH',
ww_incl=0).get_bdew_profile()
profiles.append(year_profile)
profile = pd.concat(profiles)
return profile
elif profile_type == 'hot_water_demand':
profiles = []
for year in years:
demand_df = pd.DataFrame(index=pd.date_range(datetime(year, 1, 1, 0),
periods=8760 / d_step_min,
freq=str(d_step_min) + 'H'))
# temporary fix
if len(input_profiles[parameters['temperature']][1]) < len(demand_df.index):
temp = list(input_profiles[parameters['temperature']])
temp[1] = expand_profile_to_year(input_profiles[parameters['temperature']][1], year, d_step_min)
input_profiles[parameters['temperature']] = tuple(temp)
year_profile = ThermalDemand(demand_df.index,
shlp_type='EFH',
temperature=input_profiles[parameters['temperature']][1],
building_class=1,
wind_class=1,
annual_heat_demand=parameters['yearly_demand'],
name='DomesticHotWaterDemand_EFH',
ww_incl=1).get_bdew_profile()
profiles.append(year_profile)
profile = pd.concat(profiles)
return profile
else:
raise Exception("Generator for profile type " + str(profile_type) + " is not implemented!")
def modify_profile(name, mod_type: str, parameters, input_profiles, t_start, dynamic, d_step_min):
# Example of the parameters for use in the runme
# ------------------------------------------------------------------------------------------------------------------
# 'temperature_1': ['prophet', 'modify', 'input_files/data/temperature/temperature.csv', 'temperature',
# {"winter1": 0, "spring": 0, "summer": 0,"fall": 0,"winter2": 0},
# "Tooling/Modifier/prophet_models/serialized_model_08032023.json", True, True]
# ------------------------------------------------------------------------------------------------------------------
if mod_type == "prophet":
# t_start : timestamp, e.g.: pd.Timestamp("2021-01-01 00:00:00")
# t_horizon: int, number of days that should be created, for now hardcoded to 365 due to external dependencies
# parameters[]: array of additional parameters, should contain csv_path, season_offset, model_path, new, save.
# For details on the specific parameters look at the documentation of the prophet_based_modification
profile = Modifier.prophet_based_modification(csv_path = parameters[0],
feature_name = parameters[1],
season_offset = parameters[2],
start = t_start,
model_path = parameters[3],
new = parameters[4],
mod_period = 365,
save = parameters[5])
return profile
else:
raise Exception("Modification for mod type " + str(mod_type) + " is not implemented!")