OpenSimula 0.5.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. opensimula/Child.py +19 -0
  2. opensimula/Component.py +214 -0
  3. opensimula/Iterative_process.py +47 -0
  4. opensimula/Message.py +19 -0
  5. opensimula/Parameter_container.py +80 -0
  6. opensimula/Parameters.py +755 -0
  7. opensimula/Project.py +674 -0
  8. opensimula/Simulation.py +212 -0
  9. opensimula/Variable.py +37 -0
  10. opensimula/__init__.py +8 -0
  11. opensimula/components/Building.py +511 -0
  12. opensimula/components/Building_surface.py +318 -0
  13. opensimula/components/Calculator.py +51 -0
  14. opensimula/components/Construction.py +309 -0
  15. opensimula/components/Day_schedule.py +64 -0
  16. opensimula/components/File_data.py +117 -0
  17. opensimula/components/File_met.py +1035 -0
  18. opensimula/components/Frame.py +35 -0
  19. opensimula/components/Glazing.py +122 -0
  20. opensimula/components/HVAC_DX_equipment.py +210 -0
  21. opensimula/components/HVAC_DX_system.py +254 -0
  22. opensimula/components/HVAC_MZW_system.py +554 -0
  23. opensimula/components/HVAC_SZW_system.py +338 -0
  24. opensimula/components/HVAC_coil_equipment.py +206 -0
  25. opensimula/components/HVAC_fan_equipment.py +44 -0
  26. opensimula/components/HVAC_perfect_system.py +137 -0
  27. opensimula/components/Material.py +14 -0
  28. opensimula/components/Opening.py +409 -0
  29. opensimula/components/Opening_type.py +90 -0
  30. opensimula/components/Solar_surface.py +114 -0
  31. opensimula/components/Space.py +388 -0
  32. opensimula/components/Space_type.py +81 -0
  33. opensimula/components/Surface.py +129 -0
  34. opensimula/components/Test_component.py +76 -0
  35. opensimula/components/Week_schedule.py +30 -0
  36. opensimula/components/Year_schedule.py +65 -0
  37. opensimula/components/__init__.py +40 -0
  38. opensimula/visual_3D/Environment_3D.py +257 -0
  39. opensimula/visual_3D/Polygon_3D.py +262 -0
  40. opensimula-0.5.1.dist-info/METADATA +49 -0
  41. opensimula-0.5.1.dist-info/RECORD +44 -0
  42. opensimula-0.5.1.dist-info/WHEEL +5 -0
  43. opensimula-0.5.1.dist-info/licenses/LICENSE +21 -0
  44. opensimula-0.5.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,35 @@
