Source code for eureca_building.domestic_hot_water

"""
This module includes functions to model Domestic Hot Water consumptions
"""

__author__ = "Enrico Prataviera"
__credits__ = ["Enrico Prataviera"]
__license__ = "MIT"
__version__ = "0.1"
__maintainer__ = "Enrico Prataviera"

import logging
import math

import numpy as np

from eureca_building.config import CONFIG
from eureca_building.schedule_properties import domestic_hot_water_prop
from eureca_building.fluids_properties import water_properties
from eureca_building.schedule import Schedule
from eureca_building.exceptions import InvalidScheduleType

[docs]def distrEventi(n,x,pdf): """DEPRECATED: Not working Parameters ---------- n x pdf Returns ------- """ y_guess=np.random.rand(int(n)) cdf=np.cumsum(pdf) [cdf,index]=np.unique(cdf,1) x_event=np.interp(y_guess,cdf,x[index]) time_event=np.round(x_event) return time_event
[docs]def dhw_calc_calculation(volume_unit, numunits, time_step): """DEPRECATED: not working Parameters ---------- volume_unit numunits time_step Returns ------- """ total_days = domestic_hot_water_prop["total_days"] nuses = domestic_hot_water_prop["nuses"] vol_aver_drawoff = domestic_hot_water_prop["vol_aver_drawoff"] vol_desv_drawoff = domestic_hot_water_prop["vol_desv_drawoff"] time_aver_drawoff = domestic_hot_water_prop["time_aver_drawoff"] proportion_event = domestic_hot_water_prop["proportion_event"] dist = domestic_hot_water_prop["dist"] vol_total_drawoff = vol_aver_drawoff * time_aver_drawoff vol_total_drawoff_use = np.zeros((nuses)) for j in range(nuses): vol_total_drawoff_use[j] = volume_unit * proportion_event[j] draw_offs = np.zeros((nuses)) draw_offs_dec = np.zeros((nuses)) for j in range(nuses): draw_offs_dec[j] = np.abs(vol_total_drawoff_use[j] / vol_aver_drawoff[j] - int( vol_total_drawoff_use[j] / vol_aver_drawoff[j])) if draw_offs_dec[j] >= 0.5: draw_offs[j] = math.ceil(vol_total_drawoff_use[j] / vol_aver_drawoff[j]) else: draw_offs[j] = np.round(vol_total_drawoff_use[j] / vol_aver_drawoff[j]) time_steps_hour = 60 / time_step time_steps_day = 24 * time_steps_hour array_time_step = np.zeros(4) for i in range(4): array_time_step[i] = time_step n_max = array_time_step / time_aver_drawoff for i in range(len(n_max)): n_max[i] = math.ceil(n_max[i]) Volume_use_daily_array = np.zeros((total_days, int(time_steps_day))) Volume_use_arrayb0 = np.zeros((int(total_days * time_steps_day))) dist_use = np.zeros((24, 12)) Volume_use_array = np.zeros((int(time_steps_day * total_days), nuses)) Volume_use_sum = np.zeros((1, nuses)) Volume_aver_drawoff_final = np.zeros(nuses) Volume_desv_drawoff_final = np.zeros((1, nuses)) Volume_use_time = np.zeros(np.int(time_steps_day)) Volume_use_unit = np.zeros((int(total_days * time_steps_day), numunits)) for units0 in range(numunits): for use in range(nuses): dist_use = np.repeat(dist[:, use], 12, axis=0) dist_use_t = np.reshape(dist_use, (288, 1)) / time_steps_hour vol_aver_drawoff_use = vol_aver_drawoff[use] vol_desv_drawoff_use = vol_desv_drawoff[use] time_aver_drawoff_use = time_aver_drawoff[use] draw_offs_use = int(draw_offs[use]) n_max1 = n_max[use] for day in range(total_days): time_event = distrEventi(draw_offs_use, np.arange(0, time_steps_day), dist_use_t) if use == 0: m = vol_aver_drawoff_use v = vol_desv_drawoff_use mu = np.log((m ** 2) / np.sqrt(v + m ** 2)) sigma = np.sqrt(np.log(v / (m ** 2) + 1)) Flow_rated = np.random.lognormal(mu, sigma, int(draw_offs_use)) else: Flow_rated = np.random.normal(vol_aver_drawoff_use, vol_desv_drawoff_use, int(draw_offs_use)) Volume_use = np.array(np.double(np.abs(Flow_rated * time_aver_drawoff_use))) for i in range(int(time_steps_day)): index = np.where(time_event == i) if len(index) > n_max1: index1 = index[0, int(n_max1)] else: index1 = index if draw_offs_use == 1: if index1 == 0: Volume_use_time = Volume_use else: Volume_use_time = Volume_use[index1] Volume_use_daily_array[(day, i)] = np.sum(Volume_use_time) / time_aver_drawoff_use Volume_use_resh1 = np.reshape(Volume_use_daily_array, (1, int(total_days * time_steps_day))) Volume_use_array[:, use] = Volume_use_resh1 Volume_use_sum[0, use] = np.sum(Volume_use_array[:, use]) Volume_use_unit[:, units0] = np.sum(Volume_use_array, axis=1) # if numunits>1: Volume_use_arrayb0 = np.sum(Volume_use_unit, axis=1) # else: # Volume_use_arrayb0=Volume_use_unit[:,0] Volume_totalb1 = np.sum(Volume_use_arrayb0) number_units = numunits volume_profile = Volume_use_arrayb0 total_volume = Volume_totalb1 Volume_meanb1 = ((Volume_totalb1) / numunits) / total_days return volume_profile
[docs]class DomesticHotWater: """DomesticHotWater object Class to manage all the calculations involved in the Domestic Hot Water consumption """
[docs] def __init__( self, name: str, calculation_method: str, unit = None, schedule = None, ): f"""Constructor for DomesticHotWater. Memorizes the attributes anc checks them through properties setter Parameters ---------- name : str name of the object calculation_method : str Calculation method, choose from {domestic_hot_water_prop['calculation_method']} unit : str Unit of the schedule, choose from {domestic_hot_water_prop['unit']} schedule : eureca_building.schedule.Schedule Schedule object, to be used in case the method is 'schedule' """ self.name = name self.calculation_method = calculation_method self.unit = unit self.schedule = schedule
@property def calculation_method(self): return self._calculation_method @calculation_method.setter def calculation_method(self, value): if not isinstance(value, str): raise ValueError(f"Domestic hot water {self.name}, calculation method must be a str: {value}") if value not in domestic_hot_water_prop['calculation_method']: raise ValueError(f"Domestic hot water {self.name}, calculation method not valid: {value}: Choose from {domestic_hot_water_prop['calculatio_method']}") self._calculation_method = value @property def unit(self): return self._unit @unit.setter def unit(self, value): if self._calculation_method == "Schedule": if not isinstance(value, str): raise TypeError(f"Domestic hot water {self.name}, unit is not a str: {value}") if value not in domestic_hot_water_prop["unit"]: raise TypeError( f"Domestic Hot Water {self.name}, unit not in: {domestic_hot_water_prop['unit']}\n{value}" ) self._unit = value @property def schedule(self): return self._schedule @schedule.setter def schedule(self, value): if self._calculation_method == "Schedule": if not isinstance(value, Schedule): raise ValueError(f"Domestic Hot Water {self.name}, schedule type not Schedule: {type(value)}.\nIf you chose Schedule calculation method you must provide a mass flow rate schedule") if value.schedule_type not in ["mass_flow_rate",]: raise InvalidScheduleType( f"Domestic Hot Water {self.name}, schedule type must be 'mass_flow_rate': {value.schedule_type}" ) self._schedule = value
[docs] def get_dhw_yearly_mass_flow_rate(self, area, number_of_units, weather): """This function calculates the water and mass flow rate consumption, given the area of the building and the number of units (to be used when unit and/or method need them) Parameters ---------- area : float Area of the building [m2] number_of_units : int Number of dwellings (for residential calculation done with UNI-TS 11300 weather : eureca_building.weather.WeatherFile WeatherFile object Returns ------- tuple tuple of numpy.arrays volume flow rate [m3/s], dhw heating demand [W] """ if self.calculation_method == "Schedule": schedule = self.schedule.schedule if self.unit == "L/s": volume = schedule / 1000 # to converto to m3/s if self.unit == "L/(m2 h)": volume = schedule * area / 3600 / 1000 # to converto to m3/s else: # Calculation of demand and volume with UNI-TS 11300 # Vw [lt/day] = a [lt/(m2 day)] * Af [m2] + b [lt/day] Af = area / number_of_units if Af <= 35: a = 0.0; b = 50.0 elif 35 < Af <= 50: a = 2.667; b = -43.33 elif 50 < Af <= 200: a = 1.067; b = 36.67 elif Af > 200: a = 0.0; b = 250.0 # Water Need [m3/day] Vw_single = (a * Af + b) / 1000 if self.calculation_method == "UNI-TS 11300-2": volume = np.ones(CONFIG.number_of_time_steps_year) * Vw_single * number_of_units * 365 / CONFIG.number_of_time_steps_year / CONFIG.time_step # To convert from m3/ts to m3/s if self.calculation_method == "DHW calc": # Broken # TODO: fix stochastic calculation volume = dhw_calc_calculation(Vw_single, number_of_units, CONFIG.time_step / 60)[:CONFIG.number_of_time_steps_year] / 1000 / CONFIG.time_step # to m3/ts to m3/s # L or m3? Cw = water_properties["specific_heat"] # [J/(kg K)] DTw = domestic_hot_water_prop["target temperature [°C]"] - weather.general_data["average_out_air_db_temperature"] # [°C] rho = water_properties["density"] # [kg/m3] demand = volume * rho * Cw * DTw # W return volume, demand