Skip to content

Commit

Permalink
#24 Correcting the 5R1C Method and the norm heat load calculation
Browse files Browse the repository at this point in the history
  • Loading branch information
RawadHamze committed Jan 10, 2025
1 parent dca29cc commit c3601f7
Show file tree
Hide file tree
Showing 12 changed files with 8,604 additions and 296 deletions.
58 changes: 43 additions & 15 deletions classes/datahandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def select_plz_data(self, plz):
sheet = workbook.active

for row in sheet.iter_rows(values_only=True):
if plz == row[0]:
if plz == str(row[0]):
latitude = row[4]
longitude = row[5]
weatherdatafile = row[3]
Expand Down Expand Up @@ -168,6 +168,12 @@ def generateEnvironment(self, plz):
self.time[subData["name"]] = subData["value"]
self.time["timeSteps"] = int(self.time["dataLength"] / self.time["timeResolution"])

# load the holidays
if self.site["TRYYear"] == "TRY2015":
self.time["holidays"] = self.time["holidays2015"]
elif self.site["TRYYear"] == "TRY2045":
self.time["holidays"] = self.time["holidays2045"]

# interpolate input data to achieve required data resolution
# transformation from values for points in time to values for time intervals
self.site["SunDirect"] = np.interp(np.arange(0, self.time["dataLength"] + 1, self.time["timeResolution"]),
Expand All @@ -185,6 +191,20 @@ def generateEnvironment(self, plz):

self.site["SunTotal"] = self.site["SunDirect"] + self.site["SunDiffuse"]

# Load other site-dependent values based on DIN/TS 12831-1:2020-04
srcPath = os.path.dirname(os.path.abspath(__file__))
filePath = os.path.join(os.path.dirname(srcPath), 'data', 'site_data.txt')
site_data = pd.read_csv(filePath, delimiter='\t', dtype={'Zip': str})

# Filter data for the specific zip code
filtered_data = site_data[site_data['Zip'] == plz]

# extract the needed values
self.site["altitude"] = filtered_data.iloc[0]['Altitude']
self.site["location"] = [filtered_data.iloc[0]['Latitude'],filtered_data.iloc[0]['Longitude']]
self.site["T_ne"] = filtered_data.iloc[0]['T_ne'] # norm outside temperature for calculating the design heat load
self.site["T_me"] = filtered_data.iloc[0]['T_me'] # mean annual temperature for calculating the design heat

# Calculate solar irradiance per surface direction - S, W, N, E, Roof represented by angles gamma and beta
global sun
sun = Sun(filePath=self.filePath)
Expand Down Expand Up @@ -295,6 +315,21 @@ def generateBuildings(self):
building["user"] = Users(building=building["buildingFeatures"]["building"],
area=building["buildingFeatures"]["area"])

# %% calculate design heat loads
# at norm outside temperature
building["envelope"].heatload = building["envelope"].calcHeatLoad(site=self.site, method="design")
# at bivalent temperature
building["envelope"].bivalent = building["envelope"].calcHeatLoad(site=self.site, method="bivalent")
# at heating limit temperature
building["envelope"].heatlimit = building["envelope"].calcHeatLoad(site=self.site, method="heatlimit")
# for drinking hot water
if building["user"].building in {"SFH", "MFH", "TH", "AB"}:
building["dhwload"] = bldgs["dhwload"][bldgs["buildings_short"].index(building["user"].building)] * \
building["user"].nb_flats
else:
building["dhwload"] = bldgs["dhwload"][bldgs["buildings_short"].index(building["user"].building)] * \
building["user"].nb_main_rooms

index = bldgs["buildings_short"].index(building["buildingFeatures"]["building"])
building["buildingFeatures"]["mean_drawoff_dhw"] = bldgs["mean_drawoff_vol_per_day"][index]

Expand Down Expand Up @@ -356,10 +391,15 @@ def generateDemands(self, calcUserProfiles=True, saveUserProfiles=True):

building["envelope"].calcNormativeProperties(self.SunRad, building["user"].gains)

night_setback = building["buildingFeatures"]["night_setback"]

# calculate heating profiles
building["user"].calcHeatingProfile(site=self.site,
envelope=building["envelope"],
time_resolution=self.time["timeResolution"])
night_setback=night_setback,
holidays=self.time["holidays"],
time_resolution=self.time["timeResolution"]
)