1
+ from opensimula.Component import Component
2
+ from opensimula.Parameters import Parameter_float, Parameter_float_list, Parameter_math_exp_list
3
+
4
+
5
+ class Frame(Component):
6
+ def __init__(self, name, project):
7
+ Component.__init__(self, name, project)
8
+ self.parameter("type").value = "Frame"
9
+ self.parameter(
10
+ "description").value = "Opening frame"
11
+
12
+ self.add_parameter(Parameter_float_list(
13
+ "solar_alpha", [0.85, 0.85], "frac", min=0, max=1))
14
+ self.add_parameter(Parameter_float_list(
15
+ "lw_epsilon", [0.9, 0.9], "frac", min=0, max=1))
16
+ self.add_parameter(Parameter_float(
17
+ "thermal_resistance", 0.2, "m²K/W", min=0))
18
+
19
+ def radiant_property(self, prop, radiation_type, side, theta=0):
20
+ if (radiation_type == "solar_diffuse" or radiation_type == "solar_direct"):
21
+ if (prop == "rho"):
22
+ return 1-self.parameter("solar_alpha").value[side]
23
+ elif (prop == "tau"):
24
+ return 0
25
+ elif (prop == "alpha"):
26
+ return self.parameter("solar_alpha").value[side]
27
+ elif (prop == "alpha_other_side"):
28
+ return 0
29
+ elif (radiation_type == "long_wave"):
30
+ if (prop == "rho"):
31
+ return 1-self.parameter("lw_epsilon").value[side]
32
+ elif (prop == "tau"):
33
+ return 0
34
+ elif (prop == "alpha"):
35
+ return self.parameter("lw_epsilon").value[side]
@@ -0,0 +1,122 @@
1
+ import math
2
+ from scipy.integrate import quad
3
+ from opensimula.Component import Component
4
+ from opensimula.Parameters import Parameter_float, Parameter_float_list, Parameter_math_exp, Parameter_math_exp_list
5
+
6
+
7
+ class Glazing(Component):
8
+ def __init__(self, name, project):
9
+ Component.__init__(self, name, project)
10
+ self.parameter("type").value = "Glazing"
11
+ self.parameter("description").value = "Glazing material."
12
+ self.add_parameter(Parameter_float(
13
+ "solar_tau", 0.849, "frac", min=0, max=1))
14
+ self.add_parameter(Parameter_float_list(
15
+ "solar_rho", [0.077, 0.077], "frac", min=0, max=1))
16
+ self.add_parameter(Parameter_float_list(
17
+ "g", [0.867093, 0.867093], "frac", min=0, max=1))
18
+ self.add_parameter(Parameter_float_list(
19
+ "lw_epsilon", [0.837, 0.837], "frac", min=0, max=1))
20
+ self.add_parameter(Parameter_float(
21
+ "U", 5.686, "W/m²K", min=0))
22
+ self.add_parameter(Parameter_math_exp(
23
+ "f_tau_nor", "1.3186 * cos_theta**3 - 3.5251 * cos_theta**2 + 3.2065 * cos_theta", "frac"))
24
+ f_rho = "1.8562 * cos_theta**3 - 4.4739 * cos_theta**2 + 3.6177 * cos_theta"
25
+ self.add_parameter(Parameter_math_exp_list(
26
+ "f_1_minus_rho_nor", [f_rho, f_rho], "frac"))
27
+
28
+ def pre_simulation(self, n_time_steps, delta_t):
29
+ super().pre_simulation(n_time_steps, delta_t)
30
+ self._calc_diffuse_properties()
31
+ self._calc_alpha_fractions()
32
+
33
+ def _calc_diffuse_properties(self):
34
+ def tau_integrand(theta):
35
+ tau_n = self.parameter("solar_tau").value
36
+ # variables dictonary
37
+ var_dic = {"cos_theta": math.cos(theta)}
38
+ f_tau = self.parameter("f_tau_nor").evaluate(var_dic)
39
+ tau = tau_n*f_tau
40
+ return 2*tau * math.sin(theta) * math.cos(theta)
41
+
42
+ def rho_0_integrand(theta):
43
+ rho_n = self.parameter("solar_rho").value[0]
44
+ # variables dictonary
45
+ var_dic = {"cos_theta": math.cos(theta)}
46
+ f_rho = self.parameter("f_1_minus_rho_nor").evaluate(0, var_dic)
47
+ rho = 1-(1-rho_n)*f_rho
48
+ return 2*rho * math.sin(theta) * math.cos(theta)
49
+
50
+ def rho_1_integrand(theta):
51
+ rho_n = self.parameter("solar_rho").value[1]
52
+ # variables dictonary
53
+ var_dic = {"cos_theta": math.cos(theta)}
54
+ f_rho = self.parameter("f_1_minus_rho_nor").evaluate(1, var_dic)
55
+ rho = 1-(1-rho_n)*f_rho
56
+ return 2*rho * math.sin(theta) * math.cos(theta)
57
+
58
+ tau = quad(tau_integrand, 0, math.pi/2)[0]
59
+ rho_0 = quad(rho_0_integrand, 0, math.pi/2)[0]
60
+ rho_1 = quad(rho_1_integrand, 0, math.pi/2)[0]
61
+ self.tau_solar_diffuse = tau
62
+ self.rho_solar_diffuse = [rho_0, rho_1]
63
+ self.alpha_solar_diffuse = [1-rho_0-tau, 1-rho_1-tau]
64
+
65
+ def _calc_alpha_fractions(self):
66
+ h_CR0 = 25
67
+ h_CR1 = 3.6 + 4.1/0.837 * \
68
+ self.parameter("lw_epsilon").value[1] # UNE-EN 410:2011
69
+ tau = self.parameter("solar_tau").value
70
+ U = self.parameter("U").value
71
+ if (U > 5): # simple glazing calculations are inestable
72
+ self.alpha_own_side_fraction = [0.5, 0.5]
73
+ else:
74
+ alpha_1 = 1 - tau - self.parameter("solar_rho").value[0]
75
+ g_1 = self.parameter("g").value[0]
76
+ alpha_11 = (g_1+alpha_1*U/h_CR1-alpha_1-tau)/(U/h_CR0+U/h_CR1-1)
77
+ alpha_2 = 1 - tau - self.parameter("solar_rho").value[1]
78
+ g_2 = self.parameter("g").value[1]
79
+ alpha_22 = (g_2+alpha_2*U/h_CR1-alpha_2-tau)/(U/h_CR0+U/h_CR1-1)
80
+ self.alpha_own_side_fraction = [alpha_11/alpha_1, alpha_22/alpha_2]
81
+
82
+ def thermal_resistance(self):
83
+ return (1/self.parameter("U").value - 1/25 - 1/(3.6 + 4.1/0.837 *
84
+ self.parameter("lw_epsilon").value[1])) # UNE-EN 673:2011
85
+
86
+ def radiant_property(self, prop, radiation_type, side, theta=0):
87
+ if (radiation_type == "solar_diffuse"):
88
+ if (prop == "rho"):
89
+ return self.rho_solar_diffuse[side]
90
+ elif (prop == "tau"):
91
+ return self.tau_solar_diffuse
92
+ elif (prop == "alpha"):
93
+ return self.alpha_solar_diffuse[side]*self.alpha_own_side_fraction[side]
94
+ elif (prop == "alpha_other_side"):
95
+ return self.alpha_solar_diffuse[side]*(1-self.alpha_own_side_fraction[side])
96
+ elif (radiation_type == "solar_direct"):
97
+ # variables dictonary
98
+ var_dic = {"cos_theta": math.cos(theta)}
99
+ if (prop == "rho"):
100
+ rho_n = self.parameter("solar_rho").value[side]
101
+ f_rho = self.parameter(
102
+ "f_1_minus_rho_nor").evaluate(side, var_dic)
103
+ return 1-(1-rho_n)*f_rho
104
+ elif (prop == "tau"):
105
+ return self.parameter("solar_tau").value * self.parameter("f_tau_nor").evaluate(var_dic)
106
+ elif (prop == "alpha"):
107
+ alpha = 1 - self.radiant_property("tau", radiation_type, side, theta) - \
108
+ self.radiant_property("rho", radiation_type, side, theta)
109
+ return alpha * self.alpha_own_side_fraction[side]
110
+ elif (prop == "alpha_other_side"):
111
+ alpha = 1 - self.radiant_property("tau", radiation_type, side, theta) - \
112
+ self.radiant_property("rho", radiation_type, side, theta)
113
+ return alpha * (1-self.alpha_own_side_fraction[side])
114
+ elif (radiation_type == "long_wave"):
115
+ if (prop == "rho"):
116
+ return 1-self.parameter("lw_epsilon").value[side]
117
+ elif (prop == "tau"):
118
+ return 0
119
+ elif (prop == "alpha"):
120
+ return self.parameter("lw_epsilon").value[side]
121
+ else:
122
+ pass
@@ -0,0 +1,210 @@
1
+ from opensimula.Message import Message
2
+ from opensimula.Parameters import Parameter_float, Parameter_float_list, Parameter_math_exp, Parameter_options, Parameter_boolean
3
+ from opensimula.Component import Component
4
+ from scipy.optimize import fsolve
5
+
6
+ class HVAC_DX_equipment(Component):
7
+ def __init__(self, name, project):
8
+ Component.__init__(self, name, project)
9
+ self.parameter("type").value = "HVAC_DX_equipment"
10
+ self.parameter("description").value = "HVAC Direct Expansion equipment manufacturer information"
11
+ self.add_parameter(Parameter_float("nominal_air_flow", 1, "m³/s", min=0))
12
+ self.add_parameter(Parameter_float("nominal_total_cooling_capacity", 0, "W", min=0))
13
+ self.add_parameter(Parameter_float("nominal_sensible_cooling_capacity", 0, "W", min=0))
14
+ self.add_parameter(Parameter_float("nominal_cooling_power", 0, "W", min=0))
15
+ self.add_parameter(Parameter_float("indoor_fan_power", 0, "W", min=0))
16
+ self.add_parameter(Parameter_float_list("nominal_cooling_conditions", [27, 19, 35], "ºC"))
17
+ self.add_parameter(Parameter_math_exp("total_cooling_capacity_expression", "1", "frac"))
18
+ self.add_parameter(Parameter_math_exp("sensible_cooling_capacity_expression", "1", "frac"))
19
+ self.add_parameter(Parameter_math_exp("cooling_power_expression", "1", "frac"))
20
+ self.add_parameter(Parameter_math_exp("EER_expression", "1", "frac"))
21
+ self.add_parameter(Parameter_float("nominal_heating_capacity", 0, "W", min=0))
22
+ self.add_parameter(Parameter_float("nominal_heating_power", 0, "W", min=0))
23
+ self.add_parameter(Parameter_float_list("nominal_heating_conditions", [20, 7, 6], "ºC"))
24
+ self.add_parameter(Parameter_math_exp("heating_capacity_expression", "1", "frac"))
25
+ self.add_parameter(Parameter_math_exp("heating_power_expression", "1", "frac"))
26
+ self.add_parameter(Parameter_math_exp("COP_expression", "1", "frac"))
27
+ self.add_parameter(Parameter_options("dry_coil_model", "SENSIBLE", ["TOTAL", "SENSIBLE"]))
28
+ self.add_parameter(Parameter_options("indoor_fan_operation", "CONTINUOUS", ["CONTINUOUS", "CYCLING"]))
29
+ self.add_parameter(Parameter_boolean("power_dry_coil_correction", True))
30
+ self.add_parameter(Parameter_float_list("expression_max_values", [60,30,60,30,1.5,1], "-"))
31
+ self.add_parameter(Parameter_float_list("expression_min_values", [0,0,-30,-30,0,0], "-"))
32
+
33
+ def check(self):
34
+ errors = super().check()
35
+ # Test Cooling and Heating conditions 3 values
36
+ if len(self.parameter("nominal_cooling_conditions").value)!= 3:
37
+ msg = f"Error: {self.parameter('name').value}, nominal_cooling_conditions size must be 3"
38
+ errors.append(Message(msg, "ERROR"))
39
+ if len(self.parameter("nominal_heating_conditions").value)!= 3:
40
+ msg = f"Error: {self.parameter('name').value}, nominal_heating_conditions size must be 3"
41
+ errors.append(Message(msg, "ERROR"))
42
+ return errors
43
+
44
+ def get_heating_load(self,T_idb,T_iwb,T_odb,T_owb,F_air,Q_required):
45
+ """
46
+ Q_required: Required heating load
47
+ Returns (Q_eq,Q_coil,f_load).
48
+ Q_eq: Net heat given by the equipment
49
+ Q_coil: Gross heat given by the coil
50
+ f_load: Fraction of load (0-1)
51
+ """
52
+ capacity = self.parameter("nominal_heating_capacity").value
53
+ if capacity > 0:
54
+ # variables dictonary
55
+ var_dic = self._var_state_dic([T_idb, T_iwb,T_odb,T_owb,F_air,1]) #Full load
56
+ # Capacity
57
+ cap_coil = capacity * self.parameter("heating_capacity_expression").evaluate(var_dic)
58
+ cap_eq = cap_coil + self.get_fan_heat(1)
59
+ if self.get_fan_heat(0) == 0: # Q_required is equipment load
60
+ if Q_required > cap_eq:
61
+ return (cap_eq,cap_coil,1)
62
+ else:
63
+ f_load = Q_required/cap_eq
64
+ return (Q_required, Q_required-self.get_fan_heat(f_load), f_load)
65
+ else: # Q_required is coil load
66
+ if Q_required > cap_coil:
67
+ return (cap_eq,cap_coil,1)
68
+ else:
69
+ f_load = Q_required/cap_coil
70
+ return (Q_required+self.get_fan_heat(f_load), Q_required, f_load)
71
+ else:
72
+ return (self.get_fan_heat(0),0, 0)
73
+
74
+ def get_heating_power(self,T_idb, T_iwb,T_odb,T_owb,F_air,Q_required):
75
+ """
76
+ Returns (power,indoor_fan_power,F_COP).
77
+ """
78
+ cap_nom = self.parameter("nominal_heating_capacity").value
79
+ if cap_nom > 0:
80
+ Q_eq, Q_coil, F_load = self.get_heating_load(T_idb, T_iwb,T_odb,T_owb,F_air,Q_required)
81
+ # variables dictonary
82
+ var_dic = self._var_state_dic([T_idb, T_iwb,T_odb,T_owb,F_air,F_load])
83
+ # Compressor
84
+ power_full = self.parameter("nominal_heating_power").value
85
+ power_full = power_full * self.parameter("heating_power_expression").evaluate(var_dic)
86
+ capacity = Q_coil/F_load
87
+ COP_full = capacity/power_full
88
+ F_COP = self.parameter("COP_expression").evaluate(var_dic)
89
+ COP = COP_full * F_COP
90
+ if self.parameter("indoor_fan_operation").value == "CONTINUOUS":
91
+ fan_power = self.parameter("indoor_fan_power").value
92
+ elif self.parameter("indoor_fan_operation").value == "CYCLING":
93
+ fan_power = self.parameter("indoor_fan_power").value*F_load/F_COP
94
+ return (capacity*F_load/COP + fan_power, fan_power, F_COP)
95
+ else:
96
+ fan_power = self.get_fan_power(0)
97
+ return (fan_power,fan_power,0)
98
+
99
+
100
+ def get_cooling_load(self,T_idb,T_iwb,T_odb,T_owb,F_air,Q_required):
101
+ """
102
+ Q_required: Required cooling load (negative for cooling)
103
+ Returns (Q_eq,Q_coil,Q_lat ,f_load).
104
+ Q_eq: Sensible net heat given by the equipment
105
+ Q_coil: Sensible gross heat given by the coil
106
+ Q_lat: Latent heat given by the coil
107
+ f_load: Fraction of load (0-1)
108
+ """
109
+ nom_total_capacity = self.parameter("nominal_total_cooling_capacity").value
110
+ if nom_total_capacity > 0:
111
+ Q_required = -Q_required # Positive
112
+ # variables dictonary
113
+ var_dic = self._var_state_dic([T_idb, T_iwb,T_odb,T_owb,F_air,1]) #Full load
114
+ # Total
115
+ f = self.parameter("total_cooling_capacity_expression").evaluate(var_dic)
116
+ total_capacity = nom_total_capacity * f
117
+ # Sensible
118
+ sensible_capacity = self.parameter("nominal_sensible_cooling_capacity").value
119
+ f = self.parameter("sensible_cooling_capacity_expression").evaluate(var_dic)
120
+ sensible_capacity = sensible_capacity * f
121
+ if (sensible_capacity > total_capacity):
122
+ if self.parameter("dry_coil_model").value == "SENSIBLE":
123
+ total_capacity = sensible_capacity
124
+ elif self.parameter("dry_coil_model").value == "TOTAL":
125
+ sensible_capacity = total_capacity
126
+ cap_eq = sensible_capacity - self.get_fan_heat(1)
127
+ if self.get_fan_heat(0) == 0: # Q_required is equipment load
128
+ if Q_required > cap_eq:
129
+ return (cap_eq,sensible_capacity,total_capacity-sensible_capacity,1)
130
+ else:
131
+ F_load = Q_required/cap_eq
132
+ return (Q_required, Q_required+self.get_fan_heat(F_load),(total_capacity-sensible_capacity)*F_load , F_load)
133
+ else: # Q_requiered is coil load
134
+ if Q_required > sensible_capacity:
135
+ return (cap_eq,sensible_capacity,total_capacity-sensible_capacity,1)
136
+ else:
137
+ F_load = Q_required/sensible_capacity
138
+ return (Q_required-self.get_fan_heat(F_load), Q_required,(total_capacity-sensible_capacity)*F_load , F_load)
139
+ else:
140
+ return (-self.get_fan_heat(0), 0, 0, 0)
141
+
142
+ def get_cooling_power(self,T_idb,T_iwb,T_odb,T_owb,F_air,Q_required):
143
+ """
144
+ Returns (power,indoor_fan_power,F_EER).
145
+ """
146
+ Q_eq, Q_sen, Q_lat, F_load = self.get_cooling_load(T_idb,T_iwb,T_odb,T_owb,F_air,Q_required)
147
+ if Q_sen > 0:
148
+ # variables dictonary
149
+ var_dic = self._var_state_dic([T_idb, T_iwb,T_odb,T_owb,F_air,F_load])
150
+ capacity_tot = (Q_sen+Q_lat)/F_load
151
+ capacity_sen = Q_sen/F_load
152
+ power_full = self._get_correct_cooling_power(capacity_tot,capacity_sen,var_dic)
153
+ EER_full = capacity_tot/power_full
154
+ F_EER = self.parameter("EER_expression").evaluate(var_dic)
155
+ EER = EER_full * F_EER
156
+ if self.parameter("indoor_fan_operation").value == "CONTINUOUS":
157
+ fan_power = self.parameter("indoor_fan_power").value
158
+ elif self.parameter("indoor_fan_operation").value == "CYCLING":
159
+ fan_power = self.parameter("indoor_fan_power").value*F_load/F_EER
160
+ return (capacity_tot*F_load/EER + fan_power, fan_power, F_EER)
161
+ else:
162
+ fan_power = self.get_fan_power(0)
163
+ return (fan_power,fan_power,0)
164
+
165
+ def _get_correct_cooling_power(self,total_capacity, sensible_capacity, var_dic):
166
+ power = self.parameter("nominal_cooling_power").value
167
+ if (sensible_capacity == total_capacity and self.parameter("power_dry_coil_correction").value):
168
+ T_iwb_min = self._get_min_T_iwb(var_dic)
169
+ var_dic["T_iwb"] = T_iwb_min
170
+ f = self.parameter("cooling_power_expression").evaluate(var_dic)
171
+ return (power * f)
172
+
173
+ def _get_min_T_iwb(self,var_dic):
174
+ total_capacity = self.parameter("nominal_total_cooling_capacity").value
175
+ sensible_capacity = self.parameter("nominal_sensible_cooling_capacity").value
176
+ def func(T_iwb):
177
+ var_dic["T_iwb"] = T_iwb
178
+ return (sensible_capacity*self.parameter("sensible_cooling_capacity_expression").evaluate(var_dic)-
179
+ total_capacity*self.parameter("total_cooling_capacity_expression").evaluate(var_dic))
180
+ root = fsolve(func, var_dic["T_iwb"],xtol=1e-3)
181
+ return root[0]
182
+
183
+ def get_fan_power(self, f_load):
184
+ if self.parameter("indoor_fan_operation").value == "CONTINUOUS":
185
+ return self.parameter("indoor_fan_power").value
186
+ elif self.parameter("indoor_fan_operation").value == "CYCLING":
187
+ return self.parameter("indoor_fan_power").value * f_load
188
+
189
+ def get_fan_heat(self, f_load):
190
+ return self.get_fan_power(f_load)
191
+
192
+ def _var_state_dic(self, values):
193
+ max = self.parameter("expression_max_values").value
194
+ min = self.parameter("expression_min_values").value
195
+ for i in range(len(values)):
196
+ if (values[i] > max[i]):
197
+ values[i] = max[i]
198
+ elif (values[i] < min[i]):
199
+ values[i] = min[i]
200
+ return {"T_idb":values[0],
201
+ "T_iwb":values[1],
202
+ "T_odb":values[2],
203
+ "T_owb":values[3],
204
+ "F_air":values[4],
205
+ "F_load":values[5]}
206
+
207
+
208
+
209
+
210
+
@@ -0,0 +1,254 @@
1
+ from opensimula.Message import Message
2
+ from opensimula.Parameters import Parameter_component, Parameter_float, Parameter_variable_list, Parameter_math_exp, Parameter_options
3
+ from opensimula.Component import Component
4
+ from opensimula.Variable import Variable
5
+ import psychrolib as sicro
6
+
7
+
8
+ class HVAC_DX_system(Component):
9
+ def __init__(self, name, project):
10
+ Component.__init__(self, name, project)
11
+ self.parameter("type").value = "HVAC_DX_system"
12
+ self.parameter("description").value = "HVAC Direct Expansion system for time simulation"
13
+ self.add_parameter(Parameter_component("equipment", "not_defined", ["HVAC_DX_equipment"]))
14
+ self.add_parameter(Parameter_component("spaces", "not_defined", ["Space"])) # Space, TODO: Add Air_distribution, Energy_load
15
+ self.add_parameter(Parameter_float("air_flow", 1, "m³/s", min=0))
16
+ self.add_parameter(Parameter_math_exp("outdoor_air_fraction", "0", "frac"))
17
+ self.add_parameter(Parameter_variable_list("input_variables", []))
18
+ self.add_parameter(Parameter_math_exp("heating_setpoint", "20", "°C"))
19
+ self.add_parameter(Parameter_math_exp("cooling_setpoint", "25", "°C"))
20
+ self.add_parameter(Parameter_math_exp("system_on_off", "1", "on/off"))
21
+ self.add_parameter(Parameter_options("economizer", "NO", ["NO", "TEMPERATURE","TEMPERATURE_NOT_INTEGRATED","ENTHALPY","ENTHALPY_LIMITED"]))
22
+ self.add_parameter(Parameter_float("economizer_DT", 0, "ºC", min=0))
23
+ self.add_parameter(Parameter_float("economizer_enthalpy_limit", 0, "kJ/kg", min=0))
24
+
25
+ # Variables
26
+ self.add_variable(Variable("state", unit="flag")) # 0: 0ff, 1: Heating, 2: Heating max cap, -1:Cooling, -2:Cooling max cap, 3: Venting
27
+ self.add_variable(Variable("T_OA", unit="°C"))
28
+ self.add_variable(Variable("T_OAwb", unit="°C"))
29
+ self.add_variable(Variable("T_MA", unit="°C"))
30
+ self.add_variable(Variable("T_MAwb", unit="°C"))
31
+ self.add_variable(Variable("F_air", unit="frac"))
32
+ self.add_variable(Variable("F_load", unit="frac"))
33
+ self.add_variable(Variable("outdoor_air_fraction", unit="frac"))
34
+ self.add_variable(Variable("m_air_flow", unit="kg/s"))
35
+ self.add_variable(Variable("T_SA", unit="°C"))
36
+ self.add_variable(Variable("w_SA", unit="g/kg"))
37
+ self.add_variable(Variable("Q_total", unit="W"))
38
+ self.add_variable(Variable("Q_sensible", unit="W"))
39
+ self.add_variable(Variable("Q_latent", unit="W"))
40
+ self.add_variable(Variable("power", unit="W"))
41
+ self.add_variable(Variable("indoor_fan_power", unit="W"))
42
+ self.add_variable(Variable("heating_setpoint", unit="°C"))
43
+ self.add_variable(Variable("cooling_setpoint", unit="°C"))
44
+ self.add_variable(Variable("EER", unit="frac"))
45
+ self.add_variable(Variable("COP", unit="frac"))
46
+ self.add_variable(Variable("efficiency_degradation", unit="frac"))
47
+
48
+
49
+ def check(self):
50
+ errors = super().check()
51
+ # Test equipment defined
52
+ if self.parameter("equipment").value == "not_defined":
53
+ msg = f"{self.parameter('name').value}, must define its equipment."
54
+ errors.append(Message(msg, "ERROR"))
55
+ # Test space defined
56
+ if self.parameter("spaces").value == "not_defined":
57
+ msg = f"{self.parameter('name').value}, must define its space."
58
+ errors.append(Message(msg, "ERROR"))
59
+ # Test file_met defined
60
+ if self.project().parameter("simulation_file_met").value == "not_defined":
61
+ msg = f"{self.parameter('name').value}, file_met must be defined in the project 'simulation_file_met'."
62
+ errors.append(Message(msg, "ERROR"))
63
+ return errors
64
+
65
+ def pre_simulation(self, n_time_steps, delta_t):
66
+ super().pre_simulation(n_time_steps, delta_t)
67
+ self._equipment = self.parameter("equipment").component
68
+ self._space = self.parameter("spaces").component
69
+ self._file_met = self.project().parameter("simulation_file_met").component
70
+ sicro.SetUnitSystem(sicro.SI)
71
+ self.props = self._sim_.props
72
+ self._air_flow = self.parameter("air_flow").value
73
+ self._f_air = self._air_flow / self._equipment.parameter("nominal_air_flow").value
74
+ self._f_load = 0
75
+ self._no_load_heat = self._equipment.get_fan_heat(0)
76
+ self._rho_i = self.props["RHO_A"]
77
+ self._economizer = self.parameter("economizer").value != "NO"
78
+ self._economizer_DT = self.parameter("economizer_DT").value
79
+
80
+ def pre_iteration(self, time_index, date, daylight_saving):
81
+ super().pre_iteration(time_index, date, daylight_saving)
82
+ # Outdoor air
83
+ self._T_odb = self._file_met.variable("temperature").values[time_index]
84
+ self._T_owb = self._file_met.variable("wet_bulb_temp").values[time_index]
85
+ self._w_o = self._file_met.variable("abs_humidity").values[time_index]
86
+ self.variable("T_OA").values[time_index] = self._T_odb
87
+ self.variable("T_OAwb").values[time_index] = self._T_owb
88
+ self._outdoor_rho = 1/sicro.GetMoistAirVolume(self._T_odb,self._w_o/1000,self.props["ATM_PRESSURE"])
89
+ # variables dictonary
90
+ var_dic = self.get_parameter_variable_dictionary(time_index)
91
+
92
+ # outdoor air fraction
93
+ self._outdoor_air_fraction = self.parameter("outdoor_air_fraction").evaluate(var_dic)
94
+ self._f_oa = self._outdoor_air_fraction
95
+
96
+ # setpoints
97
+ self._T_heat_sp = self.parameter("heating_setpoint").evaluate(var_dic)
98
+ self.variable("heating_setpoint").values[time_index] = self._T_heat_sp
99
+ self._T_cool_sp = self.parameter("cooling_setpoint").evaluate(var_dic)
100
+ self.variable("cooling_setpoint").values[time_index] = self._T_cool_sp
101
+ # on/off
102
+ self._on_off = self.parameter("system_on_off").evaluate(var_dic)
103
+ if self._on_off == 0:
104
+ self._state = 0
105
+ self.variable("state").values[time_index] = 0
106
+ self._on_off = False
107
+ else:
108
+ self._on_off = True
109
+
110
+ def iteration(self, time_index, date, daylight_saving, n_iter):
111
+ super().iteration(time_index, date, daylight_saving, n_iter)
112
+ space_air = {"M_a": 0, "T_a": 0, "w_a":0, "Q_s":0, "M_w":0 }
113
+ if self._on_off:
114
+ self._T_space = self._space.variable("temperature").values[time_index]
115
+ self._w_space = self._space.variable("abs_humidity").values[time_index]
116
+ # Calculate Q_required
117
+ self._calculate_required_Q()
118
+ if (self._economizer):
119
+ self._simulate_economizer() # Calculation of new f_oa
120
+ self._calculate_required_Q()
121
+ space_air = self._simulate_system()
122
+ self._space.set_control_system(space_air)
123
+ return True
124
+
125
+ def _calculate_required_Q(self):
126
+ K_t,F_t = self._space.get_thermal_equation(False)
127
+ m_cp_oa = self._air_flow * self._f_oa * self._rho_i * self.props["C_PA"]
128
+ K_ts = K_t + m_cp_oa
129
+ F_ts = F_t + m_cp_oa * self._T_odb + self._no_load_heat
130
+ T_flo = F_ts/K_ts
131
+ if T_flo > self._T_cool_sp:
132
+ self._Q_required = K_ts * self._T_cool_sp - F_ts
133
+ elif T_flo < self._T_heat_sp:
134
+ self._Q_required = K_ts * self._T_heat_sp - F_ts
135
+ else:
136
+ self._Q_required = 0
137
+ if abs(self._Q_required) < 0.0001: # Problems with convergence
138
+ self._Q_required = 0
139
+
140
+ def _simulate_economizer(self):
141
+ if (self.parameter("economizer").value == "TEMPERATURE" or self.parameter("economizer").value == "TEMPERATURE_NOT_INTEGRATED"):
142
+ on_economizer = self._T_odb < self._T_space-self._economizer_DT
143
+ elif (self.parameter("economizer").value == "ENTHALPY"):
144
+ h_odb = sicro.GetMoistAirEnthalpy(self._T_odb,self._w_o/1000)
145
+ h_space = sicro.GetMoistAirEnthalpy(self._T_space,self._w_space/1000)
146
+ on_economizer = h_odb < h_space
147
+ elif (self.parameter("economizer").value == "ENTHALPY_LIMITED"):
148
+ h_odb = sicro.GetMoistAirEnthalpy(self._T_odb,self._w_o/1000)
149
+ on_economizer = h_odb < self.parameter("economizer_enthalpy_limit").value * 1000
150
+
151
+ if (on_economizer):
152
+ if (self._Q_required < 0):
153
+ mrhocp = self._air_flow * self.props["C_PA"]* self._outdoor_rho
154
+ Q_rest_ae = mrhocp * (1-self._f_oa) * (self._T_odb - self._T_space)
155
+ if Q_rest_ae < self._Q_required:
156
+ self._f_oa += self._Q_required/(mrhocp * (self._T_odb-self._T_space))
157
+ self._Q_required = 0
158
+ else:
159
+ if (self.parameter("economizer").value == "TEMPERATURE_NOT_INTEGRATED"):
160
+ self._f_oa = self._outdoor_air_fraction
161
+ else:
162
+ self._f_oa = 1
163
+ elif (self._Q_required > 0): # Heating
164
+ self._f_oa = self._outdoor_air_fraction
165
+ else:
166
+ self._f_oa = self._outdoor_air_fraction
167
+
168
+ def _simulate_system(self):
169
+ # Venting
170
+ state = 3
171
+ f_load = 0
172
+ Q_coil = 0
173
+ Q_eq = self._equipment.get_fan_heat(0)
174
+ M_w = 0
175
+
176
+ self._T_idb, self._w_i, self._T_iwb = self._mix_air(self._f_oa, self._T_odb, self._w_o, self._T_space, self._w_space)
177
+ self._rho_i = 1/sicro.GetMoistAirVolume(self._T_idb,self._w_i/1000,self.props["ATM_PRESSURE"])
178
+
179
+ if self._Q_required > 0: # Heating
180
+ Q_eq,Q_coil,f_load = self._equipment.get_heating_load(self._T_idb, self._T_iwb, self._T_odb, self._T_owb,self._f_air,self._Q_required)
181
+ if f_load == 1:
182
+ state = 1
183
+ else:
184
+ state = 2
185
+ elif self._Q_required < 0: # Cooling
186
+ Q_eq, Q_coil, Q_lat,f_load = self._equipment.get_cooling_load(self._T_idb, self._T_iwb, self._T_odb,self._T_owb,self._f_air,self._Q_required)
187
+ if f_load == 1:
188
+ state = -2
189
+ else:
190
+ state = -1
191
+ M_w = -(Q_lat) / self.props["LAMBDA"]
192
+ Q_coil = -Q_coil
193
+ Q_eq = -Q_eq
194
+
195
+ self._state = state
196
+ self._Q_coil = Q_coil
197
+ self._Q_eq = Q_eq
198
+ self._M_w = M_w
199
+ self._f_load = f_load
200
+
201
+ air_flow = {"M_a": self._air_flow * self._f_oa * self._rho_i,
202
+ "T_a": self._T_odb,
203
+ "w_a":self._w_o,
204
+ "Q_s": Q_eq,
205
+ "M_w":self._M_w }
206
+ return air_flow
207
+
208
+ def _mix_air(self, f, T1, w1, T2, w2):
209
+ T = f * T1 + (1-f)*T2
210
+ w = f * w1 + (1-f)*w2
211
+ if (T > 100):
212
+ T_wb = 50 # Inventado
213
+ else:
214
+ T_wb = sicro.GetTWetBulbFromHumRatio(T,w/1000,self.props["ATM_PRESSURE"])
215
+ return (T,w,T_wb)
216
+
217
+ def post_iteration(self, time_index, date, daylight_saving, converged):
218
+ super().post_iteration(time_index, date, daylight_saving, converged)
219
+ self.variable("state").values[time_index] = self._state
220
+ if self._state != 0 : # on
221
+ self.variable("T_MA").values[time_index] = self._T_idb
222
+ self.variable("T_MAwb").values[time_index] = self._T_iwb
223
+ m_supply = self._air_flow * self._rho_i
224
+ self.variable("m_air_flow").values[time_index] = m_supply
225
+ self.variable("outdoor_air_fraction").values[time_index] = self._f_oa
226
+ self.variable("T_SA").values[time_index] = self._Q_eq/(m_supply*self.props["C_PA"]) + self._T_idb
227
+ self.variable("w_SA").values[time_index] = self._M_w/(self._air_flow * self._rho_i) +self._w_i
228
+ self.variable("F_air").values[time_index] = self._f_air
229
+ self.variable("F_load").values[time_index] = self._f_load
230
+ if self._state == 1 or self._state == 2: # Heating
231
+ power,fan_power, F_COP = self._equipment.get_heating_power(self._T_idb,self._T_iwb,self._T_odb,self._T_owb,self._f_air,self._Q_required)
232
+ Q_sys = self._Q_coil
233
+ self.variable("Q_sensible").values[time_index] = Q_sys
234
+ self.variable("Q_total").values[time_index] = Q_sys
235
+ self.variable("power").values[time_index] = power
236
+ self.variable("indoor_fan_power").values[time_index] = fan_power
237
+ if power>0:
238
+ self.variable("COP").values[time_index] = Q_sys/power
239
+ self.variable("efficiency_degradation").values[time_index] = F_COP
240
+ elif self._state == -1 or self._state == -2: #Cooling
241
+ power,fan_power,F_EER = self._equipment.get_cooling_power(self._T_idb,self._T_iwb,self._T_odb,self._T_owb,self._f_air,self._Q_required)
242
+ Q_s = -self._Q_coil
243
+ Q_l = -self._M_w * self.props["LAMBDA"]
244
+ self.variable("Q_sensible").values[time_index] = Q_s
245
+ self.variable("Q_latent").values[time_index] = Q_l
246
+ self.variable("Q_total").values[time_index] = Q_s + Q_l
247
+ self.variable("power").values[time_index] = power
248
+ self.variable("indoor_fan_power").values[time_index] = fan_power
249
+ if power>0:
250
+ self.variable("EER").values[time_index] = (Q_s+Q_l)/power
251
+ self.variable("efficiency_degradation").values[time_index] = F_EER
252
+ else: # venting
253
+ self.variable("power").values[time_index] = self._equipment.get_fan_power(0)
254
+ self.variable("indoor_fan_power").values[time_index] = self._equipment.get_fan_power(0)