Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spillover feature for WashingMachine and Dishwasher #54

Merged
merged 2 commits into from
Feb 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 32 additions & 20 deletions pysimdeum/core/end_use.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ def calculate_discharge(self, discharge, end, duration, intensity, temperature_f
return discharge


def simulate(self, consumption, discharge=None, users=None, ind_enduse=None, pattern_num=1, day_num=0, simulate_discharge=False):
def simulate(self, consumption, discharge=None, users=None, ind_enduse=None, pattern_num=1, day_num=0, total_days=1, simulate_discharge=False, spillover=False):

prob_usage = self.usage_probability().values

Expand Down Expand Up @@ -264,7 +264,7 @@ def calculate_discharge(self, discharge, start, duration, intensity, temperature
return discharge


def simulate(self, consumption, discharge, users=None, ind_enduse=None, pattern_num=1, day_num=0, simulate_discharge=False):
def simulate(self, consumption, discharge, users=None, ind_enduse=None, pattern_num=1, day_num=0, total_days=1, simulate_discharge=False, spillover=False):
prob_usage = self.usage_probability().values

for j, user in enumerate(users):
Expand Down Expand Up @@ -310,19 +310,21 @@ def fct_duration_pattern(self, start=None):
pattern = self.statistics['enduse_pattern']
return pattern

def calculate_discharge(self, discharge, start, j, ind_enduse, pattern_num, end_of_day):
def calculate_discharge(self, discharge, start, j, ind_enduse, pattern_num, day_num, end_of_day, total_days, spillover=False):
discharge_pattern = self.statistics['discharge_pattern']

for time in discharge_pattern[discharge_pattern > 0].index:
discharge_time = start + int(time.total_seconds())
if discharge_time > end_of_day:
discharge = handle_discharge_spillover(discharge, discharge_pattern, time, discharge_time, j, ind_enduse, pattern_num, end_of_day)
if discharge_time > end_of_day and spillover:
discharge = handle_discharge_spillover(discharge, discharge_pattern, time, discharge_time, j, ind_enduse, pattern_num, end_of_day, total_days)
elif ((day_num + 1) == total_days) and (discharge_time > end_of_day):
pass
else:
discharge[discharge_time, j, ind_enduse, pattern_num, 0] = discharge_pattern[time]

return discharge

def simulate(self, consumption, discharge=None, users=None, ind_enduse=None, pattern_num=1, day_num=0, simulate_discharge=False):
def simulate(self, consumption, discharge=None, users=None, ind_enduse=None, pattern_num=1, day_num=0, total_days=1, simulate_discharge=False, spillover=False):

prob_usage = copy.deepcopy(self.statistics['daily_pattern'].values)
freq = self.fct_frequency(numusers=len(users))
Expand Down Expand Up @@ -350,8 +352,12 @@ def simulate(self, consumption, discharge=None, users=None, ind_enduse=None, pat
previous_events.append((start, end))

end_of_day = 24 * 60 * 60 * (day_num + 1)
if end > end_of_day:
consumption = handle_spillover_consumption(consumption, pattern, start, end, j, ind_enduse, pattern_num, end_of_day, self.name)
if end > end_of_day and spillover:
consumption = handle_spillover_consumption(consumption, pattern, start, end, j, ind_enduse, pattern_num, end_of_day, self.name, total_days)
elif ((day_num + 1) == total_days) and (end > end_of_day):
difference = end_of_day - start
consumption[start:end_of_day, j, ind_enduse, pattern_num, 0] = pattern[:difference]
consumption[start:end_of_day, j, ind_enduse, pattern_num, 1] = 0
else:
difference = end - start
consumption[start:end, j, ind_enduse, pattern_num, 0] = pattern[:difference]
Expand All @@ -360,7 +366,7 @@ def simulate(self, consumption, discharge=None, users=None, ind_enduse=None, pat
if simulate_discharge:
if discharge is None:
raise ValueError("Discharge array is None. It must be initialized before being passed to the simulate function.")
discharge = self.calculate_discharge(discharge, start, j, ind_enduse, pattern_num, end_of_day)
discharge = self.calculate_discharge(discharge, start, j, ind_enduse, pattern_num, day_num, end_of_day, total_days, spillover=spillover)

return consumption, (discharge if simulate_discharge else None)

Expand Down Expand Up @@ -414,7 +420,7 @@ def fct_duration_intensity_temperature(self):

return duration, intensity, temperature

def simulate(self, consumption, discharge=None, users=None, ind_enduse=None, pattern_num=1, day_num=0, simulate_discharge=False):
def simulate(self, consumption, discharge=None, users=None, ind_enduse=None, pattern_num=1, day_num=0, total_days=1, simulate_discharge=False, spillover=False):

prob_usage = copy.deepcopy(self.statistics['daily_pattern'].values)

Expand Down Expand Up @@ -478,7 +484,7 @@ def fct_duration_intensity_temperature(self):

return duration, intensity, temperature

def simulate(self, consumption, discharge=None, users=None, ind_enduse=None, pattern_num=1, day_num=0, simulate_discharge=False):
def simulate(self, consumption, discharge=None, users=None, ind_enduse=None, pattern_num=1, day_num=0, total_days=1, simulate_discharge=False, spillover=False):

prob_usage = self.usage_probability().values

Expand Down Expand Up @@ -539,7 +545,7 @@ def fct_duration_intensity_temperature(self, age=None):

return duration, intensity, temperature

def simulate(self, consumption, discharge=None, users=None, ind_enduse=None, pattern_num=1, day_num=0, simulate_discharge=False):
def simulate(self, consumption, discharge=None, users=None, ind_enduse=None, pattern_num=1, day_num=0, total_days=1, simulate_discharge=False, spillover=False):

prob_usage = self.usage_probability().values

Expand Down Expand Up @@ -591,19 +597,21 @@ def fct_duration_pattern(self, start=None):
# duration = pattern.index[-1] - pattern.index[0]
return pattern

def calculate_discharge(self, discharge, start, j, ind_enduse, pattern_num, end_of_day):
def calculate_discharge(self, discharge, start, j, ind_enduse, pattern_num, day_num, end_of_day, total_days, spillover=False):
discharge_pattern = self.statistics['discharge_pattern']

for time in discharge_pattern[discharge_pattern > 0].index:
discharge_time = start + int(time.total_seconds())
if discharge_time > end_of_day:
discharge = handle_discharge_spillover(discharge, discharge_pattern, time, discharge_time, j, ind_enduse, pattern_num, end_of_day)
if discharge_time > end_of_day and spillover:
discharge = handle_discharge_spillover(discharge, discharge_pattern, time, discharge_time, j, ind_enduse, pattern_num, end_of_day, total_days)
elif ((day_num + 1) == total_days) and (discharge_time > end_of_day):
pass
else:
discharge[discharge_time, j, ind_enduse, pattern_num, 0] = discharge_pattern[time]

return discharge

def simulate(self, consumption, discharge=None, users=None, ind_enduse=None, pattern_num=1, day_num=0, simulate_discharge=False):
def simulate(self, consumption, discharge=None, users=None, ind_enduse=None, pattern_num=1, day_num=0, total_days=1, simulate_discharge=False, spillover=False):

prob_usage = copy.deepcopy(self.statistics['daily_pattern'].values)

Expand Down Expand Up @@ -633,8 +641,12 @@ def simulate(self, consumption, discharge=None, users=None, ind_enduse=None, pat
previous_events.append((start, end))

end_of_day = 24 * 60 * 60 * (day_num + 1)
if end > end_of_day:
consumption = handle_spillover_consumption(consumption, pattern, start, end, j, ind_enduse, pattern_num, end_of_day, "WashingMachine")
if end > end_of_day and spillover:
consumption = handle_spillover_consumption(consumption, pattern, start, end, j, ind_enduse, pattern_num, end_of_day, "WashingMachine", total_days)
elif ((day_num + 1) == total_days) and (end > end_of_day):
difference = end_of_day - start
consumption[start:end_of_day, j, ind_enduse, pattern_num, 0] = pattern[:difference]
consumption[start:end_of_day, j, ind_enduse, pattern_num, 1] = 0
else:
difference = end - start
consumption[start:end, j, ind_enduse, pattern_num, 0] = pattern[:difference]
Expand All @@ -643,7 +655,7 @@ def simulate(self, consumption, discharge=None, users=None, ind_enduse=None, pat
if simulate_discharge:
if discharge is None:
raise ValueError("Discharge array is None. It must be initialized before being passed to the simulate function.")
discharge = self.calculate_discharge(discharge, start, j, ind_enduse, pattern_num, end_of_day)
discharge = self.calculate_discharge(discharge, start, j, ind_enduse, pattern_num, day_num, end_of_day, total_days, spillover=spillover)

return consumption, (discharge if simulate_discharge else None)

Expand Down Expand Up @@ -701,7 +713,7 @@ def calculate_discharge(self, discharge, start, duration, intensity, temperature
return discharge


def simulate(self, consumption, discharge=None, users=None, ind_enduse=None, pattern_num=1, day_num=0, simulate_discharge=False):
def simulate(self, consumption, discharge=None, users=None, ind_enduse=None, pattern_num=1, day_num=0, total_days=1, simulate_discharge=False, spillover=False):

prob_usage = self.usage_probability().values

Expand Down
6 changes: 3 additions & 3 deletions pysimdeum/core/house.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ def init_consumption(self):
dims=['time', 'user', 'enduse'])
return self.consumption

def simulate(self, date=None, duration='1 day', num_patterns=1, simulate_discharge=False):
def simulate(self, date=None, duration='1 day', num_patterns=1, simulate_discharge=False, spillover=False):

if date is None:
date = datetime.now().date()
Expand Down Expand Up @@ -296,9 +296,9 @@ def simulate(self, date=None, duration='1 day', num_patterns=1, simulate_dischar
for k, appliance in enumerate(self.appliances):
for day in range(0, number_of_days, 1):
if simulate_discharge:
consumption, discharge = appliance.simulate(consumption, discharge, users=self.users, ind_enduse=k, pattern_num=num, day_num=day, simulate_discharge=simulate_discharge)
consumption, discharge = appliance.simulate(consumption, discharge, users=self.users, ind_enduse=k, pattern_num=num, day_num=day, total_days=number_of_days, simulate_discharge=simulate_discharge, spillover=spillover)
else:
consumption, _ = appliance.simulate(consumption, None, users=self.users, ind_enduse=k, pattern_num=num, day_num=day, simulate_discharge=simulate_discharge)
consumption, _ = appliance.simulate(consumption, None, users=self.users, ind_enduse=k, pattern_num=num, day_num=day, total_days=number_of_days, simulate_discharge=simulate_discharge, spillover=spillover)

if simulate_discharge:
self.consumption = xr.DataArray(data=consumption, coords=[time, users, enduse, patterns, flowtype], dims=['time', 'user', 'enduse', 'patterns', 'flowtypes'])
Expand Down
40 changes: 31 additions & 9 deletions pysimdeum/core/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ def sample_start_time(prob_joint, day_num, duration, previous_events):
if not any((start < event_end and start >= event_start) or (start < event_start and start >= event_start - int(duration)) for event_start, event_end in previous_events):
return start, end

def handle_spillover_consumption(consumption, pattern, start, end, j, ind_enduse, pattern_num, end_of_day, name):
def handle_spillover_consumption(consumption, pattern, start, end, j, ind_enduse, pattern_num, end_of_day, name, total_days):
"""Handles the spillover of consumption events that extend beyond the end of the current day.

Splits the consumption event into two parts: the part that fits within the current days and the part that spills over into the next day. The spillover part is moved to the start of the day, making an assumption that appliance had the same usage event beginning the previous day.
Expand All @@ -138,11 +138,13 @@ def handle_spillover_consumption(consumption, pattern, start, end, j, ind_enduse
ind_enduse (int): The index of the end-use appliance.
pattern_num (int): The pattern number.
end_of_day (int): The end time of the current day in seconds from the beginning of the day.
name(str): The name of the appliance.
total_days (int): The total number of days in the simulation.

Returns:
numpy.ndarray: The updated consumption array with the spillover consumption handled.
"""
print("An event for ", name, " use has spilled over to the next day. Adjusting spillover times...")
print("A usage event for ", name, " use has spilled over to the next day. Adjusting spillover times...")
# Part that fits within the current day
difference = end_of_day - start
consumption[start:end_of_day, j, ind_enduse, pattern_num, 0] = pattern[:difference]
Expand All @@ -151,14 +153,27 @@ def handle_spillover_consumption(consumption, pattern, start, end, j, ind_enduse
# Part that spills over into the next day
spillover_start = 0
spillover_end = end - end_of_day
consumption[spillover_start:spillover_start + spillover_end, j, ind_enduse, pattern_num, 0] = pattern[difference:difference + spillover_end]
consumption[spillover_start:spillover_start + spillover_end, j, ind_enduse, pattern_num, 1] = 0
print("Spillover adjustment complete.")

# Calculate the day index for the spillover
current_day = start // (24 * 60 * 60)
next_day = (current_day + 1) % total_days # if next day exceeds total number of days in the sim, wraps around to the beginning (day 0)

if next_day == 0:
consumption[spillover_start:spillover_start + spillover_end, j, ind_enduse, pattern_num, 0] = pattern[difference:difference + spillover_end]
consumption[spillover_start:spillover_start + spillover_end, j, ind_enduse, pattern_num, 1] = 0
else:
# Continue to the next day
spillover_start = next_day * 24 * 60 * 60
spillover_end = spillover_start + spillover_end
consumption[spillover_start:spillover_end, j, ind_enduse, pattern_num, 0] = pattern[difference:difference + (spillover_end - spillover_start)]
consumption[spillover_start:spillover_end, j, ind_enduse, pattern_num, 1] = 0

print("Spillover consumption adjustment complete.")

return consumption


def handle_discharge_spillover(discharge, discharge_pattern, time, discharge_time, j, ind_enduse, pattern_num, end_of_day):
def handle_discharge_spillover(discharge, discharge_pattern, time, discharge_time, j, ind_enduse, pattern_num, end_of_day, total_days):
"""Handles the spillover of discharge times that occur beyond the end of the current day, making an assumption that appliance had the same usage event beginning the previous day.

This function shifts the discharge time to the start of the day.
Expand All @@ -167,17 +182,24 @@ def handle_discharge_spillover(discharge, discharge_pattern, time, discharge_tim
discharge (numpy.ndarray): The array representing the discharge data.
discharge_pattern (pandas.Series): The pattern of discharge to be applied.
time (int): The time in seconds from the start of the appliance pattern
discharge_time (int): The discharge time in seconds from the beginning of the day.
discharge_time (int): The discharge time in seconds from the beginning of the simulation.
j (int): The index of the user.
ind_enduse (int): The index of the end-use appliance.
pattern_num (int): The pattern number.
end_of_day (int): The end time of the current day in seconds from the beginning of the day.
total_days (int): The total number of days in the simulation.

Returns:
numpy.ndarray: The updated discharge array with the spillover discharge handled.
"""
spillover_time = discharge_time - end_of_day
discharge[spillover_time, j, ind_enduse, pattern_num, 0] = discharge_pattern[time]
if discharge_time >= (total_days * 24 * 60 * 60):
spillover_time = discharge_time - end_of_day
discharge[spillover_time, j, ind_enduse, pattern_num, 0] = discharge_pattern[time]
else:
# Continue to the next day
discharge[discharge_time, j, ind_enduse, pattern_num, 0] = discharge_pattern[time]

#discharge[spillover_time, j, ind_enduse, pattern_num, 0] = discharge_pattern[time]

return discharge

Expand Down