if saveUserProfiles:
building["user"].saveHeatingProfile(building["unique_name"], os.path.join(self.resultPath, 'demands'))
Expand Down Expand Up @@ -448,18 +488,6 @@ def designDecentralDevices(self, saveGenerationProfiles=True):
for subData in jsonData:
bldgs[subData["name"]] = subData["value"]

# %% calculate design heat loads
# at norm outside temperature
building["heatload"] = building["envelope"].calcHeatLoad(site=self.site, method="design")
# at bivalent temperature
building["bivalent"] = building["envelope"].calcHeatLoad(site=self.site, method="bivalent")
# at heating limit temperature
building["heatlimit"] = building["envelope"].calcHeatLoad(site=self.site, method="heatlimit")
# for drinking hot water
building["dhwload"] = \
bldgs["dhwload"][bldgs["buildings_short"].index(building["buildingFeatures"]["building"])] * \
building["user"].nb_flats

# %% create building energy system object
# get capacities of all possible devices
bes_obj = BES(file_path=self.filePath)
Expand Down Expand Up @@ -498,7 +526,7 @@ def designDecentralDevices(self, saveGenerationProfiles=True):
building["generationSTC"],
delimiter=',')

def designCentralDevices(self):
def designCentralDevices(self, saveGenerationProfiles):
"""
Calculate capacities and generation profiles of renewable energies for central devices.
Expand Down
20 changes: 10 additions & 10 deletions classes/envelope.py
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ def loadAreas(self, prj):

def calcHeatLoad(self, site, method="design"):
"""
Calculate heat load.
Calculate design (nominal) heat load at norm outside temperature
Parameters
----------
Expand Down Expand Up @@ -505,28 +505,28 @@ def calcHeatLoad(self, site, method="design"):
U_TB = 0.05 # [W/m²K] Thermal bridge surcharge
f_g1 = 1.45 # Correction factor for annual fluctuation of the outdoor temperature
# Reduction factor
f_g2 = (self.T_set_min - T_me) / (self.T_set_min - T_ne)
f_g2 = (self.T_set_min - site["T_me"]) / (self.T_set_min - site["T_ne"])
G_w = 1.0 # influence of groundwater neglected

if method == "design":
Q_nHC = (self.A["opaque"]["wall"] * (self.U["opaque"]["wall"] + U_TB) +
self.A["window"]["sum"] * self.U["window"] +
self.A["opaque"]["roof"] * self.U["opaque"]["roof"] +
self.A["opaque"]["floor"] * self.U["opaque"]["floor"] * f_g1 * f_g2 * G_w
+ self.ventilationRate * self.c_p_air * self.rho_air * self.V / 3600) * (self.T_set_min - T_ne)
self.A["window"]["sum"] * (self.U["window"] + U_TB) +
self.A["opaque"]["roof"] * (self.U["opaque"]["roof"] + U_TB) +
self.A["opaque"]["floor"] * self.U["opaque"]["floor"] * f_g1 * f_g2 * G_w +
self.ventilationRate * self.c_p_air * self.rho_air * self.V / 3600) * (self.T_set_min - site["T_ne"])

if method == "bivalent":
Q_nHC = (self.A["opaque"]["wall"] * (self.U["opaque"]["wall"] + U_TB) +
self.A["window"]["sum"] * self.U["window"] +
self.A["opaque"]["roof"] * self.U["opaque"]["roof"] +
self.A["window"]["sum"] * (self.U["window"] + U_TB) +
self.A["opaque"]["roof"] * (self.U["opaque"]["roof"] + U_TB) +
self.A["opaque"]["floor"] * self.U["opaque"]["floor"] * f_g1 * f_g2 * G_w
+ self.ventilationRate * self.c_p_air * self.rho_air * self.V / 3600) \
* (self.T_set_min - self.T_bivalent)

if method == "heatlimit":
Q_nHC = (self.A["opaque"]["wall"] * (self.U["opaque"]["wall"] + U_TB) +
self.A["window"]["sum"] * self.U["window"] +
self.A["opaque"]["roof"] * self.U["opaque"]["roof"] +
self.A["window"]["sum"] * (self.U["window"] + U_TB) +
self.A["opaque"]["roof"] * (self.U["opaque"]["roof"] + U_TB) +
self.A["opaque"]["floor"] * self.U["opaque"]["floor"] * f_g1 * f_g2 * G_w
+ self.ventilationRate * self.c_p_air * self.rho_air * self.V / 3600) \
* (self.T_set_min - self.T_heatlimit)
Expand Down
12 changes: 4 additions & 8 deletions classes/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,23 +71,19 @@ def designECS(self, building, site):
T_bivalent = design_data["T_bivalent"]
T_heatlimit = design_data["T_heatlimit"]

