"""
HotWaterDemand.py
A. Molar-Cruz @ TUM ENS
"""
import numpy as np
[docs]
class HotWaterDemand:
# --------------------------------------------------------------------------------
def __init__(self, dt_vector, resolution,
day_vector, seasonal_vector, activity_vector,
Tw, daily_DHW, dhw_prob,
hw_tank_capacity, hw_tank_limit, hw_tank_volume_t0, hw_flow,
result_dir, use, year_class, btype, bid,
debug, save_debug):
"""
HotWaterDemand class constructor
Args:
dt_vector: Vector with datetime objects
resolution: Resolution in min
day_vector: Vector with day of year in simulation time frame
seasonal_vector: Vector with seasonal variation
activity_vector: Vector with building activity
Tw: Supply temperature of water in degC
daily_DHW: Mean daily hot water consumption [m3]
dhw_prob: DWH-loads probability
hw_tank_capacity: Hot water tank capacity in m3
hw_tank_limit: Hot water tank limit in percentage
hw_tank_volume_t0: State of hot water tank in m3
hw_flow: Flow to refill hot water tank in L/min
result_dir: Result directory
use: Use of building
year_class: Year class
btype: Building type
bid: Building id
debug: Level of debug
save_debug: Whether to save debug files
"""
# Mean daily consumption
self.daily_DHW = daily_DHW # Mean daily hot water consumption [m3]
# Hot water tank
self.hw_tank_capacity = hw_tank_capacity # Hot water tank capacity in m3
self.hw_tank_limit = hw_tank_limit * self.hw_tank_capacity # Hot water tank limit in m3
self.hw_tank_volume_t0 = hw_tank_volume_t0 # Initial state of hot water tank in m3
self.hw_tank_volume = self.hw_tank_volume_t0 # State of hot water tank in m3
self.hw_flow = hw_flow # Flow to refill hot water tank in L/min
# Hot Water values
self.Tw = Tw # Supply temperature of water in degC
self.density = 1000 # Water density in kg/m3
self.c_p = 4186 # Heat capacity of water in J/(kg degC)
# DHW-loads probabilities
self.flow_rate_cdf = dhw_prob[3] # CDF of dhw-load flow rate
self.duration_cdf = dhw_prob[4] # CDF of dhw-load duration
self.prob_daytime = dhw_prob[1] # Probability of dhw-load happening at specific time of day
self.prob_weekday = dhw_prob[2] # Probability factor of dhw-load for dif days of the weekday
# Simulation time frame
self.dt_vector = dt_vector # Vector of time steps as datetime objects
self.nts = len(self.dt_vector) # Number of time steps
self.resolution = resolution # resolution in min
self.day_vector = day_vector # Vector with day of year in simulaiton time frame
self.seasonal_vector = seasonal_vector # Sinusoidal function for seasonal variation of dhw consumption
self.activity_vector = activity_vector # Building activity vector (boolean)
# Results
self.dhw_m3 = np.zeros([self.nts]) # Instantaneous DHW demand in m3
self.dhw_tank_m3 = np.zeros([self.nts]) # Hot water tank demand in m3
self.dhw_power = np.zeros([self.nts]) # Domestic hot water demand in W
self.dhw_debug = np.zeros([self.nts, 25]) # Domestic hot water debug data per time step
#
self.debug = debug
self.save_debug = save_debug
###
self.agg_consumption = np.zeros([5]) # Aggregated consumption
# Hot water demand
# --------------------------------------------------------------------------------
[docs]
def calculate(self):
"""
Calculates time series of domestic hot water demand in m3/min and W for the
four dhw-load categories (0. Shower, 1. Bath, 2. Medium load and 3. Small load)
for the whole simulation time.
"""
if self.debug == 3:
print(' ' + '= HOT WATER DEMAND')
print(' ' + '====================================================================================='
'=====================')
# calculate flow rate in m3
for iii, day_info in enumerate(self.day_vector):
day_in_year = day_info[0]
start = day_info[1]
end = day_info[2]
# check if building is occupied
occupied = sum(self.activity_vector[start:end + 1])
# if building is occupied, create dhw demand events
if occupied > 0:
# initialize day consumption, consumption limit and flag
self.daily_limit = self.daily_DHW * self.seasonal_vector[day_in_year - 1]
self.daily_consumption_reached = False
self.daily_consumption = 0.
# add day data to debug matrix
if self.save_debug:
self.dhw_debug[start:end + 1, 0] = range(start, end + 1)
self.dhw_debug[start:end + 1, 2] = iii
self.dhw_debug[start:end + 1, 3] = day_in_year
self.dhw_debug[start:end + 1, 4] = self.daily_DHW
self.dhw_debug[start:end + 1, 5] = self.daily_limit
#
if self.debug == 3:
print(' ' + 'day: {}\tDay_in_year: {}\tMean_load: {:.3f}\tSeasonal_factor: {:.3f}\t'
'Daily_limit [m3]: {:.3f}\HW Tank [m3]: {:.3f}'.format(iii,
day_in_year,
self.daily_DHW,
self.seasonal_vector[
day_in_year - 1],
self.daily_limit,
self.hw_tank_volume))
# Loop until daily consumption is reached
loop_counter = 1
while not self.daily_consumption_reached:
#
if self.debug == 3:
print(' ' + '----------------------------------------')
print(' ' + 'Loop\tLimit\tConsumption\tTankState\tReached?')
print(' ' + '{}\t{:.3f}\t{:.3f}\t\t{:.3f}\t\t{}'.format(loop_counter, self.daily_limit,
self.daily_consumption,
self.hw_tank_volume / self.hw_tank_capacity,
self.daily_consumption_reached))
print(
' ' + '____________________________________________________________________________________________________')
print(
' ' + 'time_step\t\tActive?\tprob\trndnum\tload\tevent?\tflow\tdur\tdur_ts\tcons\t\tagg.cons')
# Loop through timesteps in day
for jjj, time_step in enumerate(self.dt_vector[start:end + 1]):
# Time step info
if self.save_debug:
self.dhw_debug[start + jjj][1] = time_step.hour + time_step.minute / 60.0
# calculate dhw-events for every time step
self.calculate_dhw_consumption_in_time_step(time_step, start + jjj, iii, day_in_year)
if self.daily_consumption_reached:
break
loop_counter += 1
# calculate convergence of daily flow [m3/day]
if self.save_debug:
self.dhw_debug[start:end + 1, 24] = self.agg_consumption[4] / day_in_year
# calculate power consumption in W
# power [W] = flow_rate [m3/min] * duration [min] * density [kg/m3] * c_p [J/(kg degC)] * delta_temperature_water [degC]
Tw_cold = 6 # degC
Tw_hot = self.Tw
dTw = Tw_hot - Tw_cold
for iii, date in enumerate(self.dt_vector):
# self.dhw_power[iii] = self.dhw_m3[iii] * self.density * self.c_p * dTw / (60 * self.resolution)
self.dhw_power[iii] = self.dhw_tank_m3[iii] * self.density * self.c_p * dTw / (60 * self.resolution)
#
if self.debug == 3:
print(
' ' + '==========================================================================================================\n')
#
[docs]
def calculate_dhw_consumption_in_time_step(self, time_step, dt_vector_index, day, day_in_year):
"""
Calculates hot water consumption events in every time step for the load categories:
0 shower
1 bath
2 medium load
3 small load
Args:
time_step time step as datetime object
dt_vector_index index of time step in simulation time frame dt_vector
day number of day in day_vector
day_in_year number of day in year (1-366)
"""
# check if building is active
if self.activity_vector[dt_vector_index]:
# add occupancy to debug matrix
if self.save_debug:
self.dhw_debug[dt_vector_index][6] = 1
# Loop for dhw-load categories
load_order = [0, 3, 2, 1] # shower, small, medium, bath
for load in load_order:
# get time step information
hour = time_step.hour + time_step.minute / 60.0 # Float value [0,24)
weekday = time_step.weekday() # Integer value [0: Monday, 6: Sunday]
# calculate probability of dhw-load event happening
# Value is scaled down with the weekday factor because the value stored in prob_daytime
# corresponds to Sunday, the day with the highest probability of water usage.
prob_event = self.prob_daytime[load](hour) * self.prob_weekday[load][weekday] / self.prob_weekday[load][6]
# calculate if dhw consumption event is taking place
rand_num_e = np.random.uniform(0, 1, 1)[0]
if rand_num_e <= prob_event:
# calculate event flow rate in m3/min
rand_num = np.random.uniform(0, 1, 1)
flow_rate = self.flow_rate_cdf[load](rand_num)[0] / 1000
# calculate event duration in min
rand_num = np.random.uniform(0, 1, 1)
duration = self.duration_cdf[load](rand_num)[0]
if duration > 0:
# calculate event hot water consumption in m3
consumption = flow_rate * duration
### scale-up consumption to reduce number of loops
min_share = 0.1 # min share of daily limit
consumption = max(consumption, self.daily_limit * min_share)
# calculate event duration in time_steps
duration_in_time_steps = int(np.ceil(duration / self.resolution))
# calculate volume per time_step
volume_time_step = consumption / duration_in_time_steps
# add event consumption to time series
self.dhw_m3[dt_vector_index:dt_vector_index + duration_in_time_steps] += volume_time_step
# add event consumption to daily hot water consumption
self.daily_consumption += consumption
self.agg_consumption[load] += consumption
self.agg_consumption[4] += consumption
# add event to debug matrix
if self.save_debug:
self.dhw_debug[dt_vector_index, 7 + load] = 1
self.dhw_debug[dt_vector_index, 11 + load] = flow_rate
self.dhw_debug[dt_vector_index, 15 + load] = duration
self.dhw_debug[dt_vector_index:dt_vector_index + duration_in_time_steps,
19 + load] = volume_time_step
# subtract volume from hot water tank
self.hw_tank_volume -= consumption
# if tank has reached limit, refill tank
if self.hw_tank_volume <= self.hw_tank_limit:
# calculate volume to refill per time step
volume_to_refill = self.hw_tank_capacity - self.hw_tank_volume
dur_refill_in_time_steps = int(
np.ceil(volume_to_refill / self.hw_flow * (self.resolution / 1000.)))
vol_refill_time_step = volume_to_refill / dur_refill_in_time_steps
# add refill event to time series
self.dhw_tank_m3[
dt_vector_index:dt_vector_index + dur_refill_in_time_steps] += vol_refill_time_step
# set hw_tank_volume to 100% capacity
self.hw_tank_volume = self.hw_tank_capacity
#
if self.debug == 3:
print(
' ' + '{}\t{}\t{:.3f}\t{:.3f}\t{}\t{}\t{:.3f}\t{:.3f}\t{}\t{:.3f}\t\t{:.3f}'.format(
time_step, 1, prob_event, rand_num_e, load, 1, flow_rate, duration,
duration_in_time_steps, consumption, self.daily_consumption))
# check daily limit (80% of nominal load)
if self.daily_consumption >= self.daily_limit * 0.8:
self.daily_consumption_reached = True
#
if self.debug == 3:
print(
' ' + '______________________________________________________________________________________________________')
print(' ' + '*** Daily consumption reached: {:.3f} >= {:.3f}***\n'.format(
self.daily_consumption, self.daily_limit))
# No dhw-load event because rand_num_e > prob_event
else:
if self.debug == 3:
print(
' ' + '{}\t{}\t{:.3f}\t{:.3f}\t{}\t{}\t{}\t{}\t{}\t{}\t\t{}'.format(time_step, 1,
prob_event,
rand_num_e,
load, 0, 0,
0, 0, 0, 0))
if self.daily_consumption_reached:
if self.save_debug:
self.dhw_debug[dt_vector_index, 23] = self.daily_consumption
break
else:
#
if self.debug == 3:
print(
' ' + '{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t\t{}'.format(time_step, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0))