OpenSimula 0.8.9__tar.gz → 0.8.10__tar.gz
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.
- {opensimula-0.8.9/src/opensimula.egg-info → opensimula-0.8.10}/PKG-INFO +1 -1
- {opensimula-0.8.9 → opensimula-0.8.10}/pyproject.toml +1 -1
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/components/Building_surface.py +37 -6
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/components/DX_unit.py +90 -4
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/components/HVAC_MZW_system.py +29 -4
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/components/HVAC_SZW_system.py +26 -6
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/components/Opening.py +6 -2
- {opensimula-0.8.9 → opensimula-0.8.10/src/opensimula.egg-info}/PKG-INFO +1 -1
- {opensimula-0.8.9 → opensimula-0.8.10}/test/test_Case_600FF.py +3 -3
- {opensimula-0.8.9 → opensimula-0.8.10}/test/test_Case_610.py +4 -4
- {opensimula-0.8.9 → opensimula-0.8.10}/test/test_HVAC_DX_Equipment.py +21 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/test/test_HVAC_DX_system.py +11 -11
- {opensimula-0.8.9 → opensimula-0.8.10}/test/test_HVAC_FAN_COIL_water_system.py +10 -10
- {opensimula-0.8.9 → opensimula-0.8.10}/test/test_Project.py +39 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/LICENSE +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/README.md +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/setup.cfg +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/Child.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/Component.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/Iterative_process.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/Message.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/Parameter_container.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/Parameters.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/Project.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/Simulation.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/Variable.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/__init__.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/components/Building.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/components/Calculator.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/components/Chiller_heat_pump.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/components/Construction.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/components/Day_schedule.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/components/Fan.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/components/File_data.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/components/File_met.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/components/Frame.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/components/Glazing.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/components/HVAC_DX_system.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/components/HVAC_perfect_system.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/components/HVAC_water_system.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/components/Material.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/components/Opening_type.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/components/Pump.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/components/Solar_surface.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/components/Space.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/components/Space_type.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/components/Surface.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/components/Test_component.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/components/Water_coil.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/components/Week_schedule.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/components/Year_schedule.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/components/__init__.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/editor/__init__.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/editor/schema.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/editor/static/editor.css +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/editor/static/editor.js +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/editor/validation.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/editor/widget.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/visual_3D/Environment_3D.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula/visual_3D/Polygon_3D.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula.egg-info/SOURCES.txt +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula.egg-info/dependency_links.txt +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula.egg-info/requires.txt +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/src/opensimula.egg-info/top_level.txt +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/test/test_Calculator.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/test/test_Construction.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/test/test_File_data.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/test/test_File_met.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/test/test_HVAC_water_system_process.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/test/test_Iterative_process.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/test/test_Schedules.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/test/test_editor_schema.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/test/test_editor_widget.py +0 -0
- {opensimula-0.8.9 → opensimula-0.8.10}/test/test_underground_repeated_simulation.py +0 -0
|
@@ -12,6 +12,8 @@ from opensimula.Parameters import (
|
|
|
12
12
|
from opensimula.Variable import Variable
|
|
13
13
|
from opensimula.visual_3D.Polygon_3D import Polygon_3D
|
|
14
14
|
|
|
15
|
+
SIGMA = 5.670374419e-8 # Stefan-Boltzmann constant, W/m²K⁴
|
|
16
|
+
|
|
15
17
|
|
|
16
18
|
class Building_surface(Surface):
|
|
17
19
|
def __init__(self, name, project):
|
|
@@ -266,8 +268,10 @@ class Building_surface(Surface):
|
|
|
266
268
|
self.parameter("ground_material").component.parameter("specific_heat").value
|
|
267
269
|
)
|
|
268
270
|
d_p = math.sqrt((self._k_g_ * periodo_segundos) / (math.pi * rho_g * c_p_g))
|
|
269
|
-
|
|
270
|
-
|
|
271
|
+
# Periodic external heat transfer coefficient, UNE-EN ISO 13370:2017 (H_pe), per m²
|
|
272
|
+
P = self.perimeter * self.parameter("exterior_perimeter_fraction").value
|
|
273
|
+
self._U_hat_ext_ = (
|
|
274
|
+
self._G_w_ * 0.37 * P * self._k_g_ * math.log(d_p / self._d_t_ + 1) / self.area
|
|
271
275
|
)
|
|
272
276
|
|
|
273
277
|
def _create_openings_list_(self):
|
|
@@ -331,7 +335,7 @@ class Building_surface(Surface):
|
|
|
331
335
|
# Meterological data
|
|
332
336
|
hor_sol_dif = self._file_met.variable("sol_diffuse").values[time_i]
|
|
333
337
|
hor_sol_dir = self._file_met.variable("sol_direct").values[time_i]
|
|
334
|
-
|
|
338
|
+
self._T_sky = self._file_met.variable("sky_temperature").values[time_i]
|
|
335
339
|
|
|
336
340
|
# Diffuse solar radiation
|
|
337
341
|
E_dif_sunny = self._file_met.solar_diffuse_rad(
|
|
@@ -360,17 +364,44 @@ class Building_surface(Surface):
|
|
|
360
364
|
q_sol0 = self.radiant_property("alpha", "solar_diffuse", 0) * (E_dif + E_dir)
|
|
361
365
|
self.variable("q_sol0").values[time_i] = q_sol0
|
|
362
366
|
# Mean radiant temperature
|
|
363
|
-
T_rm = self._F_sky *
|
|
367
|
+
T_rm = self._F_sky * self._T_sky + (1 - self._F_sky) * self._T_ext
|
|
364
368
|
self.variable("T_rm").values[time_i] = T_rm
|
|
365
|
-
# f_0
|
|
366
369
|
h_rd = self.H_RD * self.radiant_property("alpha", "long_wave", 0)
|
|
370
|
+
T_s0 = self.variable("T_s0").values[time_i - 1] if time_i > 0 else self._T_ext
|
|
371
|
+
self._q_lw_corr = self.long_wave_correction(
|
|
372
|
+
T_s0, self.radiant_property("alpha", "long_wave", 0)
|
|
373
|
+
)
|
|
374
|
+
# f_0
|
|
367
375
|
self.f_0 = self.area * (
|
|
368
376
|
-self._p_0_
|
|
369
377
|
- self.parameter("h_cv").value[0] * self._T_ext
|
|
370
378
|
- h_rd * T_rm
|
|
371
379
|
- q_sol0
|
|
380
|
+
- self._q_lw_corr
|
|
372
381
|
)
|
|
373
382
|
|
|
383
|
+
def long_wave_correction(self, T_s0, epsilon):
|
|
384
|
+
"""Exterior long wave gain (W/m²) not included in the linearized exchange
|
|
385
|
+
|
|
386
|
+
The building matrices linearize the long wave exchange with the sky and the ground
|
|
387
|
+
with H_RD (20 °C); the difference with the exact exchange is added explicitly, with
|
|
388
|
+
the surface temperature of the previous time step.
|
|
389
|
+
|
|
390
|
+
Args:
|
|
391
|
+
T_s0 (float): exterior surface temperature (°C)
|
|
392
|
+
epsilon (float): long wave emissivity of the exterior surface
|
|
393
|
+
|
|
394
|
+
Returns:
|
|
395
|
+
float: exact minus linearized long wave gain (W/m²)
|
|
396
|
+
"""
|
|
397
|
+
T_rm = self._F_sky * self._T_sky + (1 - self._F_sky) * self._T_ext
|
|
398
|
+
exact = epsilon * SIGMA * (
|
|
399
|
+
self._F_sky * (self._T_sky + 273.15) ** 4
|
|
400
|
+
+ (1 - self._F_sky) * (self._T_ext + 273.15) ** 4
|
|
401
|
+
- (T_s0 + 273.15) ** 4
|
|
402
|
+
)
|
|
403
|
+
return exact - self.H_RD * epsilon * (T_rm - T_s0)
|
|
404
|
+
|
|
374
405
|
def _calculate_direct_sunny_fraction_(self, time_i):
|
|
375
406
|
if self.project().parameter("shadow_calculation").value == "INSTANT":
|
|
376
407
|
direct_sunny_fraction = self.project().env_3D.get_direct_sunny_fraction(
|
|
@@ -426,7 +457,7 @@ class Building_surface(Surface):
|
|
|
426
457
|
self.variable("q_lwt0").values[time_i] = h_rd * (
|
|
427
458
|
self.variable("T_rm").values[time_i]
|
|
428
459
|
- self.variable("T_s0").values[time_i]
|
|
429
|
-
)
|
|
460
|
+
) + self._q_lw_corr
|
|
430
461
|
self.variable("q_lwt1").values[time_i] = (
|
|
431
462
|
-self.variable("q_cd1").values[time_i]
|
|
432
463
|
- self.variable("q_cv1").values[time_i]
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
from opensimula.Message import Message
|
|
2
2
|
from opensimula.Parameters import Parameter_float, Parameter_float_list, Parameter_math_exp, Parameter_options, Parameter_boolean
|
|
3
3
|
from opensimula.Component import Component
|
|
4
|
-
from scipy.optimize import fsolve
|
|
4
|
+
from scipy.optimize import fsolve, brentq
|
|
5
|
+
import math
|
|
6
|
+
import psychrolib as sicro
|
|
5
7
|
|
|
6
8
|
class DX_unit(Component):
|
|
7
9
|
def __init__(self, name, project):
|
|
@@ -24,7 +26,7 @@ class DX_unit(Component):
|
|
|
24
26
|
self.add_parameter(Parameter_math_exp("heating_capacity_expression", "1", "frac"))
|
|
25
27
|
self.add_parameter(Parameter_math_exp("heating_power_expression", "1", "frac"))
|
|
26
28
|
self.add_parameter(Parameter_math_exp("COP_expression", "1", "frac"))
|
|
27
|
-
self.add_parameter(Parameter_options("dry_coil_model", "SENSIBLE", ["TOTAL", "SENSIBLE"]))
|
|
29
|
+
self.add_parameter(Parameter_options("dry_coil_model", "SENSIBLE", ["TOTAL", "SENSIBLE", "ADP"]))
|
|
28
30
|
self.add_parameter(Parameter_options("indoor_fan_operation", "CONTINUOUS", ["CONTINUOUS", "CYCLING"]))
|
|
29
31
|
self.add_parameter(Parameter_boolean("power_dry_coil_correction", True))
|
|
30
32
|
self.add_parameter(Parameter_float_list("expression_max_values", [60,30,60,30,1.5,1], "-"))
|
|
@@ -41,6 +43,11 @@ class DX_unit(Component):
|
|
|
41
43
|
errors.append(Message(msg, "ERROR"))
|
|
42
44
|
return errors
|
|
43
45
|
|
|
46
|
+
def pre_simulation(self, n_time_steps, delta_t):
|
|
47
|
+
super().pre_simulation(n_time_steps, delta_t)
|
|
48
|
+
self._A_0 = None # Bypass factor constant, calculated at the first cooling call
|
|
49
|
+
self._T_iwb_dry = None
|
|
50
|
+
|
|
44
51
|
def get_heating_load(self,T_idb,T_iwb,T_odb,T_owb,F_air,Q_required):
|
|
45
52
|
"""
|
|
46
53
|
Q_required: Required heating load
|
|
@@ -118,7 +125,11 @@ class DX_unit(Component):
|
|
|
118
125
|
sensible_capacity = self.parameter("nominal_sensible_cooling_capacity").value
|
|
119
126
|
f = self.parameter("sensible_cooling_capacity_expression").evaluate(var_dic)
|
|
120
127
|
sensible_capacity = sensible_capacity * f
|
|
121
|
-
|
|
128
|
+
self._T_iwb_dry = None
|
|
129
|
+
if self.parameter("dry_coil_model").value == "ADP":
|
|
130
|
+
total_capacity, sensible_capacity = self._adp_capacities(
|
|
131
|
+
T_idb, T_iwb, T_odb, T_owb, F_air, total_capacity, sensible_capacity)
|
|
132
|
+
elif (sensible_capacity > total_capacity):
|
|
122
133
|
if self.parameter("dry_coil_model").value == "SENSIBLE":
|
|
123
134
|
total_capacity = sensible_capacity
|
|
124
135
|
elif self.parameter("dry_coil_model").value == "TOTAL":
|
|
@@ -164,7 +175,10 @@ class DX_unit(Component):
|
|
|
164
175
|
|
|
165
176
|
def _get_correct_cooling_power(self,total_capacity, sensible_capacity, var_dic):
|
|
166
177
|
power = self.parameter("nominal_cooling_power").value
|
|
167
|
-
if
|
|
178
|
+
if self.parameter("dry_coil_model").value == "ADP":
|
|
179
|
+
if self._T_iwb_dry is not None and self.parameter("power_dry_coil_correction").value:
|
|
180
|
+
var_dic["T_iwb"] = self._T_iwb_dry
|
|
181
|
+
elif (sensible_capacity == total_capacity and self.parameter("power_dry_coil_correction").value):
|
|
168
182
|
T_iwb_min = self._get_min_T_iwb(var_dic)
|
|
169
183
|
var_dic["T_iwb"] = T_iwb_min
|
|
170
184
|
f = self.parameter("cooling_power_expression").evaluate(var_dic)
|
|
@@ -180,6 +194,78 @@ class DX_unit(Component):
|
|
|
180
194
|
root = fsolve(func, var_dic["T_iwb"],xtol=1e-3)
|
|
181
195
|
return root[0]
|
|
182
196
|
|
|
197
|
+
# Apparatus dew point (ADP) / bypass factor (BF) dry coil model:
|
|
198
|
+
# BF = exp(-A_0/m), A_0 from the nominal capacities; the coil is dry when the humidity
|
|
199
|
+
# ratio of the ADP is not below the inlet one.
|
|
200
|
+
def _adp_capacities(self, T_idb, T_iwb, T_odb, T_owb, F_air, total_capacity, sensible_capacity):
|
|
201
|
+
p = self._sim_.props["ATM_PRESSURE"]
|
|
202
|
+
if getattr(self, "_A_0", None) is None: # also used outside a simulation
|
|
203
|
+
self._A_0 = self._get_A_0(p)
|
|
204
|
+
w_in = sicro.GetHumRatioFromTWetBulb(T_idb, min(T_iwb, T_idb), p)
|
|
205
|
+
m_air = self._air_mass_flow(T_idb, w_in, F_air, p)
|
|
206
|
+
w_adp, SHR = self._adp_state(T_idb, w_in, m_air, total_capacity, p)
|
|
207
|
+
if w_adp >= w_in: # Dry coil: capacity at the wet bulb of the dry/wet boundary
|
|
208
|
+
T_iwb_dry = self._get_dry_T_iwb(T_idb, T_iwb, T_odb, T_owb, F_air, m_air, p)
|
|
209
|
+
var_dic = self._var_state_dic([T_idb, T_iwb_dry, T_odb, T_owb, F_air, 1])
|
|
210
|
+
total_capacity = (self.parameter("nominal_total_cooling_capacity").value
|
|
211
|
+
* self.parameter("total_cooling_capacity_expression").evaluate(var_dic))
|
|
212
|
+
self._T_iwb_dry = T_iwb_dry
|
|
213
|
+
return (total_capacity, total_capacity)
|
|
214
|
+
# Wet coil: the latent capacity cannot exceed that of the ADP model
|
|
215
|
+
sensible_capacity = max(sensible_capacity, total_capacity * SHR)
|
|
216
|
+
if sensible_capacity > total_capacity:
|
|
217
|
+
total_capacity = sensible_capacity
|
|
218
|
+
return (total_capacity, sensible_capacity)
|
|
219
|
+
|
|
220
|
+
def _get_A_0(self, p):
|
|
221
|
+
T_idb, T_iwb, _ = self.parameter("nominal_cooling_conditions").value
|
|
222
|
+
Q_tot = self.parameter("nominal_total_cooling_capacity").value
|
|
223
|
+
Q_sen = self.parameter("nominal_sensible_cooling_capacity").value
|
|
224
|
+
w_in = sicro.GetHumRatioFromTWetBulb(T_idb, T_iwb, p)
|
|
225
|
+
m_air = self._air_mass_flow(T_idb, w_in, 1, p)
|
|
226
|
+
h_in = sicro.GetMoistAirEnthalpy(T_idb, w_in)
|
|
227
|
+
h_out = h_in - Q_tot / m_air
|
|
228
|
+
c_p = sicro.GetMoistAirEnthalpy(1, w_in) - sicro.GetMoistAirEnthalpy(0, w_in)
|
|
229
|
+
T_out = T_idb - Q_sen / (m_air * c_p)
|
|
230
|
+
w_out = sicro.GetHumRatioFromEnthalpyAndTDryBulb(h_out, T_out)
|
|
231
|
+
# ADP: the line through inlet and outlet states meets the saturation curve
|
|
232
|
+
def func(T):
|
|
233
|
+
return sicro.GetSatHumRatio(T, p) - (w_in + (w_out - w_in) * (T - T_idb) / (T_out - T_idb))
|
|
234
|
+
T_adp = brentq(func, -40, T_out)
|
|
235
|
+
BF = (T_out - T_adp) / (T_idb - T_adp)
|
|
236
|
+
return -math.log(BF) * m_air
|
|
237
|
+
|
|
238
|
+
def _air_mass_flow(self, T_idb, w_in, F_air, p):
|
|
239
|
+
return self.parameter("nominal_air_flow").value * F_air / sicro.GetMoistAirVolume(T_idb, w_in, p)
|
|
240
|
+
|
|
241
|
+
def _adp_state(self, T_idb, w_in, m_air, total_capacity, p):
|
|
242
|
+
"""Humidity ratio of the ADP and sensible heat ratio for the given total capacity"""
|
|
243
|
+
BF = math.exp(-self._A_0 / m_air)
|
|
244
|
+
h_in = sicro.GetMoistAirEnthalpy(T_idb, w_in)
|
|
245
|
+
h_adp = h_in - total_capacity / m_air / (1 - BF)
|
|
246
|
+
if h_adp >= sicro.GetSatAirEnthalpy(T_idb, p): # No capacity: dry
|
|
247
|
+
return (sicro.GetSatHumRatio(T_idb, p), 1)
|
|
248
|
+
if h_adp <= sicro.GetSatAirEnthalpy(-60, p):
|
|
249
|
+
return (0, 0)
|
|
250
|
+
T_adp = brentq(lambda T: sicro.GetSatAirEnthalpy(T, p) - h_adp, -60, T_idb)
|
|
251
|
+
w_adp = sicro.GetSatHumRatio(T_adp, p)
|
|
252
|
+
h_tin_wadp = sicro.GetMoistAirEnthalpy(T_idb, w_adp)
|
|
253
|
+
SHR = min((h_tin_wadp - h_adp) / (h_in - h_adp), 1)
|
|
254
|
+
return (w_adp, SHR)
|
|
255
|
+
|
|
256
|
+
def _get_dry_T_iwb(self, T_idb, T_iwb, T_odb, T_owb, F_air, m_air, p):
|
|
257
|
+
"""Inlet wet bulb temperature at which the coil starts to be wet"""
|
|
258
|
+
def func(T_wb):
|
|
259
|
+
var_dic = self._var_state_dic([T_idb, T_wb, T_odb, T_owb, F_air, 1])
|
|
260
|
+
total = (self.parameter("nominal_total_cooling_capacity").value
|
|
261
|
+
* self.parameter("total_cooling_capacity_expression").evaluate(var_dic))
|
|
262
|
+
w = sicro.GetHumRatioFromTWetBulb(T_idb, T_wb, p)
|
|
263
|
+
return self._adp_state(T_idb, w, m_air, total, p)[0] - w
|
|
264
|
+
T_wb_min = min(T_iwb, T_idb)
|
|
265
|
+
if func(T_idb) >= 0: # Dry even at saturation
|
|
266
|
+
return T_idb
|
|
267
|
+
return brentq(func, T_wb_min, T_idb, xtol=1e-3)
|
|
268
|
+
|
|
183
269
|
def get_fan_power(self, f_load):
|
|
184
270
|
if self.parameter("indoor_fan_operation").value == "CONTINUOUS":
|
|
185
271
|
return self.parameter("indoor_fan_power").value
|
|
@@ -58,6 +58,7 @@ class HVAC_MZW_system(Component): # HVAC Multizone Water system
|
|
|
58
58
|
)
|
|
59
59
|
)
|
|
60
60
|
self.add_parameter(Parameter_math_exp("outdoor_air_fraction", "0", "frac"))
|
|
61
|
+
self.add_parameter(Parameter_math_exp("outdoor_air_flow", "0", "m³/s"))
|
|
61
62
|
self.add_parameter(Parameter_variable_list("input_variables", []))
|
|
62
63
|
self.add_parameter(Parameter_math_exp("supply_heating_setpoint", "10", "°C"))
|
|
63
64
|
self.add_parameter(Parameter_math_exp("supply_cooling_setpoint", "15", "°C"))
|
|
@@ -214,6 +215,16 @@ class HVAC_MZW_system(Component): # HVAC Multizone Water system
|
|
|
214
215
|
f"{self.parameter('name').value}, must define its reheat water system."
|
|
215
216
|
)
|
|
216
217
|
errors.append(Message(msg, "ERROR"))
|
|
218
|
+
# Test minimum outdoor air given only once
|
|
219
|
+
if _not_zero(self.parameter("outdoor_air_fraction").value) and _not_zero(
|
|
220
|
+
self.parameter("outdoor_air_flow").value
|
|
221
|
+
):
|
|
222
|
+
msg = f"{self.parameter('name').value}, outdoor_air_fraction and outdoor_air_flow cannot be used at the same time."
|
|
223
|
+
errors.append(Message(msg, "ERROR"))
|
|
224
|
+
# VAV systems get the outdoor air from the return air flow
|
|
225
|
+
if self.parameter("vav").value and _not_zero(self.parameter("outdoor_air_flow").value):
|
|
226
|
+
msg = f"{self.parameter('name').value}, outdoor_air_flow cannot be used in VAV systems."
|
|
227
|
+
errors.append(Message(msg, "ERROR"))
|
|
217
228
|
# Test file_met defined
|
|
218
229
|
if self.project().parameter("simulation_file_met").value == "not_defined":
|
|
219
230
|
msg = f"{self.parameter('name').value}, file_met must be defined in the project 'simulation_file_met'."
|
|
@@ -286,6 +297,7 @@ class HVAC_MZW_system(Component): # HVAC Multizone Water system
|
|
|
286
297
|
# outdoor air fraction
|
|
287
298
|
self.F_OA_min = self.parameter("outdoor_air_fraction").evaluate(var_dic)
|
|
288
299
|
self.F_OA = self.F_OA_min
|
|
300
|
+
self.V_OA = self.parameter("outdoor_air_flow").evaluate(var_dic)
|
|
289
301
|
# setpoints
|
|
290
302
|
self.T_heat_sp = self.parameter("supply_heating_setpoint").evaluate(var_dic)
|
|
291
303
|
self.variable("supply_heating_setpoint").values[time_index] = self.T_heat_sp
|
|
@@ -350,20 +362,26 @@ class HVAC_MZW_system(Component): # HVAC Multizone Water system
|
|
|
350
362
|
# Calculate F_OA based on return air flow
|
|
351
363
|
if self.is_vav:
|
|
352
364
|
self.F_OA = (self.air_flow * self.F_flow_total - v_return) / (self.air_flow* self.F_flow_total)
|
|
365
|
+
elif self.V_OA > 0:
|
|
366
|
+
self._set_outdoor_air_from_flow(rho_ZA)
|
|
353
367
|
|
|
354
|
-
#
|
|
355
|
-
|
|
356
|
-
|
|
368
|
+
# Zone mass balance: the minimum outdoor air is exhausted from the zones
|
|
369
|
+
self.m_return = self.m_air_supply * (1 - self.F_OA)
|
|
370
|
+
if self.return_fan is None or self.m_return <= 0: # No return fan
|
|
357
371
|
self.T_RA = self.T_ZA
|
|
358
372
|
else:
|
|
359
373
|
# Calculate flow fraction in return fan
|
|
360
|
-
self.m_return = v_return * rho_ZA
|
|
361
374
|
Q_return_fan = self._get_fan_power(
|
|
362
375
|
"return", self.F_load, v_return/self.return_air_flow
|
|
363
376
|
)
|
|
364
377
|
self.T_RA = Q_return_fan / (self.m_return * self.props["C_PA"]) + self.T_ZA
|
|
365
378
|
|
|
366
379
|
|
|
380
|
+
def _set_outdoor_air_from_flow(self, rho_ZA):
|
|
381
|
+
# Outdoor air flow given at zone air conditions (the air exhausted from the zones)
|
|
382
|
+
self.F_OA_min = min(self.V_OA * rho_ZA / self.m_air_supply, 1)
|
|
383
|
+
self.F_OA = self.F_OA_min
|
|
384
|
+
|
|
367
385
|
def _calculate_mixed_air(self, f_oa):
|
|
368
386
|
# Mixed air
|
|
369
387
|
self.T_MA, self.w_MA, self.T_MAwb = self._mix_air(f_oa, self.T_OA, self.w_OA, self.T_RA, self.w_RA)
|
|
@@ -717,3 +735,10 @@ class HVAC_MZW_system(Component): # HVAC Multizone Water system
|
|
|
717
735
|
self.variable("T_ADP").values[time_index] = self.T_ADP
|
|
718
736
|
self.variable("epsilon").values[time_index] = self.epsilon
|
|
719
737
|
self.variable("epsilon_adp").values[time_index] = self.epsilon_adp
|
|
738
|
+
|
|
739
|
+
|
|
740
|
+
def _not_zero(expression):
|
|
741
|
+
try:
|
|
742
|
+
return float(expression) != 0
|
|
743
|
+
except ValueError: # Expression with variables
|
|
744
|
+
return True
|
|
@@ -30,6 +30,7 @@ class HVAC_SZW_system(Component): # HVAC Single Zone Water system
|
|
|
30
30
|
self.add_parameter(Parameter_float("air_flow", 1, "m³/s", min=0))
|
|
31
31
|
self.add_parameter(Parameter_float("return_air_flow", 1, "m³/s", min=0))
|
|
32
32
|
self.add_parameter(Parameter_math_exp("outdoor_air_fraction", "0", "frac"))
|
|
33
|
+
self.add_parameter(Parameter_math_exp("outdoor_air_flow", "0", "m³/s"))
|
|
33
34
|
self.add_parameter(Parameter_variable_list("input_variables", []))
|
|
34
35
|
self.add_parameter(Parameter_math_exp("heating_setpoint", "20", "°C"))
|
|
35
36
|
self.add_parameter(Parameter_math_exp("cooling_setpoint", "25", "°C"))
|
|
@@ -146,6 +147,12 @@ class HVAC_SZW_system(Component): # HVAC Single Zone Water system
|
|
|
146
147
|
f"{self.parameter('name').value}, must define its heating water system."
|
|
147
148
|
)
|
|
148
149
|
errors.append(Message(msg, "ERROR"))
|
|
150
|
+
# Test minimum outdoor air given only once
|
|
151
|
+
if _not_zero(self.parameter("outdoor_air_fraction").value) and _not_zero(
|
|
152
|
+
self.parameter("outdoor_air_flow").value
|
|
153
|
+
):
|
|
154
|
+
msg = f"{self.parameter('name').value}, outdoor_air_fraction and outdoor_air_flow cannot be used at the same time."
|
|
155
|
+
errors.append(Message(msg, "ERROR"))
|
|
149
156
|
# Test file_met defined
|
|
150
157
|
if self.project().parameter("simulation_file_met").value == "not_defined":
|
|
151
158
|
msg = f"{self.parameter('name').value}, file_met must be defined in the project 'simulation_file_met'."
|
|
@@ -199,6 +206,7 @@ class HVAC_SZW_system(Component): # HVAC Single Zone Water system
|
|
|
199
206
|
# outdoor air fraction
|
|
200
207
|
self.F_OA_min = self.parameter("outdoor_air_fraction").evaluate(var_dic)
|
|
201
208
|
self.F_OA = self.F_OA_min
|
|
209
|
+
self.V_OA = self.parameter("outdoor_air_flow").evaluate(var_dic)
|
|
202
210
|
# setpoints
|
|
203
211
|
self.T_heat_sp = self.parameter("heating_setpoint").evaluate(var_dic)
|
|
204
212
|
self.variable("heating_setpoint").values[time_index] = self.T_heat_sp
|
|
@@ -218,10 +226,6 @@ class HVAC_SZW_system(Component): # HVAC Single Zone Water system
|
|
|
218
226
|
self.epsilon_adp = 0
|
|
219
227
|
# Initial mass flows
|
|
220
228
|
self.m_air_supply = self.air_flow * self.props["RHO_A"]
|
|
221
|
-
if self.return_fan is None: # No return fan
|
|
222
|
-
self.v_air_return = self.air_flow * (1 - self.F_OA)
|
|
223
|
-
else: # Return fan
|
|
224
|
-
self.v_air_return = self.return_air_flow
|
|
225
229
|
|
|
226
230
|
def iteration(self, time_index, date, daylight_saving, n_iter):
|
|
227
231
|
super().iteration(time_index, date, daylight_saving, n_iter)
|
|
@@ -245,8 +249,11 @@ class HVAC_SZW_system(Component): # HVAC Single Zone Water system
|
|
|
245
249
|
self.T_ZA, self.w_ZA / 1000, self.props["ATM_PRESSURE"]
|
|
246
250
|
)
|
|
247
251
|
self.w_RA = self.w_ZA
|
|
248
|
-
|
|
249
|
-
|
|
252
|
+
if self.V_OA > 0:
|
|
253
|
+
self._set_outdoor_air_from_flow(rho_ZA)
|
|
254
|
+
# Zone mass balance: the minimum outdoor air is exhausted from the zone
|
|
255
|
+
self.m_air_return = self.m_air_supply * (1 - self.F_OA_min)
|
|
256
|
+
if self.return_fan is None or self.m_air_return <= 0: # No return fan
|
|
250
257
|
self.Q_return_fan_required = 0
|
|
251
258
|
self.T_RA = self.T_ZA
|
|
252
259
|
else:
|
|
@@ -258,6 +265,12 @@ class HVAC_SZW_system(Component): # HVAC Single Zone Water system
|
|
|
258
265
|
self.m_air_return * self.props["C_PA"]
|
|
259
266
|
)
|
|
260
267
|
|
|
268
|
+
def _set_outdoor_air_from_flow(self, rho_ZA):
|
|
269
|
+
# Outdoor air flow given at zone air conditions (the air exhausted from the zone)
|
|
270
|
+
self.F_OA_min = min(self.V_OA * rho_ZA / self.m_air_supply, 1)
|
|
271
|
+
if self.parameter("economizer").value == "NO" or self.F_OA < self.F_OA_min:
|
|
272
|
+
self.F_OA = self.F_OA_min
|
|
273
|
+
|
|
261
274
|
def _calculate_required_Q(self):
|
|
262
275
|
K_t, F_t = self.space.get_thermal_equation(False)
|
|
263
276
|
Q_supply_fan = self._get_fan_power("supply", self.F_load)
|
|
@@ -583,3 +596,10 @@ class HVAC_SZW_system(Component): # HVAC Single Zone Water system
|
|
|
583
596
|
self.variable("T_ADP").values[time_index] = self.T_ADP
|
|
584
597
|
self.variable("epsilon").values[time_index] = self.epsilon
|
|
585
598
|
self.variable("epsilon_adp").values[time_index] = self.epsilon_adp
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
def _not_zero(expression):
|
|
602
|
+
try:
|
|
603
|
+
return float(expression) != 0
|
|
604
|
+
except ValueError: # Expression with variables
|
|
605
|
+
return True
|
|
@@ -228,8 +228,12 @@ class Opening(Component):
|
|
|
228
228
|
T_rm = surface.variable("T_rm").values[time_i]
|
|
229
229
|
self.variable("T_rm").values[time_i] = T_rm
|
|
230
230
|
h_rd = self.H_RD * self.radiant_property("alpha", "long_wave", 0)
|
|
231
|
+
T_s0 = self.variable("T_s0").values[time_i - 1] if time_i > 0 else self._T_ext
|
|
232
|
+
self._q_lw_corr = surface.long_wave_correction(
|
|
233
|
+
T_s0, self.radiant_property("alpha", "long_wave", 0)
|
|
234
|
+
)
|
|
231
235
|
self.f_0 = self.area * (
|
|
232
|
-
-self.parameter("h_cv").value[0] * self._T_ext - h_rd * T_rm
|
|
236
|
+
-self.parameter("h_cv").value[0] * self._T_ext - h_rd * T_rm - self._q_lw_corr
|
|
233
237
|
)
|
|
234
238
|
# q_sol0 will be corrected by the building
|
|
235
239
|
|
|
@@ -312,7 +316,7 @@ class Opening(Component):
|
|
|
312
316
|
self.variable("q_lwt0").values[time_i] = h_rd * (
|
|
313
317
|
self.variable("T_rm").values[time_i]
|
|
314
318
|
- self.variable("T_s0").values[time_i]
|
|
315
|
-
)
|
|
319
|
+
) + self._q_lw_corr
|
|
316
320
|
self.variable("q_lwt1").values[time_i] = (
|
|
317
321
|
+self.variable("q_cd").values[time_i]
|
|
318
322
|
- self.variable("q_cv1").values[time_i]
|
|
@@ -319,6 +319,6 @@ def test_ff_temperature():
|
|
|
319
319
|
|
|
320
320
|
T = pro.component("spaces_1").variable("temperature").values
|
|
321
321
|
|
|
322
|
-
assert T.mean() == pytest.approx(25.
|
|
323
|
-
assert T.max() == pytest.approx(65.
|
|
324
|
-
assert T.min() == pytest.approx(-
|
|
322
|
+
assert T.mean() == pytest.approx(25.393174594921604)
|
|
323
|
+
assert T.max() == pytest.approx(65.59282242450941)
|
|
324
|
+
assert T.min() == pytest.approx(-12.419790814021622)
|
|
@@ -349,10 +349,10 @@ def test_HVAC_perfect_system():
|
|
|
349
349
|
peak_heating = load.max()/1000
|
|
350
350
|
peak_cooling = -load.min()/1000
|
|
351
351
|
|
|
352
|
-
assert annual_heating == pytest.approx(4.
|
|
353
|
-
assert annual_cooling == pytest.approx(4.
|
|
354
|
-
assert peak_heating == pytest.approx(3.
|
|
355
|
-
assert peak_cooling == pytest.approx(5.
|
|
352
|
+
assert annual_heating == pytest.approx(4.463690912071373)
|
|
353
|
+
assert annual_cooling == pytest.approx(4.184622421932999)
|
|
354
|
+
assert peak_heating == pytest.approx(3.1410488100839884)
|
|
355
|
+
assert peak_cooling == pytest.approx(5.470013279897169)
|
|
356
356
|
|
|
357
357
|
|
|
358
358
|
def test_interpolated_sunny_fraction():
|
|
@@ -47,3 +47,24 @@ def test_points():
|
|
|
47
47
|
assert dry_coil_Q[2] == pytest.approx(0,rel=1e-2)
|
|
48
48
|
|
|
49
49
|
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_adp_dry_coil_model():
|
|
53
|
+
sim = osim.Simulation()
|
|
54
|
+
pro = sim.new_project("pro")
|
|
55
|
+
pro.read_dict(case_dict)
|
|
56
|
+
dx = pro.component("HVAC_equipment")
|
|
57
|
+
dx.parameter("dry_coil_model").value = "ADP"
|
|
58
|
+
|
|
59
|
+
# Nominal conditions: the bypass factor reproduces the nominal sensible heat ratio
|
|
60
|
+
Q_eq, Q_sen, Q_lat, F_load = dx.get_cooling_load(26.7, 19.4, 35, 25, 1, -6136)
|
|
61
|
+
assert Q_sen / (Q_sen + Q_lat) == pytest.approx(6136 / 7951, rel=2e-2)
|
|
62
|
+
# Dry inlet air (26.7 °C, 13 °C wet bulb): dry coil, no latent load
|
|
63
|
+
Q_eq, Q_sen, Q_lat, F_load = dx.get_cooling_load(26.7, 13, 35, 25, 1, -3000)
|
|
64
|
+
assert Q_lat == 0
|
|
65
|
+
assert dx._T_iwb_dry > 13
|
|
66
|
+
# The latent capacity goes to 0 continuously approaching the dry/wet boundary
|
|
67
|
+
T_wb = dx._T_iwb_dry
|
|
68
|
+
lat = [dx.get_cooling_load(26.7, T_wb + dT, 35, 25, 1, -3000)[2] for dT in (0.05, 0.5, 1)]
|
|
69
|
+
assert 0 <= lat[0] < lat[1] < lat[2]
|
|
70
|
+
assert lat[0] < 0.1 * lat[2]
|
|
@@ -373,12 +373,12 @@ def test_HVAC_DX_system_without_vent():
|
|
|
373
373
|
power = pro.component("system").variable("power").values.sum()/1e6
|
|
374
374
|
annual_latent = pro.component("system").variable("Q_latent").values.sum()/1e6
|
|
375
375
|
|
|
376
|
-
assert annual_heating == pytest.approx(3.
|
|
377
|
-
assert annual_cooling == pytest.approx(
|
|
376
|
+
assert annual_heating == pytest.approx(3.505853012164859)
|
|
377
|
+
assert annual_cooling == pytest.approx(5.011342208430301)
|
|
378
378
|
assert peak_heating == pytest.approx(2.749886298191046)
|
|
379
|
-
assert peak_cooling == pytest.approx(5.
|
|
380
|
-
assert power == pytest.approx(14.
|
|
381
|
-
assert annual_latent == pytest.approx(0.
|
|
379
|
+
assert peak_cooling == pytest.approx(5.716379241482701)
|
|
380
|
+
assert power == pytest.approx(14.432399666361574)
|
|
381
|
+
assert annual_latent == pytest.approx(0.054370526548591265)
|
|
382
382
|
|
|
383
383
|
|
|
384
384
|
def test_HVAC_DX_system_with_vent():
|
|
@@ -400,10 +400,10 @@ def test_HVAC_DX_system_with_vent():
|
|
|
400
400
|
annual_latent = pro.component("system").variable("Q_latent").values.sum()/1e6
|
|
401
401
|
print(annual_heating,annual_cooling,peak_heating,peak_cooling,power,annual_latent)
|
|
402
402
|
|
|
403
|
-
assert annual_heating == pytest.approx(6.
|
|
404
|
-
assert annual_cooling == pytest.approx(4.
|
|
405
|
-
assert peak_heating == pytest.approx(
|
|
406
|
-
assert peak_cooling == pytest.approx(5.
|
|
407
|
-
assert power == pytest.approx(16.
|
|
408
|
-
assert annual_latent == pytest.approx(0.
|
|
403
|
+
assert annual_heating == pytest.approx(6.413918255026011)
|
|
404
|
+
assert annual_cooling == pytest.approx(4.0843062420685365)
|
|
405
|
+
assert peak_heating == pytest.approx(3.9487898560161283)
|
|
406
|
+
assert peak_cooling == pytest.approx(5.397096853455196)
|
|
407
|
+
assert power == pytest.approx(16.614980023519852)
|
|
408
|
+
assert annual_latent == pytest.approx(0.0753997559884013)
|
|
409
409
|
|
|
@@ -376,12 +376,12 @@ def test_HVAC_FAN_COIL_water_system():
|
|
|
376
376
|
peak_heating = Q_sens.max() / 1000
|
|
377
377
|
peak_cooling = -Q_sens.min() / 1000
|
|
378
378
|
|
|
379
|
-
assert annual_heating == pytest.approx(0.
|
|
380
|
-
assert annual_cooling == pytest.approx(4.
|
|
381
|
-
assert annual_humidification == pytest.approx(0.
|
|
379
|
+
assert annual_heating == pytest.approx(0.05794164810570399, rel=1e-5)
|
|
380
|
+
assert annual_cooling == pytest.approx(4.841634385823313, rel=1e-5)
|
|
381
|
+
assert annual_humidification == pytest.approx(0.5815140376898394, rel=1e-5)
|
|
382
382
|
assert annual_dehumidification == pytest.approx(0.0)
|
|
383
|
-
assert peak_heating == pytest.approx(2.
|
|
384
|
-
assert peak_cooling == pytest.approx(5.
|
|
383
|
+
assert peak_heating == pytest.approx(2.4139930539173866, rel=1e-5)
|
|
384
|
+
assert peak_cooling == pytest.approx(5.52532910869739, rel=1e-5)
|
|
385
385
|
|
|
386
386
|
yearly_df = water.variable_dataframe(
|
|
387
387
|
frequency="yearly", value="sum", pos_neg_columns=["Q_gen"]
|
|
@@ -389,13 +389,13 @@ def test_HVAC_FAN_COIL_water_system():
|
|
|
389
389
|
heating_generation = yearly_df["Q_gen_pos"].values[0] / 1e6
|
|
390
390
|
cooling_generation = yearly_df["Q_gen_neg"].values[0] / 1e6
|
|
391
391
|
|
|
392
|
-
assert heating_generation == pytest.approx(0.
|
|
393
|
-
assert cooling_generation == pytest.approx(-4.
|
|
392
|
+
assert heating_generation == pytest.approx(0.05987756110752762, rel=1e-5)
|
|
393
|
+
assert cooling_generation == pytest.approx(-4.934717594594343, rel=1e-5)
|
|
394
394
|
|
|
395
395
|
T_WGO = water.variable("T_WGO").values
|
|
396
396
|
T_WGI = water.variable("T_WGI").values
|
|
397
397
|
|
|
398
|
-
assert T_WGO.max() == pytest.approx(50.
|
|
398
|
+
assert T_WGO.max() == pytest.approx(50.848459465012894, rel=1e-5)
|
|
399
399
|
assert T_WGO.min() == pytest.approx(7.0)
|
|
400
|
-
assert T_WGI.max() == pytest.approx(50.
|
|
401
|
-
assert T_WGI.min() == pytest.approx(7.
|
|
400
|
+
assert T_WGI.max() == pytest.approx(50.876345651587265, rel=1e-5)
|
|
401
|
+
assert T_WGI.min() == pytest.approx(7.041357495923282, rel=1e-5)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import pytest
|
|
1
2
|
import opensimula as osim
|
|
2
3
|
|
|
3
4
|
p1_dic = {
|
|
@@ -220,3 +221,41 @@ def test_opening_surface_type():
|
|
|
220
221
|
assert surface_type_errors() == []
|
|
221
222
|
wall.parameter("surface_type").value = "UNDERGROUND"
|
|
222
223
|
assert len(surface_type_errors()) == 1
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def test_outdoor_air_flow_or_fraction():
|
|
227
|
+
sim = osim.Simulation()
|
|
228
|
+
sim.console_print = False
|
|
229
|
+
p1 = sim.new_project("Project 1")
|
|
230
|
+
szw = p1.new_component("HVAC_SZW_system", "szw")
|
|
231
|
+
mzw = p1.new_component("HVAC_MZW_system", "mzw")
|
|
232
|
+
|
|
233
|
+
def outdoor_air_errors():
|
|
234
|
+
return [e for e in p1.check() if "outdoor_air" in e.text]
|
|
235
|
+
|
|
236
|
+
assert outdoor_air_errors() == []
|
|
237
|
+
for system in (szw, mzw):
|
|
238
|
+
system.parameter("outdoor_air_fraction").value = "0.3"
|
|
239
|
+
system.parameter("outdoor_air_flow").value = 0.1
|
|
240
|
+
assert len(outdoor_air_errors()) == 2
|
|
241
|
+
for system in (szw, mzw):
|
|
242
|
+
system.parameter("outdoor_air_fraction").value = 0
|
|
243
|
+
mzw.parameter("vav").value = True
|
|
244
|
+
assert len(outdoor_air_errors()) == 1
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def test_long_wave_correction():
|
|
248
|
+
sim = osim.Simulation()
|
|
249
|
+
p1 = sim.new_project("Project 1")
|
|
250
|
+
wall = p1.new_component("Building_surface", "wall")
|
|
251
|
+
wall._F_sky = 0.5
|
|
252
|
+
# Around 20 °C the linearized exchange is exact
|
|
253
|
+
wall._T_sky, wall._T_ext = 19.0, 20.0
|
|
254
|
+
assert abs(wall.long_wave_correction(20.5, 0.9)) < 0.01
|
|
255
|
+
# In winter the linearized exchange (H_RD at 20 °C) overestimates the loss to the sky
|
|
256
|
+
wall._T_sky, wall._T_ext = -20.0, 0.0
|
|
257
|
+
T_s0 = -2.0
|
|
258
|
+
T_rm = 0.5 * -20.0 + 0.5 * 0.0
|
|
259
|
+
linear = wall.H_RD * 0.9 * (T_rm - T_s0)
|
|
260
|
+
assert wall.long_wave_correction(T_s0, 0.9) > 0
|
|
261
|
+
assert wall.long_wave_correction(T_s0, 0.9) / -linear == pytest.approx(0.29, abs=0.02)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|