with open(os.path.join(self.file_path, 'design_weather_data.json')) as json_file:
jsonData = json.load(json_file)
for subData in jsonData:
if subData["Klimazone"] == site["climateZone"]:
T_design = subData["Theta_e"] # [°C] outside design temperature
T_design = site["T_ne"] # [°C] outside design temperature

# %% conduct linear interpolation
# for optimal design at bivalent temperature
self.design_load = building["heatload"] + building["dhwload"]
limit_load = building["heatlimit"]
self.design_load = building["envelope"].heatload + building["dhwload"]
limit_load = building["envelope"].heatlimit

self.bivalent_load = self.design_load + (limit_load - self.design_load) / (T_heatlimit - T_design) \
* (T_bivalent - T_design)

# Load list of possible devices
dev = {}
with open(os.path.join(self.file_path, 'decentral_device_data.json')) as json_file:
with open(os.path.join(self.file_path, 'decenfftral_device_data.json')) as json_file:
jsonData = json.load(json_file)
for subData in jsonData:
dev[subData["abbreviation"]] = {}
Expand Down
27 changes: 12 additions & 15 deletions classes/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ def calcProfiles(self, site, time_resolution, time_horizon, building, path, init
#self.elec = np.loadtxt(path + '/elec_' + unique_name + '.csv', delimiter=',')
#self.gains = np.loadtxt(path + '/gains_' + unique_name + '.csv', delimiter=',')

def calcHeatingProfile(self, site, envelope, time_resolution):
def calcHeatingProfile(self, site, envelope, night_setback, holidays, time_resolution):
"""
Calculate heat demand for each building.
Expand All @@ -369,21 +369,18 @@ def calcHeatingProfile(self, site, envelope, time_resolution):
None.
"""

dt = time_resolution/(60*60)
dt = time_resolution / (60 * 60)
# calculate the temperatures (Q_HC, T_op, T_m, T_air, T_s)
(Q_HC, T_i, T_s, T_m, T_op) = heating.calculate(envelope, envelope.T_set_min, site["T_e"], dt)
#(Q_H, Q_C, T_op, T_m, T_i, T_s) = heating.calc(envelope, site["T_e"], dt)
# heating load for the current time step in Watt
self.heat = np.zeros(len(Q_HC))
for t in range(len(Q_HC)):
self.heat[t] = max(0, Q_HC[t])
self.annual_heat_demand = np.sum(self.heat)

(Q_HC, T_i, T_s, T_m, T_op) = heating.calculate(envelope, envelope.T_set_max, site["T_e"], dt)
self.cooling = np.zeros(len(Q_HC))
for t in range(len(Q_HC)):
self.cooling[t] = min(0, Q_HC[t]) * (-1)
self.annual_cooling_demand = np.sum(self.cooling)
if night_setback == 1:
(Q_H, Q_C, T_op, T_m, T_i, T_s) = heating.calc_night_setback(envelope, site["T_e"], holidays, dt,
self.building)
elif night_setback == 0:
(Q_H, Q_C, T_op, T_m, T_i, T_s) = heating.calc(envelope, site["T_e"], holidays, dt, self.building)
# heating and cooling loads for the current time step in Watt
self.heat = Q_H
self.cooling = Q_C
self.annual_heat_demand = np.sum(Q_H)
self.annual_cooling_demand = np.sum(Q_C)

def saveProfiles(self, unique_name, path):
"""
Expand Down
6 changes: 3 additions & 3 deletions data/scenarios/example.csv
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
id;building;year;retrofit;area;heater;PV;STC;EV;BAT;f_TES;f_BAT;f_EV;f_PV;f_STC;gamma_PV;ev_charging
0;SFH;2022;1;150;heat_grid;0;0;0;0;35;1;M;0.4;0.04;0;on_demand
1;SFH;2022;1;165;heat_grid;0;0;0;0;35;1;M;0.4;0.04;0;on_demand
id;building;year;retrofit;construction_type;night_setback;area;heater;PV;STC;EV;BAT;f_TES;f_BAT;f_EV;f_PV;f_STC;gamma_PV;ev_charging
0;SFH;1980;0;;0;140;heat_grid;1;1;1;1;35;1;M;0.4;0.04;0;on_demand
1;MFH;1980;0;;0;300;heat_grid;1;1;1;1;35;1;M;0.4;0.04;0;on_demand
Loading

0 comments on commit c3601f7

Please sign in to comment.