def poly_area(poly): ''' From a list of points calculates the area of the polygon Parameters ---------- poly : list of list of floats (polygon) list with three floats with the coordinates of the first point Returns ------- float: the polygon area ''' # Check input data type if not isinstance(poly, list): raise TypeError(f'ERROR poly_area function, the input is not a list: input {poly}') for vtx in poly: if not isinstance(vtx, list) and not isinstance(vtx, tuple): print(type(vtx)) raise TypeError(f'ERROR poly_area function, an input is not a list: input {vtx}') if len(vtx) != 3: raise TypeError(f'ERROR poly_area function, a vertex is not a list of 3 components: input {vtx}') try: vtx = list(vtx) vtx[0] = float(vtx[0]) vtx[1] = float(vtx[1]) vtx[2] = float(vtx[2]) except ValueError: raise ValueError(f'ERROR unit_normal function, a coordinate is not a float: input {vtx}') # area of polygon poly if len(poly) < 3: # Not a plane - no area wrn('WARNING number of vertices lower than 3, the area will be zero') return 0 total = [0, 0, 0] N = len(poly) for i in range(N): vi1 = poly[i] vi2 = poly[(i+1) % N] prod = np.cross(vi1, vi2) total[0] += prod[0] total[1] += prod[1] total[2] += prod[2] result = np.dot(total, unit_normal(poly[0], poly[1], poly[2])) return float(abs(result/2))
def checkSatCond(T, x, p): ''' Check Saturation Condition This function takes as inputs temperature [°C] and humidity ratio [kg_vap/kg_as] to check if a point is outside saturation conditions Parameters ---------- T : float Temperature [°C] x : float Specific Humidity [kg_vap/kg_as]. p : float Pressure [Pa]. Returns ------- Boolean, Saturation Pressure [Pa]. ''' # Check input data type if not isinstance(T, float): raise TypeError(f'ERROR input T is not an interger: T {T}') if not isinstance(x, float): raise TypeError(f'ERROR input x is not an interger: x {x}') # Control input data quality if T < -15 or T > 60: wrn(f"WARNING CheckSatCond function, input temperature outside limit boundary [-15,60]: T {T}" ) if x < 0.0005 or x > 0.040: wrn(f"WARNING CheckSatCond function, input humidity outside limit boundary [0.0005,0.04]: x {x}" ) # Is or not outide the saturation condition? True/False pp = p * x / (0.622 + x) if T < 0: psat = 610.5 * np.exp((21.875 * T) / (265.5 + T)) else: psat = 610.5 * np.exp((17.269 * T) / (237.3 + T)) if pp - psat > 0.01: SatCond = False else: SatCond = True return SatCond, psat
def __init__(self, ExtRoofArea, l): ''' Initilization of the photovoltaic plant Parameters ---------- ExtRoofArea : float building's external roof area [m2] l : int number of simulation time steps [-] Returns ------- None ''' # Check input data if not isinstance(ExtRoofArea, float): raise TypeError( f'ERROR DistrictPVGIS class, ExtRoofArea is not a float: ExtRoofArea {ExtRoofArea}' ) if not isinstance(l, int): raise TypeError( f'ERROR DistrictPVGIS class, l is not a int: l {l}') # Check input data quality if ExtRoofArea < 0: wrn(f"WARNING DistrictPVGIS class, input ExtRoofArea must be positive: ExtRoofArea {ExtRoofArea}" ) if l < 0: wrn(f"WARNING DistrictPVGIS class, input l must be positive: l {l}" ) # Total area and nominal power evaluation self.A_all = ExtRoofArea * self.area # Total plant area [m2] self.P_nom = 1000 * self.A_all * self.etaNom # Nominal power in Standard condition [W] # Output vectors initialization self.P = np.zeros(l) self.etaRel = np.zeros(l) self.FB = np.zeros(l) self.P_el = np.zeros(l) self.l = l
def PVGIS_production(tilt, GR, BR, DR, RR, Ta, W, P_nom, i): ''' This function allows to calculate the photovoltaic electricity production Parameters ---------- tilt : float inclination angle of the photovoltaic panel [°] GR : float global radiation on the oriented surface [W/m2] BR : float beam radiation on the oriented surface [W/m2] DR : float diffure radiation on the oriented surface [W/m2] RR : float reflected radiation on the oriented surfaces [W/m2] Ta : float external air temperature [°C] W : float wind speed [m/s] P_nom : float nominal power of the photovoltaic plant in Standard conditions i : float angle of incidence [°] Returns ------- P : float electrical power production [W] etaRel : float plant relative efficiency [-] FB : float angular losses factor for beam radiation [-] ''' # Check input data type if not isinstance(tilt, float): raise TypeError( f'ERROR PVGIS_production function, tilt is not a float: tilt {tilt}' ) if not isinstance(GR, float): raise TypeError( f'ERROR PVGIS_production function, GR is not a float: GR {GR}') if not isinstance(BR, float): raise TypeError( f'ERROR PVGIS_production function, BR is not a float: BR {BR}') if not isinstance(DR, float): raise TypeError( f'ERROR PVGIS_production function, DR is not a float: DR {DR}') if not isinstance(RR, float): raise TypeError( f'ERROR PVGIS_production function, RR is not a float: RR {RR}') if not isinstance(Ta, float): raise TypeError( f'ERROR PVGIS_production function, Ta is not a float: Ta {Ta}') if not isinstance(W, float): raise TypeError( f'ERROR PVGIS_production function, W is not a float: W {W}') if not isinstance(P_nom, float): raise TypeError( f'ERROR PVGIS_production function, P_nom is not a float: P_nom {P_nom}' ) if not isinstance(i, float): raise TypeError( f'ERROR PVGIS_production function, i is not a float: i {i}') # Check input data quality if not 0 <= GR <= 3000: wrn(f"WARNING PVGIS_production function, input GR is out of plausible range: GR {GR}" ) if not 0 <= BR <= 3000: wrn(f"WARNING PVGIS_production function, input BR is out of plausible range: BR {BR}" ) if not 0 <= DR <= 3000: wrn(f"WARNING PVGIS_production function, input DR is out of plausible range: DR {DR}" ) if not 0 <= RR <= 3000: wrn(f"WARNING PVGIS_production function, input RR is out of plausible range: RR {RR}" ) if not -50 <= Ta <= 60: wrn(f"WARNING PVGIS_production function, input Ta is out of plausible range: Ta {Ta}" ) if not 0 <= W <= 25: wrn(f"WARNING PVGIS_production function, input W is out of plausible range: W {W}" ) # Degree to radians conversion i = np.radians(i) tiltrad = np.radians(tilt) # Angular losses calculation (N. Martin and JM. Ruiz) ar = 0.157 # Reference value for Air/Glass/Silicon c1 = 4 / (3 * np.pi) c2 = -0.074 # Reference value for Air/Glass/Silicon FB = (np.exp(-np.cos(i) / ar) - np.exp(-1 / ar)) / (1 - np.exp(-1 / ar)) FD = np.exp(-1 / ar * (c1 * (np.sin(tiltrad) + (np.pi - tilt * np.pi / 180 - np.sin(tiltrad)) / (1 + np.cos(tiltrad))) + c2 * (np.sin(tiltrad) + (np.pi - tilt * np.pi / 180 - np.sin(tiltrad)) / (1 + np.cos(tiltrad)))**2)) FA = np.exp(-1 / ar * (c1 * (np.sin(tiltrad) + (tilt * np.pi / 180 - np.sin(tiltrad)) / (1 - np.cos(tiltrad))) + c2 * (np.sin(tiltrad) + (tilt * np.pi / 180 - np.sin(tiltrad)) / (1 - np.cos(tiltrad)))**2)) # Setting curve coefficients (Reference values for c-Si modules) k1 = -0.017237 k2 = -0.040465 k3 = -0.004702 k4 = 0.000149 k5 = 0.000170 k6 = 0.000005 # Module temperature evaluation (c-Si modules) U0 = 26.92 U1 = 6.24 Tm = Ta + GR / (U0 + U1 * W) # Module temperature [°C] tm = Tm - 25 # Relative efficiency and power production calculation g = GR / 1000 if g == 0: etaRel = 0 else: etaRel = 1 + k1 * np.log(g) + k2 * np.log( g)**2 + k3 * tm + k4 * tm * np.log(g) + k5 * tm * np.log( g)**2 + k6 * tm**2 P = 1 / 1000 * P_nom * etaRel * (BR * (1 - FB) + DR * (1 - FD) + RR * (1 - FA)) # [W] if P < 0: P = 0 return P, etaRel, FB # Output variables
def AHUCalc(self, t, G_da_vent, AHUOnOff, AHUHUM, Sens_Recovery_eff, Lat_Recovery_eff, OutAirRatio, T_ext, x_ext, T_int, x_int, T_sup, x_sup): ''' Solution for the single time step of the Air Handling Unit Parameters: t: timestep [-] G_da_vent: mass flow rate [kg/s] AHUOnOff: AHU availabilty [-] AHUHUM: Humidistat availability [-] SensRecoveryEff: Sensible heat recovery efficiency [-] LatRecoveryEff: Latent heat recovery efficiency [-] OutAirRatio: Outdoor air ratio [-] T_ext: external temperature [°C] x_ext: external specific humidity [kg_v/kg_da] T_int: zone internal temperature [°C] x_int: zone internal specific humidity [kg_v/kg_da] T_sup: supply temperature [°C] x_sup: supplyspecific humidity [kg_v/kg_da] Returns: None ''' # Check input data type if not isinstance(t, int): raise TypeError( f'ERROR AHUCalc, bd {self.bd_name}, time step {t}, input t is not an interger: t {t}' ) if (not isinstance(G_da_vent, float)) or G_da_vent < 0: raise TypeError( f'ERROR AHUCalc, bd {self.bd_name}, time step {t}, input G_da_vent is not a positive float: G_da_vent {G_da_vent}' ) if not AHUOnOff in [0, 1, -1]: raise TypeError( f'ERROR AHUCalc, bd {self.bd_name}, time step {t}, input AHUOnOff must be 0,1,-1: AHUOnOff {AHUOnOff}' ) if not isinstance(AHUHUM, bool): raise TypeError( f'ERROR AHUCalc, bd {self.bd_name}, time step {t}, input AHUHUM is not a boolean: AHUHUM {AHUHUM}' ) if (not isinstance(Sens_Recovery_eff, float) ) or Sens_Recovery_eff < 0. or Sens_Recovery_eff > 1.: raise TypeError( f'ERROR AHUCalc, bd {self.bd_name}, time step {t}, input Sens_Recovery_eff must be included in the range 0-1: Sens_Recovery_eff {Sens_Recovery_eff}' ) if (not isinstance(Lat_Recovery_eff, float) ) or Lat_Recovery_eff < 0. or Lat_Recovery_eff > 1.: raise TypeError( f'ERROR AHUCalc, bd {self.bd_name}, time step {t}, input Lat_Recovery_eff must be included in the range 0-1: Lat_Recovery_eff {Lat_Recovery_eff}' ) if (not isinstance(OutAirRatio, float)) or OutAirRatio < 0. or OutAirRatio > 1.: raise TypeError( f'ERROR AHUCalc, bd {self.bd_name}, time step {t}, input OutAirRatio must be included in the range 0-1: OutAirRatio {OutAirRatio}' ) if not isinstance(T_ext, float): raise TypeError( f'ERROR AHUCalc, bd {self.bd_name}, time step {t}, input T_ext is not an interger: T_ext {T_ext}' ) if not isinstance(T_int, float): raise TypeError( f'ERROR AHUCalc, bd {self.bd_name}, time step {t}, input T_int is not an interger: T_int {T_int}' ) if not isinstance(T_sup, float): raise TypeError( f'ERROR AHUCalc, bd {self.bd_name}, time step {t}, input T_sup is not an interger: T_sup {T_sup}' ) if not isinstance(x_ext, float): raise TypeError( f'ERROR AHUCalc, bd {self.bd_name}, time step {t}, input x_ext is not an interger: x_ext {x_ext}' ) if not isinstance(x_int, float): raise TypeError( f'ERROR AHUCalc, bd {self.bd_name}, time step {t}, input x_int is not an interger: x_int {x_int}' ) if not isinstance(x_sup, float): raise TypeError( f'ERROR AHUCalc, bd {self.bd_name}, time step {t}, input x_sup is not an interger: x_sup {x_sup}' ) # Control input data quality if T_ext < -15 or T_ext > 60: wrn(f"WARNING AHU class, AHUCalc method, bd {self.bd_name}, time step {t}, input temperature outside limit boundary [-15,60]: T_ext {T_ext}" ) if T_int < -15 or T_int > 60: wrn(f"WARNING AHU class, AHUCalc method, bd {self.bd_name}, time step {t}, input temperature outside limit boundary [-15,60]: T_int {T_int}" ) if T_sup < -15 or T_sup > 60: wrn(f"WARNING AHU class, AHUCalc method, bd {self.bd_name}, time step {t}, input temperature outside limit boundary [-15,60]: T_sup {T_sup}" ) if x_ext < 0.0005 or x_ext > 0.040: wrn(f"WARNING AHU class, AHUCalc method, bd {self.bd_name}, time step {t}, input humidity outside limit boundary [0.0005,0.04]: x_ext {x_ext}" ) if x_int < 0.0005 or x_int > 0.040: wrn(f"WARNING AHU class, AHUCalc method, bd {self.bd_name}, time step {t}, input humidity outside limit boundary [0.0005,0.04]: x_int {x_int}" ) if x_sup < 0.0005 or x_sup > 0.040: wrn(f"WARNING AHU class, AHUCalc method, bd {self.bd_name}, time step {t}, input humidity outside limit boundary [0.0005,0.04]: x_sup {x_sup}" ) # Set some input variables self.T_supAHU[t] = T_sup self.x_sup = x_sup # Saturation conditions check SatCond, psat = checkSatCond(T_ext, x_ext, self.p_atm) if SatCond == False: x_ext = 0.99 * 0.622 * psat / (self.p_atm - 0.99 * psat) if AHUOnOff == 0: self.x_sup = x_ext SatCond, psat = checkSatCond(T_int, x_int, self.p_atm) if SatCond == False: wrn(f'ERROR AHUCalc method, bd {self.bd_name}, time step {t}, Zone conditions outside saturation limit' ) SatCond, psat = checkSatCond(self.T_supAHU[t], self.x_sup, self.p_atm) if SatCond == False: wrn(f'ERROR: AHUCalc method, bd {self.bd_name}, time step {t}, Supply conditions outside saturation limit' ) # Pre-processing on Heat Recovery and Mixer self.h_ext = self.cp_air * T_ext + (self.r_0 + self.cpv * T_ext) * x_ext self.h_z = self.cp_air * T_int + (self.r_0 + self.cpv * T_int) * x_int # Heat Recovery Bypass in condition of free heating or cooling if (T_int - T_ext) * AHUOnOff > 0: self.T_hr = T_ext + Sens_Recovery_eff * (T_int - T_ext) self.T_out = T_int - Sens_Recovery_eff * (T_int - T_ext) else: self.T_hr = T_ext self.T_out = T_int OutAirRatio = 1 if (x_int - x_ext) * AHUOnOff > 0: self.x_hr = x_ext + Lat_Recovery_eff * (x_int - x_ext) else: self.x_hr = x_ext self.h_hr = self.cp_air * self.T_hr + ( self.r_0 + self.cpv * self.T_hr) * self.x_hr # Mixer self.h_mix = OutAirRatio * self.h_hr + (1 - OutAirRatio) * self.h_z self.x_mix = OutAirRatio * self.x_hr + (1 - OutAirRatio) * x_int self.T_mix = (self.h_mix - self.r_0 * self.x_mix) / ( self.cp_air + self.cpv * self.x_mix) # BATTERIES DEMAND CALCULATION # SENSIBLE AND LATENT CONTROL MODE if AHUHUM == True: # Heating mode if AHUOnOff == 1: # Adiabatic Saturator self.x_as = self.x_sup p_as = self.p_atm * self.x_as / (0.622 + self.x_as) if p_as >= 610.5: self.T_as = 237.3 * np.log( p_as / 610.5) / (17.269 - np.log(p_as / 610.5)) else: self.T_as = 265.5 * np.log( p_as / 610.5) / (21.875 - np.log(p_as / 610.5)) self.h_as = self.cp_air * self.T_as + ( self.r_0 + self.cpv * self.T_as) * self.x_as # Pre-Heater control if self.h_mix < self.h_as: self.h_ph = self.h_as self.x_ph = self.x_mix self.T_ph = (self.h_ph - self.r_0 * self.x_ph) / ( 1.006 + self.cpv * self.x_ph) else: # In this case the Pre-Heater is by-passed self.h_ph = self.h_mix self.x_ph = self.x_mix self.T_ph = self.T_mix self.h_as = self.h_ph # Humidification need check if self.x_ph > self.x_sup: self.x_as = self.x_ph self.x_sup = self.x_as self.T_as = (self.h_as - self.r_0 * self.x_as) / ( self.cp_air + self.cpv * self.x_as) # Controlling T_supAHU > T_as if self.T_supAHU[t] < self.T_as: self.T_supAHU[t] = self.T_as self.h_sup = self.cp_air * self.T_supAHU[t] + ( self.r_0 + self.cpv * self.T_supAHU[t]) * self.x_sup # Batteries Demand [kW] # Pre-Heater preh_Dem = G_da_vent * (self.h_ph - self.h_mix) preh_Dem_sens = G_da_vent * self.cp_air * (self.T_ph - self.T_mix) preh_Dem_lat = G_da_vent * self.r_0 * (self.x_ph - self.x_mix) # Adiabatic Saturator sat_Dem = G_da_vent * (self.h_as - self.h_ph) sat_Dem_lat = G_da_vent * self.r_0 * (self.x_as - self.x_ph) sat_Dem_sens = sat_Dem - sat_Dem_lat # Post-Heater posth_Dem = G_da_vent * (self.h_sup - self.h_as) posth_Dem_sens = G_da_vent * self.cp_air * (self.T_supAHU[t] - self.T_as) posth_Dem_lat = G_da_vent * self.r_0 * (self.x_sup - self.x_as) # Total Demand self.AHUDemand[t] = preh_Dem + sat_Dem + posth_Dem self.AHUDemand_sens[ t] = preh_Dem_sens + sat_Dem_sens + posth_Dem_sens self.AHUDemand_lat[ t] = preh_Dem_lat + sat_Dem_lat + posth_Dem_lat # Cooling mode elif AHUOnOff == -1: # Dehumidificator self.x_de = self.x_sup p_de = self.p_atm * self.x_de / (0.622 + self.x_de) if p_de >= 610.5: self.T_de = 237.3 * np.log( p_de / 610.5) / (17.269 - np.log(p_de / 610.5)) else: self.T_de = 265.5 * np.log( p_de / 610.5) / (21.875 - np.log(p_de / 610.5)) # Dehumidifcator and post-heater control if self.x_de >= self.x_mix: self.x_de = self.x_mix self.x_sup = self.x_de if self.T_supAHU[t] <= self.T_mix: self.T_de = self.T_supAHU[t] else: self.T_de = self.T_mix self.T_supAHU[t] = self.T_de self.h_de = self.cp_air * self.T_de + ( self.r_0 + self.cpv * self.T_de) * self.x_de self.h_sup = self.cp_air * self.T_supAHU[t] + ( self.r_0 + self.cpv * self.T_supAHU[t]) * self.x_sup # Batteries Demand [kW] # Dehumidificator deu_Dem = G_da_vent * (self.h_de - self.h_mix) deu_Dem_sens = G_da_vent * self.cp_air * (self.T_de - self.T_mix) deu_Dem_lat = G_da_vent * self.r_0 * (self.x_de - self.x_mix) # Post-Heater posth_Dem = G_da_vent * (self.h_sup - self.h_de) posth_Dem_sens = G_da_vent * self.cp_air * (self.T_supAHU[t] - self.T_de) posth_Dem_lat = G_da_vent * self.r_0 * (self.x_sup - self.x_de) # Total Demand self.AHUDemand[t] = deu_Dem + posth_Dem self.AHUDemand_sens[t] = deu_Dem_sens + posth_Dem_sens self.AHUDemand_lat[t] = deu_Dem_lat + posth_Dem_lat elif AHUOnOff == 0: # Plant Off self.T_hr, self.T_mix, self.T_supAHU[t] = T_ext, T_ext, T_ext self.x_hr, self.x_mix, self.x_sup = x_ext, x_ext, x_ext self.h_hr, self.h_mix, self.h_sup = self.h_ext, self.h_ext, self.h_ext # Batteries Demand [kW] self.AHUDemand[t] = 0 self.AHUDemand_sens[t] = 0 self.AHUDemand_lat[t] = 0 else: print('AHUOnOff value not allowed') # SENSIBLE CONTROL MODE if AHUHUM == False: # Heating mode if AHUOnOff == 1: # Pre-Heater and Adiabatic Saturator doesn't exist!! self.h_ph = self.h_mix self.x_ph = self.x_mix self.T_ph = self.T_mix self.h_as = self.h_ph self.x_as = self.x_ph self.T_as = self.T_ph # Post-Heater if self.T_supAHU[t] < self.T_as: self.T_supAHU[t] = self.T_as self.x_sup = self.x_as self.h_sup = self.cp_air * self.T_supAHU[t] + ( self.r_0 + self.cpv * self.T_supAHU[t]) * self.x_sup # Batteries Demand [kW] self.AHUDemand[t] = G_da_vent * self.cp_air * ( self.T_supAHU[t] - self.T_as) self.AHUDemand_sens[t] = self.AHUDemand[t] self.AHUDemand_lat[t] = 0 elif AHUOnOff == -1: # Cooling mode if self.T_supAHU[t] > self.T_mix: self.T_supAHU[t] = self.T_mix self.x_sup = self.x_mix self.h_sup = self.h_mix else: SatCond, psat = checkSatCond(self.T_supAHU[t], self.x_mix, self.p_atm) if SatCond == False: # print('Info: supply temperature is too low -- changed') if self.T_supAHU[t] < 0: p_sat = 610.5 * np.exp( (21.875 * self.T_supAHU[t]) / (265.5 + self.T_supAHU[t])) else: p_sat = 610.5 * np.exp( (17.269 * self.T_supAHU[t]) / (237.3 + self.T_supAHU[t])) self.x_sup = 0.99 * 0.622 * p_sat / (self.p_atm - 0.99 * p_sat) self.h_sup = self.cp_air * self.T_supAHU[t] + ( self.r_0 + self.cpv * self.T_supAHU[t]) * self.x_sup else: self.x_sup = self.x_mix self.h_sup = self.cp_air * self.T_supAHU[t] + ( self.r_0 + self.cpv * self.T_supAHU[t]) * self.x_sup # Batteries Demand [kW] self.AHUDemand[t] = G_da_vent * (self.h_sup - self.h_mix) self.AHUDemand_sens[t] = self.AHUDemand[t] self.AHUDemand_lat[t] = 0 elif AHUOnOff == 0: # Plant OFF self.T_hr, self.T_mix, self.T_supAHU[t] = T_ext, T_ext, T_ext self.x_hr, self.x_mix, self.x_sup = x_ext, x_ext, x_ext self.h_hr, self.h_mix, self.h_sup = self.h_ext, self.h_ext, self.h_ext # Batteries Demand [kW] self.AHUDemand[t] = 0 self.AHUDemand_sens[t] = 0 self.AHUDemand_lat[t] = 0 else: sys.exit('AHUOnOff value not allowed at time step: ' + str(t))
def __init__(self,name,azSubdiv,hSubdiv,wwr,rh_gross,vertList=[[0,0,0],[0,0,0],[0,0,0]],surfType='ExtWall'): ''' input: list of vertices: [[x1,y1,z1],[x2,y2,z2],[x3,y3,z3],..] typology: one of these strings: 'ExtWall','Roof','GroundFloor' complanarity: https://www.geeksforgeeks.org/program-to-check-whether-4-points-in-a-3-d-plane-are-coplanar/ the area is calculated from: https://stackoverflow.com/questions/12642256/python-find-area-of-polygon-from-xyz-coordinates Parameters ---------- name : string name of the surface azSubdiv: int number of azimuth subdivision hSubdiv: int number of tilt subdivision wwr : list of floats four floats with the window to wall ratio (N E S W) rh_gross : float multiplicator of the surface area vertList : list of list of floats (polygon) list with n lists of three floats (n vertices) surfType : string string that defines the surface type. 'ExtWall' or 'GroundFloor' or 'Roof' Returns ------- None. ''' # Check input data type if not isinstance(name, str): raise TypeError(f'ERROR Surface class geometry, name is not a string: name {name}') if not isinstance(azSubdiv, int): raise TypeError(f'ERROR Surface class geometry, azSubdiv is not a int: azSubdiv {azSubdiv}') if not isinstance(hSubdiv, int): raise TypeError(f'ERROR Surface class geometry, azSubdiv is not a int: azSubdiv {hSubdiv}') if not isinstance(wwr, list) or not isinstance(wwr[0], float) or not isinstance(wwr[1], float) or not isinstance(wwr[2], float) or not isinstance(wwr[3], float): raise TypeError(f'ERROR Surface class geometry, wwr is not a list of floats: wwr {wwr}') if not isinstance(rh_gross, float): raise TypeError(f'ERROR Surface class geometry, rh_gross is not a float: rh_gross {rh_gross}') if not surfType in ['ExtWall' , 'GroundFloor' , 'Roof' ]: raise TypeError(f'ERROR Surface class geometry, surfType is not a correct string: surfType {surfType}') if not isinstance(vertList, list): raise TypeError(f'ERROR Surface class geometry , the input is not a list: input {vertList}') for vtx in vertList: if not isinstance(vtx, list): raise TypeError(f'ERROR Surface class geometry, an input is not a list: input {vtx}') if len(vtx) != 3: raise TypeError(f'ERROR Surface class geometry, a vertex is not a list of 3 components: input {vtx}') try: vtx[0] = float(vtx[0]) vtx[1] = float(vtx[1]) vtx[2] = float(vtx[2]) except ValueError: raise ValueError(f'ERROR Surface class geometry, a coordinate is not a float: input {vtx}') # Check input data quality if azSubdiv > 10 or hSubdiv > 5: wrn(f"WARNING Surface class, init, solar calculation could be long..... azSubdiv {azSubdiv}, hSubdiv {hSubdiv}") for wwr_ in wwr: if wwr_ < 0. or wwr_ > 0.9: wrn(f"WARNING Surface class, init, are you sure about the window to wall ratio ? wwr {wwr}") if rh_gross < 0.5 or rh_gross > 1.5: wrn(f"WARNING Surface class, init, are you sure about the external walls multiplication coeff?? rh_gross {rh_gross}") self.name = name # Check coplanarity if not check_complanarity(vertList): print('surface points not planar') self.type = '--' self.area = 0. self.normal = 0. self.height = 0. self.azimuth = 0. return self.OnOff_shading = 'Off' self.type = surfType self.vertList = vertList # Area calculation self.area = poly_area(self.vertList) if self.area == 0.: self.area = 0.0000001 ''' Considering only three points in calculating the normal vector could create reverse orientations if the three points are in a non-convex angle of the surface for this reason theres an alternative way to calculate the normal, implemented in function: normalAlternative reference: https://stackoverflow.com/questions/32274127/how-to-efficiently-determine-the-normal-to-a-polygon-in-3d-space ''' #self.normal = unit_normal(self.vertList[0], self.vertList[1], self.vertList[2]) self.normal = normalAlternative(self.vertList) # set the azimuth and zenith if self.normal[2] == 1: self.height = 0 self.azimuth = 0 elif self.normal[2] == -1: self.height= 180 self.azimuth = 0 else: self.height = 90 - np.degrees(np.arctan((self.normal[2]/(np.sqrt(self.normal[0]**2+self.normal[1]**2))))) if self.normal[1] == 0: if self.normal[0] > 0: self.azimuth = -90 elif self.normal[0] < 0: self.azimuth = 90 else: if self.normal[1]<0: self.azimuth = np.degrees(np.arctan(self.normal[0]/self.normal[1])) else: if self.normal[0] < 0: self.azimuth = 180 + np.degrees(np.arctan(self.normal[0]/self.normal[1])) else: self.azimuth = -180 + np.degrees(np.arctan(self.normal[0]/self.normal[1])) # Set surface inclination if self.height < 40: self.type = 'Roof' if self.height > 150: self.type = 'GroundFloor' if self.type == 'ExtWall': self.area = self.area*rh_gross # Azimuth and tilt approximation delta_a = 360/(2*azSubdiv) delta_h = 90/(2*hSubdiv) x = np.arange(-delta_h,90+2*delta_h,2*delta_h) for n in range(len(x)-1): if self.height >= x[n] and self.height < x[n+1]: self.height_round = int((x[n]+x[n+1])/2) self.F_r = (1+np.cos(np.radians(self.height_round)))/2 elif self.height >= x[-1] and self.height < 150: self.height_round = 90 self.F_r = (1+np.cos(np.radians(self.height_round)))/2 else: self.height_round = 0 # Only to avoid errors y = np.arange(-180-delta_a,180+2*delta_a,2*delta_a) for n in range(len(y)-1): if self.azimuth >= y[n] and self.azimuth < y[n+1]: self.azimuth_round = int((y[n]+y[n+1])/2) if self.azimuth_round == 180: self.azimuth_round = -180 if self.height_round == 0: self.azimuth_round = 0 # Set the window area if self.type == 'ExtWall': self.centroid_coord = centroid(vertList) if 135 < self.azimuth_round <= 180 or -180 <= self.azimuth_round < -135: self.wwr = wwr[0] elif -135 <= self.azimuth_round <= -45: self.wwr = wwr[1] elif -45 < self.azimuth_round < 45: self.wwr = wwr[2] elif 45 <= self.azimuth_round <= 135: self.wwr = wwr[3] else: self.wwr = 0 self.opaqueArea = (1-self.wwr)*self.area self.glazedArea = (self.wwr)*self.area if self.glazedArea == 0: self.glazedArea = 0.0000001 #Avoid zero division if self.opaqueArea == 0: self.opaqueArea = 0.0000001 #Avoid zero division
def __init__(self, epw_name, input_path='.', tz='Europe/Rome', year=2020, ts=2, hours=8760, n_years=1, irradiances_calc=True, azSubdiv=8, hSubdiv=3, shad_tol=[80., 100., 80.]): ''' initialize weather obj It processes the epw file to extract arrays of temperature, wind, humidity etc...... Parameters ---------- epw_name : str path of the epw file. input_path : str path of the Input files. tz : str this is the time zone. For a list of the avilable see: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones year : int the year of simulation. it is used only to create a pd.DataFrame. ts : int number of time steps in a hour. hours : int number of hours n_years : int number of years of sim azSubdiv: int number of the different direction (azimuth) solar radiation will be calculated hSubdiv: int number of the different direction (solar height) solar radiation will be calculated shad_tol: list of floats list of the tollerances for urban shading calc (azimuth, disance, theta) Returns ------- None ''' # Check input data type if not isinstance(epw_name, str): raise TypeError( f'ERROR Weather object, init, input epw_file is not a string: epw_file {epw_name}' ) if not isinstance(input_path, str): raise TypeError( f'ERROR Weather object, init, input input_path is not a string: input_path {input_path}' ) if not isinstance(tz, str): raise TypeError( f'ERROR Weather object, init, input tz is not a string: tz {tz}' ) if not isinstance(year, int): try: year = int(year) except: raise TypeError( f'ERROR Weather object, init, input year is not an int: year {year}' ) if not isinstance(ts, int): try: ts = int(ts) except: raise TypeError( f'ERROR Weather object, init, input ts is not an int: ts {ts}' ) if not isinstance(hours, int): try: hours = int(hours) except: raise TypeError( f'ERROR Weather object, init, input hours is not an int: hours {hours}' ) if not isinstance(n_years, int): try: n_years = int(n_years) except: raise TypeError( f'ERROR Weather object, init, input n_years is not an int: n_years {n_years}' ) if not isinstance(azSubdiv, int): raise TypeError( f'ERROR Weather object, init, input azSubdiv is not an integer: azSubdiv {azSubdiv}' ) if not isinstance(hSubdiv, int): raise TypeError( f'ERROR Weather object, init, input hSubdiv is not an integer: hSubdiv {hSubdiv}' ) if not isinstance(shad_tol, list): raise TypeError( f'ERROR Weather object, init, input shad_tol is not list: shad_tol {shad_tol}' ) if not isinstance(shad_tol[0], float) or not isinstance( shad_tol[1], float) or not isinstance(shad_tol[2], float): try: shad_tol[0] = float(shad_tol[0]) shad_tol[1] = float(shad_tol[1]) shad_tol[2] = float(shad_tol[2]) except: raise TypeError( f'ERROR Weather object, init, input shad_tol is not a list of floats: shad_tol {shad_tol}' ) # Check input data quality if ts > 4: wrn(f"WARNING Weather object, init, input ts is higher than 4, this means more than 4 time steps per hours were set: ts {ts}" ) if azSubdiv > 10 or hSubdiv > 5: wrn(f"WARNING Weather object, init, solar calculation could be long..... azSubdiv {azSubdiv}, hSubdiv {hSubdiv}" ) if shad_tol[0] < 0.0 or shad_tol[0] > 90.0: wrn(f"WARNING Weather object, init, toll_az is out of range [0-90]..... toll_az {shad_tol[0]}" ) if shad_tol[1] < 0.0 or shad_tol[1] > 200.0: wrn(f"WARNING Weather object, init, toll_dist is out of range [0-200]..... toll_dist {shad_tol[1]}" ) if shad_tol[2] < 0.0 or shad_tol[2] > 90.0: wrn(f"WARNING Weather object, init, toll_theta is out of range [0-90]..... toll_theta {shad_tol[2]}" ) epw_file = os.path.join(input_path, epw_name) # Importing and processing weather data from .epw try: epw = pvlib.iotools.read_epw( epw_file, coerce_year=year) # Reading the epw via pvlib except FileNotFoundError: raise FileNotFoundError( f"ERROR Weather epw file not found in the Input folder: epw name {epw_name}, input folder {input_path}" ) epw_res = epw[0].reset_index(drop=True) # Exporting the hourly values lat, lon = epw[1]['latitude'], epw[1][ 'longitude'] # Extracting latitude and longitude from the epw site = pvlib.location.Location(lat, lon, tz=tz) # Creating a location variable time = np.arange(8760) # Time vector inizialization # Weather Data and Average temperature difference between Text and Tsky w = epw_res['wind_speed'] # [m/s] T_ext = epw_res['temp_air'] # [°C] T_ext_H_avg = np.mean([T_ext[0:2160], T_ext[6599:8759]]) # [°C] RH_ext = epw_res['relative_humidity'] / 100 # [0-1] T_dp = epw_res['temp_dew'] # [°C] P_ = epw_res['atmospheric_pressure'] # [Pa] n_opaque = epw_res['opaque_sky_cover'] # [0-10] dT_er = TskyCalc( T_ext, T_dp, P_, n_opaque) # Average temperature difference between Text and Tsky w, T_ext, RH_ext, T_dp, P_, n_opaque = rescale_weather( ts, w, T_ext, RH_ext, T_dp, P_, n_opaque) Solar_position = site.get_solarposition( times=epw[0].index).reset_index(drop=True) Solar_position = SolarPosition(ts, Solar_position) # Inizialization of Solar Gain DataFrame # Creates a dataframe with the hourly solar radiation ond angle of incidence for several directions if irradiances_calc: Irradiances = PlanesIrradiances(site, epw, year, azSubdiv, hSubdiv) Irradiances.Irradiances.to_csv( os.path.join(input_path, 'PlanesIrradiances.csv')) # Solar Gain DataFrame Solar_Gains = pd.read_csv(os.path.join(input_path, 'PlanesIrradiances.csv'), header=[0, 1, 2], index_col=[0]) Solar_Gains = rescale_sol_gain(ts, Solar_Gains) # Check some weather data values if not np.all(np.greater(T_ext, -50.)) or not np.all( np.less(T_ext, 60.)): wrn(f"WARNING Weather class, input T_ext is out of plausible range: T_ext {T_ext}" ) if not np.all(np.greater(w, -0.001)) or not np.all(np.less(w, 25.001)): wrn(f"WARNING Weather class, input w is out of plausible range: w {w}" ) if not np.all(np.greater(RH_ext, -0.0001)) or not np.all( np.less(RH_ext, 1.)): wrn(f"WARNING Weather class, input w is out of plausible range: w {w}" ) # Memorizing the attributes needed for the sim self.ts = ts # number of timesteps in an hour self.hours = hours # number of hours of a sim self.sim_time = [ts, hours] # self.tau = 3600 / ts # number of seconds of a time step (for the solution of the networks) self.Text = T_ext # External air temperature [°C] self.RHext = RH_ext # External relative humidity [0-1] self.azSubdiv = azSubdiv # Number of azimuth subdivision [-] self.hSubdiv = hSubdiv # Number of height subdivision [-] self.w = w # wind velocity [m/s] self.dT_er = dT_er # Average temperature diff between external air and sky vault[°C] self.THextAv = T_ext_H_avg # Average heating season air temperature [°C] self.SolarPosition = Solar_position self.SolarGains = Solar_Gains # tollerances for urban shading calculation self.Az_toll = shad_tol[0] # [°] self.Dist_toll = shad_tol[1] # [m] self.Theta_toll = shad_tol[2] # [°]
def rescale_weather(ts, w, T_ext, RH_ext, T_dp, P_, n_opaque): ''' rescales weather variables arrays returns numpy arrays!!!! Parameters ---------- ts : int Number of time steps per hour. w : dataframe column External wind speed [m/s] T_ext : dataframe column External dry bulb temperature [°C] RH_ext : dataframe column External relative humidity [0-1] T_dp : dataframe column External dew point temperature [°C] P_ : dataframe column External pressure [Pa] n_opaque : dataframe column Opaque sky covering [-] Returns ------- w: np array, wind speed [m/s] t: np array, temperature [°C] rh: np array, relative humidity [0-1] dp: np array, dew point temperature [°C] p: np array, pressure [Pa] nop: np array, opaque sky covering [-] ''' # Check input data type if not isinstance(ts, int): raise TypeError(f'ERROR input ts is not an integer: ts {ts}') if not isinstance(w, pd.core.series.Series): raise TypeError(f'ERROR input w is not a pandas series: w {w}') if not isinstance(T_ext, pd.core.series.Series): raise TypeError( f'ERROR input T_ext is not a pandas series: T_ext {T_ext}') if not isinstance(RH_ext, pd.core.series.Series): raise TypeError( f'ERROR input RH_ext is not a pandas series: RH_ext {RH_ext}') if not isinstance(T_dp, pd.core.series.Series): raise TypeError( f'ERROR input T_dp is not a pandas series: T_dp {T_dp}') if not isinstance(P_, pd.core.series.Series): raise TypeError(f'ERROR input P_ is not a pandas series: P_ {P_}') if not isinstance(n_opaque, pd.core.series.Series): raise TypeError( f'ERROR input n_opaque is not a pandas series: n_opaque {n_opaque}' ) # Check input data quality if not np.all(np.greater(w, -0.001)) or not np.all(np.less(w, 25.001)): wrn(f"WARNING rescale_weather function, input w is out of plausible range: w {w}" ) # Rescale the data with pandas rescale data = pd.DataFrame(zip(w, T_ext, RH_ext, T_dp, P_, n_opaque), columns=['w', 't', 'rh', 'dp', 'p', 'nop']) m = str(60 / float(ts)) + 'min' time = pd.date_range('2020-01-01', periods=8760, freq='1h') data.set_index(time, inplace=True) data = data.resample(m).interpolate( method='linear') # Steps interpolation Resampling return data['w'].to_numpy(), data['t'].to_numpy(), data['rh'].to_numpy( ), data['dp'].to_numpy(), data['p'].to_numpy(), data['nop'].to_numpy()
def __init__(self, site, epw, year=2020, azSubdiv=8, hSubdiv=3): ''' initialize PlaneIrradiances object Parameters ---------- site : pvlib.location.Location Location object from pvlib. epw : tuple from pvlib read_epw contains 2 dataframe with the epw characteristics year : int year of the simulation, just to set the dataframe index azSubdiv: int number of the different direction (azimuth) solar radiation will be calculated hSubdiv: int number of the different direction (solar height) solar radiation will be calculated Returns ------- None ''' # Check input data type if not isinstance(site, pvlib.location.Location): raise TypeError( f'ERROR input site is not a pvlib location object: site {site}' ) if not isinstance(epw, tuple): raise TypeError(f'ERROR input epw is not a tuple: epw {epw}') if not isinstance(epw[0], pd.core.frame.DataFrame): raise TypeError( f'ERROR input epw[0] is not a pandas dataframe: epw[0] {epw[0]}' ) if not isinstance(epw[1], dict): raise TypeError( f'ERROR input epw[1] is not a dictionary: epw[1] {epw[1]}') if not isinstance(year, int): raise TypeError(f'ERROR input year is not an integer: year {year}') if not isinstance(azSubdiv, int): raise TypeError( f'ERROR input azSubdiv is not an integer: azSubdiv {azSubdiv}') if not isinstance(hSubdiv, int): raise TypeError( f'ERROR input hSubdiv is not an integer: hSubdiv {hSubdiv}') # Control input data quality if azSubdiv > 10 or hSubdiv > 5: wrn(f"WARNING PlanesIrradiances class, init, solar calculation could be long..... azSubdiv {azSubdiv}, hSubdiv {hSubdiv}" ) # Creates the dataframe with global beam and Angle of Incidence for many directions time = epw[0].index irradiance = epw[0][['ghi', 'dni', 'dhi']] self.solar_position = site.get_solarposition(times=time) azimuth = np.linspace(-180, 180, azSubdiv + 1)[:-1] height = np.linspace(90, 0, hSubdiv + 1)[:-1] components = ['global', 'direct', 'AOI'] col = pd.MultiIndex.from_product( [azimuth, height, components], names=['Azimuth', 'Height', 'Component']) col = col.union( pd.MultiIndex.from_product( [[0], [0], components], names=['Azimuth', 'Height', 'Component'])) Irradiances = pd.DataFrame(0., index=time, columns=col) for az in azimuth: for h in height: POA = get_irradiance(site, time, self.solar_position, h, az, irradiance, year) Irradiances[az, h, 'global'] = POA['POA'] Irradiances[az, h, 'direct'] = POA['POA_B'] Irradiances[az, h, 'AOI'] = POA['AOI'] POA = get_irradiance(site, time, self.solar_position, 0, 0, irradiance, year) Irradiances[0, 0, 'global'] = POA['POA'] Irradiances[0, 0, 'direct'] = POA['POA_B'] Irradiances[0, 0, 'AOI'] = POA['AOI'] self.Irradiances = Irradiances
def get_irradiance(site, time, solar_position, surf_tilt, surf_az, irradiance, year): ''' function from pvlib to calculate irradiance on a specific surface https://pvlib-python.readthedocs.io/en/stable/auto_examples/plot_ghi_transposition.html#sphx-glr-auto-examples-plot-ghi-transposition-py Parameters ---------- site : pvlib.location.Location Location object from pvlib. time: pandas series time index of the epw object from pvlib solar_position : pandas dataframe the solar position dataframe from pvlib surf_tilt: float tilt of the surface surf_az: float azimuth of the surface irradiance: pandas dataframe dataframe from the epw with ghi, dni, dhi year : int year of the simulation, just to set the dataframe index Returns ------- pandas DataFrame with the irradiances on the surface ''' # Check input data type if not isinstance(site, pvlib.location.Location): raise TypeError( f'ERROR input site is not a pvlib location object: site {site}') if not isinstance(time, pd.core.indexes.datetimes.DatetimeIndex): raise TypeError( f'ERROR input time is not a pandas series: time {time}') if not isinstance(solar_position, pd.core.frame.DataFrame): raise TypeError( f'ERROR input solar_position is not a pandas dataframe: solar_position {solar_position}' ) if not isinstance(surf_tilt, float): try: surf_tilt = float(surf_tilt) except: raise TypeError( f'ERROR input surf_tilt is not a float: surf_tilt {surf_tilt}') if not isinstance(surf_az, float): try: surf_az = float(surf_az) except: raise TypeError( f'ERROR input surf_az is not a float: surf_az {surf_az}') if not isinstance(irradiance, pd.core.frame.DataFrame): raise TypeError( f'ERROR input irradiance is not a pandas dataframe: irradiance {irradiance}' ) if not isinstance(year, int): try: year = int(year) except: raise TypeError(f'ERROR input year is not an integer: year {year}') # Control input data quality if surf_tilt < 0 or surf_tilt > 90 or surf_az < -180 or surf_az > 180: wrn(f"WARNING get_irradiance funtion, are you sure that the surface orientation is correct?? surf_tilt {surf_tilt}, surf_az {surf_az}" ) # Use pvlib function to calculate the irradiance on the surface surf_az = surf_az + 180 POA_irradiance = pvlib.irradiance.get_total_irradiance( surface_tilt=surf_tilt, surface_azimuth=surf_az, dni_extra=pvlib.irradiance.get_extra_radiation(time, solar_constant=1366.1, method='spencer', epoch_year=year), dni=irradiance['dni'], ghi=irradiance['ghi'], dhi=irradiance['dhi'], solar_zenith=solar_position['apparent_zenith'], solar_azimuth=solar_position['azimuth'], model='isotropic', model_perez='allsitescomposite1990', airmass=site.get_airmass(solar_position=solar_position)) AOI = pvlib.irradiance.aoi(surface_tilt=surf_tilt, surface_azimuth=surf_az, solar_zenith=solar_position['apparent_zenith'], solar_azimuth=solar_position['azimuth']) # Cleaning AOI vector for i in range(len(AOI)): if AOI[i] > 90 or solar_position['apparent_zenith'][i] > 90: AOI[i] = 90 return pd.DataFrame({ 'GHI': irradiance['ghi'], 'POA': POA_irradiance['poa_global'], 'POA_B': POA_irradiance['poa_direct'], 'POA_D': POA_irradiance['poa_global'] - POA_irradiance['poa_direct'], 'AOI': AOI, 'solar zenith': solar_position['apparent_zenith'] })
def loadSimpleArchetype(path, timeIndex, first_day=1, ts=1, PlantDays=[2520, 3984, 6192, 6912]): ''' Archetype loading in case you use loadSchedSemp This function takes the path of the excel file constining the schedules and loads it Works for the daily schedule (see file ScheduleSemp.xlsx in /Input/ Parameters ---------- path : string Path containing the string of the file_schedule.xlsx timeIndex : np array of int This is the array containing the index of the simulation time steps . first_day : int First day of the week (1 monday, 2 tuesday, ..... 7 sunday) ts : int Number of time steps per hour. PlantDays : list List of integers that sets the 4 time steps of heating and cooling season start and stops [last heating timestep, first cooling timestep, last cooling timstep, fist heating time step] Returns ------- archetypes: dictionary with archetype_key/Archetype(object) data ''' # Check input data type if not isinstance(path, str): raise TypeError(f'ERROR input path is not a string: path {path}') if not isinstance(timeIndex, np.ndarray): raise TypeError( f'ERROR input timeIndex is not a np.array: timeIndex {timeIndex}') if not isinstance(first_day, int): raise TypeError( f'ERROR input first_day is not an integer: first_day {first_day}') if not isinstance(ts, int): raise TypeError(f'ERROR input ts is not an integer: ts {ts}') if not isinstance(PlantDays, list): raise TypeError( f'ERROR input PlantDays is not a list: PlantDays {PlantDays}\nThis parameter must be a list of integers (default [2520,3984,6192,6912])' ) if not isinstance(PlantDays[0], int) or not isinstance( PlantDays[1], int) or not isinstance( PlantDays[2], int) or not isinstance(PlantDays[3], int): raise TypeError( f'ERROR input PlantDays in not a list of integers: PlantDays {PlantDays}' ) # Control input data quality if first_day > 7 or first_day < 1: wrn(f"WARNING loadSimpleArchetype function, input fisrt_day should be in the range [0,7]: first_day {first_day}" ) if ts > 4: wrn(f"WARNING loadSimpleArchetype function, input ts is higher than 4, this means more than 4 time steps per hours were set: ts {ts}" ) try: ex = pd.ExcelFile(path) except FileNotFoundError: raise FileNotFoundError( f'ERROR Failed to open the schedule xlsx file {path}... Insert a proper path' ) # Creation of the archetypes' dictionary archetypes = dict() last_H_day = PlantDays[0] first_C_day = PlantDays[1] last_C_day = PlantDays[2] first_H_day = PlantDays[3] # read archetype names from the excel sheet archetype_list = pd.read_excel(path, sheet_name='GeneralData', header=[0], index_col=[0], skiprows=1) names = archetype_list.index for use in names: archetypes[use] = Archetype(use) year = pd.DataFrame() schedule = pd.read_excel(path, sheet_name=use, header=[0, 1, 2], index_col=[0]) schedule = schedule.iloc[1:25] * schedule.loc['Valore nominale'] week = pd.concat([schedule['Weekday']] * 5 + [schedule['Weekend']] * 2) # The heating cooling availabilty is create considering the PlantDays variable first_week = week.iloc[(first_day - 1) * 24:] year = pd.concat([first_week] + [week] * 53).iloc[:8760].set_index(timeIndex) year.loc[last_H_day:first_C_day, ('Plant Availability', '[-]')] = year.loc[last_H_day:first_C_day, ('Plant Availability', '[-]')] * 0 year.loc[first_C_day:last_C_day, ('Plant Availability', '[-]')] = year.loc[first_C_day:last_C_day, ('Plant Availability', '[-]')] * -1 year.loc[last_C_day:first_H_day, ('Plant Availability', '[-]')] = year.loc[last_C_day:first_H_day, ('Plant Availability', '[-]')] * 0 archetypes[use].loadSchedSemp(year, archetype_list.loc[use]) archetypes[use].rescale_df(ts) archetypes[use].create_np() return archetypes
def loadSchedSemp(self, year_df, supplementary_data): ''' Used for ScheduleSemp.xlsx Excel file Parameters ---------- arch : pandas dataframe This includes the yearly schedule of a single archetype supplementary_data : pd.series This series includes some additional data about the archetype (Sensible and Latent AHU recovery, Convective fraction of internal gains) Returns ------- None. ''' # Each schedule is set to a different attribute of the class try: self.sched_df['appliances'] = year_df['Appliances', '[W/m²]'] self.sched_df['lighting'] = year_df['Lighting', '[W/m²]'] self.sched_df['people'] = year_df['Occupancy (Sensible)', '[W/m²]'] self.sched_df['vapour'] = year_df[ 'Vapour FlowRate', '[g/(m² s)]'] / 1000 #--> kg conversion (a) self.sched_df['heatingTSP'] = year_df['HeatSP', '[°C]'] self.sched_df['coolingTSP'] = year_df['CoolSP', '[°C]'] self.sched_df['HeatingRHSP'] = year_df['HumSP', '[%]'] / 100 self.sched_df['CoolingRHSP'] = year_df['DehumSP', '[%]'] / 100 self.sched_df['ventFlowRate'] = year_df['Ventilation FlowRate', '[m³/(s m²)]'] self.sched_df['infFlowRate'] = year_df['Infiltration FlowRate', '[Vol/h]'] self.sched_df['plantOnOffSens'] = year_df['Plant Availability', '[-]'] self.sched_df['plantOnOffLat'] = year_df['Plant Availability', '[-]'] self.sched_df['AHUOnOff'] = year_df['Plant Availability', '[-]'] except KeyError: raise KeyError( f'ERROR Archetype object {self.name}: can not find all schedules' ) # Following parameters are not set in the Excel file self.sched_df['AHUTSupp'] = pd.Series( [22.] * 8760, index=self.sched_df['appliances'].index) self.sched_df['AHUxSupp'] = pd.Series( [0.005] * 8760, index=self.sched_df['appliances'].index) self.sched_df['AHUxSupp'].iloc[3984:6192] = 0.007 try: self.scalar_data['conFrac'] = float( supplementary_data.loc['ConvFrac']) self.scalar_data['AHUHUM'] = bool(supplementary_data.loc['AHUHum']) self.scalar_data['sensRec'] = float( supplementary_data.loc['SensRec']) self.scalar_data['latRec'] = float( supplementary_data.loc['LatRec']) self.scalar_data['outdoorAirRatio'] = float( supplementary_data.loc['OutAirRatio']) except KeyError: raise KeyError( f"ERROR Loading end use {self.name}. GeneralData does not have the correct columns names: ConvFrac, AHUHum, SensRec, LatRec, OutAirRatio" ) except ValueError: raise ValueError(f"""ERROR Loading end use {self.name}. GeneralData I'm not able to parse the General data. ConvFrac should be a float {supplementary_data['ConvFrac']} AHUHum should be a boolean {supplementary_data['AHUHum']} SensRec should be a float {supplementary_data['SensRec']} LatRec should be a float {supplementary_data['LatRec']} OutAirRatio should be a float {supplementary_data['OutAirRatio']} """) # Check the quality of input data if not 0. <= self.scalar_data['conFrac'] <= 1.: wrn(f"WARNING Loading end use {self.name}. Convective fraction of the heat gain outside boundary condition [0-1]: ConvFrac {self.scalar_data['conFrac']}" ) if not 0. <= self.scalar_data['sensRec'] <= 1.: wrn(f"WARNING Loading end use {self.name}. Sensible recovery of the AHU outside boundary condition [0-1]: sensRec {self.scalar_data['sensRec']}" ) if not 0. <= self.scalar_data['latRec'] <= 1.: wrn(f"WARNING Loading end use {self.name}. Latent recovery of the AHU outside boundary condition [0-1]: sensRec {self.scalar_data['latRec']}" ) if not 0. <= self.scalar_data['outdoorAirRatio'] <= 1.: wrn(f"WARNING Loading end use {self.name}. Outdoor air ratio of the AHU outside boundary condition [0-1]: outdoorAirRatio {self.scalar_data['outdoorAirRatio']}" )
def loadArchetype(path, timeIndex, ts): ''' Archetype loading in case you use loadSchedComp This function takes the path of the excel file constining the schedules and loads it Works for the yearly schedule (see file ScheduleComp.xlsx in /Input/ Parameters ---------- path : string Path containing the string of the file_schedule.xlsx timeIndex : np array of int This is the array containing the index of the simulation time steps . ts : int Number of time steps per hour. Returns ------- archetypes: dictionary with archetype_key/Archetype(object) data ''' # Check input data type if not isinstance(path, str): raise TypeError(f'ERROR input path is not a string: path {path}') if not isinstance(timeIndex, np.ndarray): raise TypeError( f'ERROR input timeIndex is not a np.array: timeIndex {timeIndex}') if not isinstance(ts, int): raise TypeError(f'ERROR input ts is not an integer: ts {ts}') # Control input data quality if ts > 4: wrn(f"WARNING loadSimpleArchetype function, input ts is higher than 4, this means more than 4 time steps per hours were set: ts {ts}" ) try: sched = pd.read_excel(path, sheet_name="Schedule", header=2, index_col=[0]).set_index(timeIndex) arch = pd.read_excel(path, sheet_name="Archetype", header=1, index_col=[0]) except FileNotFoundError: raise FileNotFoundError( f'ERROR Failed to open the schedule xlsx file {path}... Insert a proper path' ) # Reset some indexes set the right index sched = sched.reset_index().drop(columns=["Time"]) sched.index = timeIndex # Creation of the archetypes' dictionary archetypes = dict() for i in arch.index: if i != 'Archetype': archetypes[i] = Archetype(i) archetypes[i].loadSchedComp(arch.loc[i], sched) archetypes[i].rescale_df(ts) archetypes[i].create_np() return archetypes