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.
- opensimula/Child.py +19 -0
- opensimula/Component.py +214 -0
- opensimula/Iterative_process.py +47 -0
- opensimula/Message.py +19 -0
- opensimula/Parameter_container.py +80 -0
- opensimula/Parameters.py +755 -0
- opensimula/Project.py +674 -0
- opensimula/Simulation.py +212 -0
- opensimula/Variable.py +37 -0
- opensimula/__init__.py +8 -0
- opensimula/components/Building.py +511 -0
- opensimula/components/Building_surface.py +318 -0
- opensimula/components/Calculator.py +51 -0
- opensimula/components/Construction.py +309 -0
- opensimula/components/Day_schedule.py +64 -0
- opensimula/components/File_data.py +117 -0
- opensimula/components/File_met.py +1035 -0
- opensimula/components/Frame.py +35 -0
- opensimula/components/Glazing.py +122 -0
- opensimula/components/HVAC_DX_equipment.py +210 -0
- opensimula/components/HVAC_DX_system.py +254 -0
- opensimula/components/HVAC_MZW_system.py +554 -0
- opensimula/components/HVAC_SZW_system.py +338 -0
- opensimula/components/HVAC_coil_equipment.py +206 -0
- opensimula/components/HVAC_fan_equipment.py +44 -0
- opensimula/components/HVAC_perfect_system.py +137 -0
- opensimula/components/Material.py +14 -0
- opensimula/components/Opening.py +409 -0
- opensimula/components/Opening_type.py +90 -0
- opensimula/components/Solar_surface.py +114 -0
- opensimula/components/Space.py +388 -0
- opensimula/components/Space_type.py +81 -0
- opensimula/components/Surface.py +129 -0
- opensimula/components/Test_component.py +76 -0
- opensimula/components/Week_schedule.py +30 -0
- opensimula/components/Year_schedule.py +65 -0
- opensimula/components/__init__.py +40 -0
- opensimula/visual_3D/Environment_3D.py +257 -0
- opensimula/visual_3D/Polygon_3D.py +262 -0
- opensimula-0.5.1.dist-info/METADATA +49 -0
- opensimula-0.5.1.dist-info/RECORD +44 -0
- opensimula-0.5.1.dist-info/WHEEL +5 -0
- opensimula-0.5.1.dist-info/licenses/LICENSE +21 -0
- opensimula-0.5.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import math
|
|
2
|
+
from opensimula.Message import Message
|
|
3
|
+
from opensimula.components.Surface import Surface
|
|
4
|
+
from opensimula.Parameters import Parameter_component, Parameter_float_list, Parameter_options, Parameter_component_list
|
|
5
|
+
from opensimula.Variable import Variable
|
|
6
|
+
from opensimula.visual_3D.Polygon_3D import Polygon_3D
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Building_surface(Surface):
|
|
10
|
+
def __init__(self, name, project):
|
|
11
|
+
Surface.__init__(self, name, project)
|
|
12
|
+
# Parameters
|
|
13
|
+
self.parameter("type").value = "Building_surface"
|
|
14
|
+
self.parameter("description").value = "Building surface"
|
|
15
|
+
self.add_parameter(Parameter_component("construction", "not_defined", ["Construction"]))
|
|
16
|
+
self.add_parameter(Parameter_component_list("spaces", ["not_defined", "not_defined"], ["Space"]))
|
|
17
|
+
self.add_parameter(Parameter_options("surface_type", "EXTERIOR", ["EXTERIOR", "INTERIOR", "UNDERGROUND", "VIRTUAL"]))
|
|
18
|
+
self.add_parameter(Parameter_float_list("h_cv", [19.3, 2], "W/m²K", min=0))
|
|
19
|
+
|
|
20
|
+
# Constants
|
|
21
|
+
self.H_RD = 5.705 # 4*sigma*(293^3)
|
|
22
|
+
# Variables
|
|
23
|
+
self.add_variable(Variable("T_s0", "°C"))
|
|
24
|
+
self.add_variable(Variable("T_s1", "°C"))
|
|
25
|
+
self.add_variable(Variable("q_cd0", "W/m²"))
|
|
26
|
+
self.add_variable(Variable("q_cd1", "W/m²"))
|
|
27
|
+
self.add_variable(Variable("p_0", "W/m²"))
|
|
28
|
+
self.add_variable(Variable("p_1", "W/m²"))
|
|
29
|
+
self.add_variable(Variable("q_cv0", "W/m²"))
|
|
30
|
+
self.add_variable(Variable("q_cv1", "W/m²"))
|
|
31
|
+
self.add_variable(Variable("q_sol0", "W/m²"))
|
|
32
|
+
self.add_variable(Variable("q_sol1", "W/m²"))
|
|
33
|
+
self.add_variable(Variable("q_swig0", "W/m²"))
|
|
34
|
+
self.add_variable(Variable("q_swig1", "W/m²"))
|
|
35
|
+
self.add_variable(Variable("q_lwig0", "W/m²"))
|
|
36
|
+
self.add_variable(Variable("q_lwig1", "W/m²"))
|
|
37
|
+
self.add_variable(Variable("q_lwt0", "W/m²"))
|
|
38
|
+
self.add_variable(Variable("q_lwt1", "W/m²"))
|
|
39
|
+
# For exterior surface
|
|
40
|
+
self.add_variable(Variable("T_rm", "°C"))
|
|
41
|
+
|
|
42
|
+
# k values must be calculated by each subclass
|
|
43
|
+
self.k = [1.0, 2.0]
|
|
44
|
+
self.k_01 = -1
|
|
45
|
+
|
|
46
|
+
def check(self):
|
|
47
|
+
errors = super().check()
|
|
48
|
+
# Test space defined
|
|
49
|
+
surface_type = self.parameter("surface_type").value
|
|
50
|
+
if surface_type == "EXTERIOR" or surface_type == "UNDERGROUND":
|
|
51
|
+
if self.parameter("spaces").value[0] == "not_defined":
|
|
52
|
+
msg = f"{self.parameter('name').value}, must define its space."
|
|
53
|
+
errors.append(Message(msg, "ERROR"))
|
|
54
|
+
elif surface_type == "INTERIOR" or surface_type == "VIRTUAL":
|
|
55
|
+
if (
|
|
56
|
+
self.parameter("spaces").value[0] == "not_defined"
|
|
57
|
+
or self.parameter("spaces").value[1] == "not_defined"
|
|
58
|
+
):
|
|
59
|
+
msg = f"{self.parameter('name').value}, must define two spaces."
|
|
60
|
+
errors.append(Message(msg, "ERROR"))
|
|
61
|
+
# Test construction defined for non-virtual surfaces
|
|
62
|
+
if surface_type != "VIRTUAL" and self.parameter("construction").value == "not_defined":
|
|
63
|
+
msg = f"{self.parameter('name').value}, Building surfaces must define its construction."
|
|
64
|
+
errors.append(Message(msg, "ERROR"))
|
|
65
|
+
self._surface_type_ = self.parameter("surface_type").value
|
|
66
|
+
if self._surface_type_ == "EXTERIOR" or self._surface_type_ == "INTERIOR":
|
|
67
|
+
self._create_openings_list_()
|
|
68
|
+
return errors
|
|
69
|
+
|
|
70
|
+
def get_building(self):
|
|
71
|
+
return self.parameter("spaces").component[0].get_building()
|
|
72
|
+
|
|
73
|
+
def get_space(self,side=0):
|
|
74
|
+
return self.parameter("spaces").component[side]
|
|
75
|
+
|
|
76
|
+
def radiant_property(self, prop, radiation_type, side, theta=0):
|
|
77
|
+
if self.parameter("surface_type").value == "VIRTUAL":
|
|
78
|
+
if prop == "tau":
|
|
79
|
+
return 1
|
|
80
|
+
else:
|
|
81
|
+
return 0
|
|
82
|
+
else:
|
|
83
|
+
return self.parameter("construction").component.radiant_property(prop, radiation_type, side, theta)
|
|
84
|
+
|
|
85
|
+
# ____________ pre_simulation ____________
|
|
86
|
+
def pre_simulation(self, n_time_steps, delta_t):
|
|
87
|
+
super().pre_simulation(n_time_steps, delta_t)
|
|
88
|
+
self._file_met = self.project().parameter("simulation_file_met").component
|
|
89
|
+
self._albedo = self.project().parameter("albedo").value
|
|
90
|
+
self._T_ini = self.get_building().parameter("initial_temperature").value
|
|
91
|
+
self._F_sky = (1 + math.sin(math.radians(self.parameter("altitude").value))) / 2
|
|
92
|
+
self._surface_type_ = self.parameter("surface_type").value
|
|
93
|
+
if self._surface_type_ == "EXTERIOR" or self._surface_type_ == "INTERIOR":
|
|
94
|
+
self._create_openings_list_()
|
|
95
|
+
if self._surface_type_ != "VIRTUAL":
|
|
96
|
+
self._calculate_K_()
|
|
97
|
+
if self._surface_type_ == "EXTERIOR":
|
|
98
|
+
self._sunny_index_ = self.project().env_3D.get_sunny_index(self.parameter("name").value)
|
|
99
|
+
|
|
100
|
+
def _create_openings_list_(self):
|
|
101
|
+
project_openings_list = self.project().component_list(comp_type="Opening")
|
|
102
|
+
self.openings = []
|
|
103
|
+
for opening in project_openings_list:
|
|
104
|
+
if opening.parameter("surface").component == self:
|
|
105
|
+
self.openings.append(opening)
|
|
106
|
+
|
|
107
|
+
def _calculate_K_(self):
|
|
108
|
+
self.a_0, self.a_1, self.a_01 = self.parameter("construction").component.get_A()
|
|
109
|
+
self.k_01 = self.area * self.a_01
|
|
110
|
+
surface_type = self.parameter("surface_type").value
|
|
111
|
+
if surface_type == "EXTERIOR":
|
|
112
|
+
self.k[0] = self.area * (self.a_0
|
|
113
|
+
- self.parameter("h_cv").value[0]
|
|
114
|
+
- self.H_RD * self.radiant_property("alpha", "long_wave", 0)
|
|
115
|
+
)
|
|
116
|
+
self.k[1] = self.area * (self.a_1 - self.parameter("h_cv").value[1])
|
|
117
|
+
elif surface_type == "INTERIOR":
|
|
118
|
+
# interior
|
|
119
|
+
self.k[0] = self.area * (self.a_0 - self.parameter("h_cv").value[0])
|
|
120
|
+
self.k[1] = self.area * (self.a_1 - self.parameter("h_cv").value[1])
|
|
121
|
+
elif surface_type == "UNDERGROUND":
|
|
122
|
+
# Underground
|
|
123
|
+
self.k[0] = 1 # not used
|
|
124
|
+
self.k[1] = self.area * (self.a_1 - self.parameter("h_cv").value[0]) # only one defined
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
#________ pre_iteration ____________
|
|
128
|
+
def pre_iteration(self, time_i, date, daylight_saving):
|
|
129
|
+
super().pre_iteration(time_i, date, daylight_saving)
|
|
130
|
+
# Meterological data
|
|
131
|
+
self._T_ext = self._file_met.variable("temperature").values[time_i]
|
|
132
|
+
if self._surface_type_ != "VIRTUAL":
|
|
133
|
+
# Trasient part
|
|
134
|
+
self._p_0_, self._p_1_ = self.parameter("construction").component.get_P(
|
|
135
|
+
time_i,
|
|
136
|
+
self.variable("T_s0").values,
|
|
137
|
+
self.variable("T_s1").values,
|
|
138
|
+
self.variable("q_cd0").values,
|
|
139
|
+
self.variable("q_cd1").values,
|
|
140
|
+
self._T_ini)
|
|
141
|
+
self.variable("p_0").values[time_i] = self._p_0_
|
|
142
|
+
self.variable("p_1").values[time_i] = self._p_1_
|
|
143
|
+
if self._surface_type_ == "EXTERIOR":
|
|
144
|
+
self._pre_iteration_exterior_(time_i)
|
|
145
|
+
elif self._surface_type_ == "UNDERGROUND":
|
|
146
|
+
self.variable("T_s0").values[time_i] = self._file_met.variable("underground_temperature").values[time_i]
|
|
147
|
+
|
|
148
|
+
def _pre_iteration_exterior_(self, time_i):
|
|
149
|
+
# Meterological data
|
|
150
|
+
hor_sol_dif = self._file_met.variable("sol_diffuse").values[time_i]
|
|
151
|
+
hor_sol_dir = self._file_met.variable("sol_direct").values[time_i]
|
|
152
|
+
T_sky = self._file_met.variable("sky_temperature").values[time_i]
|
|
153
|
+
|
|
154
|
+
# Diffuse solar radiation
|
|
155
|
+
E_dif_sunny = self._file_met.solar_diffuse_rad(
|
|
156
|
+
time_i,
|
|
157
|
+
self.orientation_angle("azimuth", 0),
|
|
158
|
+
self.orientation_angle("altitude", 0),
|
|
159
|
+
)
|
|
160
|
+
E_dif_sunny = E_dif_sunny + (1 - self._F_sky) * self._albedo * (hor_sol_dif + hor_sol_dir)
|
|
161
|
+
self.variable("E_dif_sunny").values[time_i] = E_dif_sunny
|
|
162
|
+
diffuse_sunny_fraction = self.project().env_3D.get_diffuse_sunny_fraction(self._sunny_index_)
|
|
163
|
+
E_dif = E_dif_sunny * diffuse_sunny_fraction
|
|
164
|
+
self.variable("E_dif").values[time_i] = E_dif
|
|
165
|
+
# Direct solar radiation
|
|
166
|
+
E_dir_sunny = self._file_met.solar_direct_rad(
|
|
167
|
+
time_i,
|
|
168
|
+
self.orientation_angle("azimuth", 0),
|
|
169
|
+
self.orientation_angle("altitude", 0),
|
|
170
|
+
)
|
|
171
|
+
self.variable("E_dir_sunny").values[time_i] = E_dir_sunny
|
|
172
|
+
E_dir = E_dir_sunny * self._calculate_direct_sunny_fraction_(time_i)
|
|
173
|
+
self.variable("E_dir").values[time_i] = E_dir
|
|
174
|
+
q_sol0 = self.radiant_property("alpha", "solar_diffuse", 0) * (E_dif + E_dir)
|
|
175
|
+
self.variable("q_sol0").values[time_i] = q_sol0
|
|
176
|
+
# Mean radiant temperature
|
|
177
|
+
T_rm = self._F_sky * T_sky + (1 - self._F_sky) * self._T_ext
|
|
178
|
+
self.variable("T_rm").values[time_i] = T_rm
|
|
179
|
+
# f_0
|
|
180
|
+
h_rd = self.H_RD * self.radiant_property("alpha", "long_wave", 0)
|
|
181
|
+
self.f_0 = self.area * (
|
|
182
|
+
-self._p_0_ - self.parameter("h_cv").value[0] * self._T_ext - h_rd * T_rm - q_sol0
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
def _calculate_direct_sunny_fraction_(self, time_i):
|
|
186
|
+
if self.project().parameter("shadow_calculation").value == "INSTANT":
|
|
187
|
+
direct_sunny_fraction = self.project().env_3D.get_direct_sunny_fraction(
|
|
188
|
+
self._sunny_index_
|
|
189
|
+
)
|
|
190
|
+
elif self.project().parameter("shadow_calculation").value == "INTERPOLATION":
|
|
191
|
+
azi = self._file_met.variable("sol_azimuth").values[time_i]
|
|
192
|
+
alt = self._file_met.variable("sol_altitude").values[time_i]
|
|
193
|
+
if not math.isnan(alt):
|
|
194
|
+
direct_sunny_fraction = (
|
|
195
|
+
self.project().env_3D.get_direct_interpolated_sunny_fraction(
|
|
196
|
+
self._sunny_index_, azi, alt
|
|
197
|
+
)
|
|
198
|
+
)
|
|
199
|
+
else:
|
|
200
|
+
direct_sunny_fraction = 1
|
|
201
|
+
elif self.project().parameter("shadow_calculation").value == "NO":
|
|
202
|
+
direct_sunny_fraction = 1
|
|
203
|
+
return direct_sunny_fraction
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
#________ post_iteration ____________
|
|
207
|
+
def post_iteration(self, time_index, date, daylight_saving, converged):
|
|
208
|
+
super().post_iteration(time_index, date, daylight_saving, converged)
|
|
209
|
+
if self._surface_type_ == "EXTERIOR":
|
|
210
|
+
self._calculate_exterior_T_s0_(time_index)
|
|
211
|
+
if self._surface_type_ != "VIRTUAL":
|
|
212
|
+
self._calculate_heat_fluxes_(time_index)
|
|
213
|
+
|
|
214
|
+
def _calculate_exterior_T_s0_(self, time_i):
|
|
215
|
+
T_s0 = (self.f_0 - self.k_01 * self.variable("T_s1").values[time_i]) / self.k[0]
|
|
216
|
+
self.variable("T_s0").values[time_i] = T_s0
|
|
217
|
+
|
|
218
|
+
def _calculate_heat_fluxes_(self, time_i):
|
|
219
|
+
self.variable("q_cd0").values[time_i] = (
|
|
220
|
+
self.a_0 * self.variable("T_s0").values[time_i]
|
|
221
|
+
+ self.a_01 * self.variable("T_s1").values[time_i]
|
|
222
|
+
+ self.variable("p_0").values[time_i]
|
|
223
|
+
)
|
|
224
|
+
self.variable("q_cd1").values[time_i] = (
|
|
225
|
+
self.a_01 * self.variable("T_s0").values[time_i]
|
|
226
|
+
+ self.a_1 * self.variable("T_s1").values[time_i]
|
|
227
|
+
+ self.variable("p_1").values[time_i]
|
|
228
|
+
)
|
|
229
|
+
if self._surface_type_ == "EXTERIOR":
|
|
230
|
+
self.variable("q_cv0").values[time_i] = self.parameter("h_cv").value[0] * (
|
|
231
|
+
self._T_ext - self.variable("T_s0").values[time_i]
|
|
232
|
+
)
|
|
233
|
+
self.variable("q_cv1").values[time_i] = self.parameter("h_cv").value[1] * (
|
|
234
|
+
self.get_space().variable("temperature").values[time_i]
|
|
235
|
+
- self.variable("T_s1").values[time_i]
|
|
236
|
+
)
|
|
237
|
+
h_rd = self.H_RD * self.radiant_property("alpha", "long_wave", 0)
|
|
238
|
+
self.variable("q_lwt0").values[time_i] = h_rd * (
|
|
239
|
+
self.variable("T_rm").values[time_i] - self.variable("T_s0").values[time_i]
|
|
240
|
+
)
|
|
241
|
+
self.variable("q_lwt1").values[time_i] = (
|
|
242
|
+
-self.variable("q_cd1").values[time_i]
|
|
243
|
+
- self.variable("q_cv1").values[time_i]
|
|
244
|
+
- self.variable("q_sol1").values[time_i]
|
|
245
|
+
- self.variable("q_swig1").values[time_i]
|
|
246
|
+
- self.variable("q_lwig1").values[time_i]
|
|
247
|
+
)
|
|
248
|
+
elif self._surface_type_ == "INTERIOR":
|
|
249
|
+
self.variable("q_cv0").values[time_i] = self.parameter("h_cv").value[0] * (
|
|
250
|
+
self.get_space(0).variable("temperature").values[time_i]
|
|
251
|
+
- self.variable("T_s0").values[time_i]
|
|
252
|
+
)
|
|
253
|
+
self.variable("q_cv1").values[time_i] = self.parameter("h_cv").value[1] * (
|
|
254
|
+
self.get_space(1).variable("temperature").values[time_i]
|
|
255
|
+
- self.variable("T_s1").values[time_i]
|
|
256
|
+
)
|
|
257
|
+
self.variable("q_lwt0").values[time_i] = (
|
|
258
|
+
-self.variable("q_cd0").values[time_i]
|
|
259
|
+
- self.variable("q_cv0").values[time_i]
|
|
260
|
+
- self.variable("q_sol0").values[time_i]
|
|
261
|
+
- self.variable("q_swig0").values[time_i]
|
|
262
|
+
- self.variable("q_lwig0").values[time_i]
|
|
263
|
+
)
|
|
264
|
+
self.variable("q_lwt1").values[time_i] = (
|
|
265
|
+
-self.variable("q_cd1").values[time_i]
|
|
266
|
+
- self.variable("q_cv1").values[time_i]
|
|
267
|
+
- self.variable("q_sol1").values[time_i]
|
|
268
|
+
- self.variable("q_swig1").values[time_i]
|
|
269
|
+
- self.variable("q_lwig1").values[time_i]
|
|
270
|
+
)
|
|
271
|
+
elif self._surface_type_ == "UNDERGROUND":
|
|
272
|
+
self.variable("q_cv0").values[time_i] = -self.variable("q_cd0").values[time_i]
|
|
273
|
+
self.variable("q_cv1").values[time_i] = self.parameter("h_cv").value * (
|
|
274
|
+
self.get_space().variable("temperature").values[time_i]
|
|
275
|
+
- self.variable("T_s1").values[time_i]
|
|
276
|
+
)
|
|
277
|
+
self.variable("q_lwt1").values[time_i] = (
|
|
278
|
+
-self.variable("q_cd1").values[time_i]
|
|
279
|
+
- self.variable("q_cv1").values[time_i]
|
|
280
|
+
- self.variable("q_sol1").values[time_i]
|
|
281
|
+
- self.variable("q_swig1").values[time_i]
|
|
282
|
+
- self.variable("q_lwig1").values[time_i]
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
@property
|
|
286
|
+
def area(self):
|
|
287
|
+
area = super().area
|
|
288
|
+
if self._surface_type_ == "EXTERIOR" or self._surface_type_ == "INTERIOR":
|
|
289
|
+
for opening in self.openings:
|
|
290
|
+
area -= opening.area
|
|
291
|
+
return area
|
|
292
|
+
|
|
293
|
+
def get_polygon_3D(self):
|
|
294
|
+
azimuth = self.orientation_angle("azimuth", 0, "global")
|
|
295
|
+
altitude = self.orientation_angle("altitude", 0, "global")
|
|
296
|
+
origin = self.get_origin("global")
|
|
297
|
+
pol_2D = self.get_polygon_2D()
|
|
298
|
+
name = self.parameter("name").value
|
|
299
|
+
holes_2D = []
|
|
300
|
+
if self._surface_type_ == "EXTERIOR" or self._surface_type_ == "INTERIOR":
|
|
301
|
+
for opening in self.openings:
|
|
302
|
+
holes_2D.append(opening.get_polygon_2D())
|
|
303
|
+
if self._surface_type_ == "EXTERIOR":
|
|
304
|
+
return Polygon_3D(
|
|
305
|
+
name, origin, azimuth, altitude, pol_2D, holes_2D, color="white"
|
|
306
|
+
)
|
|
307
|
+
elif self._surface_type_ == "INTERIOR":
|
|
308
|
+
return Polygon_3D(
|
|
309
|
+
name, origin, azimuth, altitude, pol_2D, holes_2D, color="green", shading=False, calculate_shadows=False
|
|
310
|
+
)
|
|
311
|
+
elif self._surface_type_ == "UNDERGROUND":
|
|
312
|
+
return Polygon_3D(
|
|
313
|
+
name, origin, azimuth, altitude, pol_2D, holes_2D, color="brown", shading=False, calculate_shadows=False
|
|
314
|
+
)
|
|
315
|
+
elif self._surface_type_ == "VIRTUAL":
|
|
316
|
+
return Polygon_3D(
|
|
317
|
+
name, origin, azimuth, altitude, pol_2D, color="green", opacity=0.4, shading=False, calculate_shadows=False
|
|
318
|
+
)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from opensimula.Component import Component
|
|
2
|
+
from opensimula.Message import Message
|
|
3
|
+
from opensimula.Parameters import Parameter_variable_list, Parameter_math_exp_list, Parameter_string_list
|
|
4
|
+
from opensimula.Variable import Variable
|
|
5
|
+
|
|
6
|
+
class Calculator(Component):
|
|
7
|
+
def __init__(self, name, project):
|
|
8
|
+
Component.__init__(self, name, project)
|
|
9
|
+
self.parameter("type").value = "Calculator"
|
|
10
|
+
self.parameter(
|
|
11
|
+
"description").value = "Component to perform calculations with variables from other components."
|
|
12
|
+
# Parameters
|
|
13
|
+
self.add_parameter(Parameter_variable_list("input_variables", []))
|
|
14
|
+
self.add_parameter(Parameter_string_list("output_variables", []))
|
|
15
|
+
self.add_parameter(Parameter_string_list("output_units", []))
|
|
16
|
+
self.add_parameter(Parameter_math_exp_list("output_expressions", []))
|
|
17
|
+
|
|
18
|
+
def check(self):
|
|
19
|
+
self._variables_ = {} # Delete all the variables
|
|
20
|
+
errors = super().check()
|
|
21
|
+
if (
|
|
22
|
+
len(self.parameter("output_variables").value)
|
|
23
|
+
!= len(self.parameter("output_expressions").value) or len(self.parameter("output_variables").value)
|
|
24
|
+
!= len(self.parameter("output_units").value)
|
|
25
|
+
):
|
|
26
|
+
msg = Message(f"Error: {self.parameter('name').value}, output_variables, output_units and output_expressions size must be equal","ERROR")
|
|
27
|
+
errors.append(msg)
|
|
28
|
+
else:
|
|
29
|
+
# Create output variables
|
|
30
|
+
for i in range(len(self.parameter("output_variables").value)):
|
|
31
|
+
name = self.parameter("output_variables").value[i]
|
|
32
|
+
unit = self.parameter("output_units").value[i]
|
|
33
|
+
desc = self.parameter("output_expressions").value[i]
|
|
34
|
+
self.add_variable(Variable(name, unit, desc))
|
|
35
|
+
|
|
36
|
+
return errors
|
|
37
|
+
|
|
38
|
+
def pre_simulation(self, n_time_steps, delta_t):
|
|
39
|
+
super().pre_simulation(n_time_steps, delta_t)
|
|
40
|
+
|
|
41
|
+
def pre_iteration(self, time_index, date, daylight_saving):
|
|
42
|
+
super().pre_iteration(time_index, date, daylight_saving)
|
|
43
|
+
# variables dictonary
|
|
44
|
+
var_dic = self.get_parameter_variable_dictionary(time_index)
|
|
45
|
+
|
|
46
|
+
# Output variables
|
|
47
|
+
for i in range(len(self.parameter("output_variables").value)):
|
|
48
|
+
calculated_value = self.parameter(
|
|
49
|
+
"output_expressions").evaluate(i, var_dic)
|
|
50
|
+
name = self.parameter("output_variables").value[i]
|
|
51
|
+
self.variable(name).values[time_index] = calculated_value
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import math
|
|
2
|
+
import numpy as np
|
|
3
|
+
from scipy.optimize import brentq
|
|
4
|
+
from opensimula.Message import Message
|
|
5
|
+
from opensimula.Parameters import Parameter_component_list, Parameter_float_list
|
|
6
|
+
from opensimula.Component import Component
|
|
7
|
+
|
|
8
|
+
class Construction(Component):
|
|
9
|
+
def __init__(self, name, project):
|
|
10
|
+
Component.__init__(self, name, project)
|
|
11
|
+
self.parameter("type").value = "Construction"
|
|
12
|
+
self.parameter(
|
|
13
|
+
"description").value = "Construction using layers of material"
|
|
14
|
+
|
|
15
|
+
self.add_parameter(Parameter_float_list(
|
|
16
|
+
"solar_alpha", [0.8, 0.8], "frac", min=0, max=1))
|
|
17
|
+
self.add_parameter(Parameter_float_list(
|
|
18
|
+
"lw_epsilon", [0.9, 0.9], "frac", min=0, max=1))
|
|
19
|
+
self.add_parameter(Parameter_component_list(
|
|
20
|
+
"materials", [], "Material"))
|
|
21
|
+
self.add_parameter(Parameter_float_list("thicknesses", [], "m", min=0))
|
|
22
|
+
|
|
23
|
+
def check(self):
|
|
24
|
+
errors = super().check()
|
|
25
|
+
# Test if materials an thicknesses size are equals
|
|
26
|
+
if len(self.parameter("materials").value) != len(self.parameter("thicknesses").value):
|
|
27
|
+
msg =f"Error: {self.parameter('name').value}, material and thicknesses parameters must have same length"
|
|
28
|
+
errors.append(Message(msg, "error"))
|
|
29
|
+
return errors
|
|
30
|
+
|
|
31
|
+
# Functions for Transfer Function Calculation
|
|
32
|
+
|
|
33
|
+
def pre_simulation(self, n_time_steps, delta_t):
|
|
34
|
+
super().pre_simulation(n_time_steps, delta_t)
|
|
35
|
+
self._calc_trans_fun_(delta_t)
|
|
36
|
+
|
|
37
|
+
def get_T_step_fluxes(self):
|
|
38
|
+
n_q = len(self._coef_Q)
|
|
39
|
+
n_t = len(self._coef_T_a)
|
|
40
|
+
Q_old_1 = np.zeros(n_q)
|
|
41
|
+
Q_old_2 = np.zeros(n_q)
|
|
42
|
+
T_old = np.ones(n_t)
|
|
43
|
+
# T_old[0] = 0
|
|
44
|
+
q_2 = 1
|
|
45
|
+
Q_2 = []
|
|
46
|
+
Q_1 = []
|
|
47
|
+
n = 0
|
|
48
|
+
while n <= 1 or math.fabs(q_2) > 1e-15:
|
|
49
|
+
q_2 = np.dot(
|
|
50
|
+
self._coef_T_a - self._coef_T_b, T_old.transpose()
|
|
51
|
+
) - np.dot(self._coef_Q, Q_old_2.transpose())
|
|
52
|
+
q_1 = np.dot(
|
|
53
|
+
self._coef_T_b - self._coef_T_c, T_old.transpose()
|
|
54
|
+
) - np.dot(self._coef_Q, Q_old_1.transpose())
|
|
55
|
+
Q_2.append(q_2)
|
|
56
|
+
Q_1.append(q_1)
|
|
57
|
+
Q_old_2 = np.roll(Q_old_2, 1)
|
|
58
|
+
Q_old_2[0] = 0
|
|
59
|
+
Q_old_2[1] = q_2
|
|
60
|
+
Q_old_1 = np.roll(Q_old_1, 1)
|
|
61
|
+
Q_old_1[0] = 0
|
|
62
|
+
Q_old_1[1] = q_1
|
|
63
|
+
T_old = np.roll(T_old, 1)
|
|
64
|
+
T_old[0] = 0
|
|
65
|
+
n = n + 1
|
|
66
|
+
return (Q_1, Q_2)
|
|
67
|
+
|
|
68
|
+
def thermal_resistance(self):
|
|
69
|
+
resis_tot = 0
|
|
70
|
+
for i in range(len(self.parameter("materials").value)):
|
|
71
|
+
resis_tot = resis_tot + self._resis_layer_(i)
|
|
72
|
+
return resis_tot
|
|
73
|
+
|
|
74
|
+
def radiant_property(self, prop, radiation_type, side, theta=0):
|
|
75
|
+
if (radiation_type == "solar_diffuse" or radiation_type == "solar_direct"):
|
|
76
|
+
if (prop == "rho"):
|
|
77
|
+
return 1-self.parameter("solar_alpha").value[side]
|
|
78
|
+
elif (prop == "tau"):
|
|
79
|
+
return 0
|
|
80
|
+
elif (prop == "alpha"):
|
|
81
|
+
return self.parameter("solar_alpha").value[side]
|
|
82
|
+
elif (prop == "alpha_other_side"):
|
|
83
|
+
return 0
|
|
84
|
+
elif (radiation_type == "long_wave"):
|
|
85
|
+
if (prop == "rho"):
|
|
86
|
+
return 1-self.parameter("lw_epsilon").value[side]
|
|
87
|
+
elif (prop == "tau"):
|
|
88
|
+
return 0
|
|
89
|
+
elif (prop == "alpha"):
|
|
90
|
+
return self.parameter("lw_epsilon").value[side]
|
|
91
|
+
|
|
92
|
+
def get_A(self):
|
|
93
|
+
"""
|
|
94
|
+
Conduction actual time coefficients
|
|
95
|
+
Returns:
|
|
96
|
+
(a_0, a_1, a_01):
|
|
97
|
+
"""
|
|
98
|
+
return (-self._coef_T_c[0], -self._coef_T_a[0], self._coef_T_b[0])
|
|
99
|
+
|
|
100
|
+
def get_P(self, time_i, T_s0, T_s1, q_cd0, q_cd1, T_ini):
|
|
101
|
+
"""
|
|
102
|
+
Conduction P for one time instant
|
|
103
|
+
Returns:
|
|
104
|
+
(p_0, p_1):
|
|
105
|
+
"""
|
|
106
|
+
p0 = 0
|
|
107
|
+
p1 = 0
|
|
108
|
+
for i in range(1, len(self._coef_T_a)):
|
|
109
|
+
j = time_i - i
|
|
110
|
+
if j < 0:
|
|
111
|
+
T_0 = T_ini
|
|
112
|
+
T_1 = T_ini
|
|
113
|
+
else:
|
|
114
|
+
T_0 = T_s0[j]
|
|
115
|
+
T_1 = T_s1[j]
|
|
116
|
+
p0 += -self._coef_T_c[i] * T_0 + self._coef_T_b[i] * T_1
|
|
117
|
+
p1 += -self._coef_T_a[i] * T_1 + self._coef_T_b[i] * T_0
|
|
118
|
+
for i in range(1, len(self._coef_Q)):
|
|
119
|
+
j = time_i - i
|
|
120
|
+
if j < 0:
|
|
121
|
+
q_0 = 0
|
|
122
|
+
q_1 = 0
|
|
123
|
+
else:
|
|
124
|
+
q_0 = q_cd0[j]
|
|
125
|
+
q_1 = q_cd1[j]
|
|
126
|
+
p0 += -self._coef_Q[i] * q_0
|
|
127
|
+
p1 += -self._coef_Q[i] * q_1
|
|
128
|
+
return (p0, p1)
|
|
129
|
+
|
|
130
|
+
def _resis_layer_(self, layer):
|
|
131
|
+
material = self.parameter("materials").component[layer]
|
|
132
|
+
resis_def = material.parameter("use_resistance").value
|
|
133
|
+
if resis_def:
|
|
134
|
+
return material.parameter("thermal_resistance").value
|
|
135
|
+
else:
|
|
136
|
+
return (
|
|
137
|
+
self.parameter("thicknesses").value[layer]
|
|
138
|
+
/ material.parameter("conductivity").value
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
def _alpha_layer_(self, layer):
|
|
142
|
+
material = self.parameter("materials").component[layer]
|
|
143
|
+
resis_def = material.parameter("use_resistance").value
|
|
144
|
+
if resis_def:
|
|
145
|
+
return (
|
|
146
|
+
self.parameter("thicknesses").value[layer]
|
|
147
|
+
/ material.parameter("thermal_resistance").value
|
|
148
|
+
) / (
|
|
149
|
+
material.parameter("density").value
|
|
150
|
+
* material.parameter("specific_heat").value
|
|
151
|
+
)
|
|
152
|
+
else:
|
|
153
|
+
return material.parameter("conductivity").value / (
|
|
154
|
+
material.parameter("density").value
|
|
155
|
+
* material.parameter("specific_heat").value
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
def _tau_layer_(self, layer):
|
|
159
|
+
L = self.parameter("thicknesses").value[layer]
|
|
160
|
+
return L * L / self._alpha_layer_(layer)
|
|
161
|
+
|
|
162
|
+
def _H_Matrix_Layer_(self, s, layer):
|
|
163
|
+
# Calculate H Matrix for one layer
|
|
164
|
+
resis = self._resis_layer_(layer)
|
|
165
|
+
tau = self._tau_layer_(layer)
|
|
166
|
+
aux = math.sqrt(tau * s)
|
|
167
|
+
|
|
168
|
+
A = math.cos(aux)
|
|
169
|
+
if s == 0:
|
|
170
|
+
B = resis
|
|
171
|
+
C = 0
|
|
172
|
+
else:
|
|
173
|
+
B = resis / aux * math.sin(aux)
|
|
174
|
+
C = -aux * math.sin(aux) / resis
|
|
175
|
+
return np.array([[A, B], [C, A]])
|
|
176
|
+
|
|
177
|
+
def _dH_Matrix_Layer_(self, s, layer):
|
|
178
|
+
# Calculate dif H Matrix for one layer
|
|
179
|
+
resis = self._resis_layer_(layer)
|
|
180
|
+
tau = self._tau_layer_(layer)
|
|
181
|
+
aux = math.sqrt(tau * s)
|
|
182
|
+
|
|
183
|
+
if s == 0:
|
|
184
|
+
A = tau / 2
|
|
185
|
+
B = (resis * tau) / 6
|
|
186
|
+
C = tau / resis
|
|
187
|
+
else:
|
|
188
|
+
A = (tau / 2) * math.sin(aux) / aux
|
|
189
|
+
B = (resis / (2 * s)) * (math.sin(aux) / aux - math.cos(aux))
|
|
190
|
+
C = (tau / (2 * resis)) * ((math.sin(aux) / aux) + math.cos(aux))
|
|
191
|
+
return np.array([[A, B], [C, A]])
|
|
192
|
+
|
|
193
|
+
def _H_Matrix_(self, s):
|
|
194
|
+
H = np.eye(2)
|
|
195
|
+
for i in range(len(self.parameter("materials").value)):
|
|
196
|
+
H = np.dot(H, self._H_Matrix_Layer_(s, i))
|
|
197
|
+
return H
|
|
198
|
+
|
|
199
|
+
def _dH_Matrix_(self, s):
|
|
200
|
+
H = np.zeros((2, 2))
|
|
201
|
+
n = len(self.parameter("materials").value)
|
|
202
|
+
for i in range(n):
|
|
203
|
+
P1 = np.eye(2)
|
|
204
|
+
for j in range(i):
|
|
205
|
+
P1 = np.dot(P1, self._H_Matrix_Layer_(s, j))
|
|
206
|
+
P2 = np.eye(2)
|
|
207
|
+
for j in range(i + 1, n):
|
|
208
|
+
P2 = np.dot(P2, self._H_Matrix_Layer_(s, j))
|
|
209
|
+
H = H + np.dot(P1, np.dot(self._dH_Matrix_Layer_(s, i), P2))
|
|
210
|
+
return H
|
|
211
|
+
|
|
212
|
+
def _B_roots_(self, delta_t):
|
|
213
|
+
def func(s):
|
|
214
|
+
B = self._H_Matrix_(s)[0, 1]
|
|
215
|
+
return B
|
|
216
|
+
|
|
217
|
+
delta_s = 1e-6
|
|
218
|
+
a = 1e-15
|
|
219
|
+
B_a = func(a)
|
|
220
|
+
b = delta_s
|
|
221
|
+
roots = []
|
|
222
|
+
exp = 1
|
|
223
|
+
while exp > 1e-10:
|
|
224
|
+
B_b = func(b)
|
|
225
|
+
if B_a * B_b <= 0: # Signo contrario o un cero
|
|
226
|
+
bisec = brentq(func, a, b)
|
|
227
|
+
roots.append(bisec)
|
|
228
|
+
exp = math.exp(-bisec * delta_t)
|
|
229
|
+
a = b
|
|
230
|
+
B_a = B_b
|
|
231
|
+
b = b + delta_s
|
|
232
|
+
|
|
233
|
+
return roots
|
|
234
|
+
|
|
235
|
+
def _calc_trans_fun_(self, delta_t):
|
|
236
|
+
min_coef = 1e-10
|
|
237
|
+
dH_0 = self._dH_Matrix_(0)
|
|
238
|
+
H_0 = self._H_Matrix_(0)
|
|
239
|
+
C0 = H_0[0, 0] / H_0[0, 1]
|
|
240
|
+
C1x = (dH_0[0, 0] * H_0[0, 1] - H_0[0, 0] * dH_0[0, 1]) / (
|
|
241
|
+
H_0[0, 1] * H_0[0, 1]
|
|
242
|
+
)
|
|
243
|
+
C1y = (-dH_0[0, 1]) / (H_0[0, 1] * H_0[0, 1])
|
|
244
|
+
C1z = (dH_0[1, 1] * H_0[0, 1] - dH_0[0, 1]) / (H_0[0, 1] * H_0[0, 1])
|
|
245
|
+
|
|
246
|
+
# e_coef
|
|
247
|
+
roots = self._B_roots_(delta_t)
|
|
248
|
+
n_coef = len(roots) + 1
|
|
249
|
+
ex = []
|
|
250
|
+
ey = []
|
|
251
|
+
ez = []
|
|
252
|
+
h = []
|
|
253
|
+
for root in roots:
|
|
254
|
+
H = self._H_Matrix_(root)
|
|
255
|
+
dH = self._dH_Matrix_(root)
|
|
256
|
+
ex.append(H[0, 0] / (root * root * dH[0, 1]))
|
|
257
|
+
ey.append(1 / (root * root * dH[0, 1]))
|
|
258
|
+
ez.append(H[1, 1] / (root * root * dH[0, 1]))
|
|
259
|
+
h.append(math.exp(-root * delta_t))
|
|
260
|
+
d = [1, -h[0]]
|
|
261
|
+
for i in range(1, len(roots)):
|
|
262
|
+
u = [1, -h[i]]
|
|
263
|
+
d = np.convolve(d, u)
|
|
264
|
+
|
|
265
|
+
ox = []
|
|
266
|
+
oy = []
|
|
267
|
+
oz = []
|
|
268
|
+
for i in range(1, n_coef):
|
|
269
|
+
sum_exp_x = 0
|
|
270
|
+
sum_exp_y = 0
|
|
271
|
+
sum_exp_z = 0
|
|
272
|
+
for j in range(len(roots)):
|
|
273
|
+
exp = math.exp(-(i) * roots[j] * delta_t)
|
|
274
|
+
sum_exp_x = sum_exp_x + ex[j] * exp
|
|
275
|
+
sum_exp_y = sum_exp_y + ey[j] * exp
|
|
276
|
+
sum_exp_z = sum_exp_z + ez[j] * exp
|
|
277
|
+
ox.append(C1x + i * C0 * delta_t + sum_exp_x)
|
|
278
|
+
oy.append(C1y + i * C0 * delta_t + sum_exp_y)
|
|
279
|
+
oz.append(C1z + i * C0 * delta_t + sum_exp_z)
|
|
280
|
+
|
|
281
|
+
ramp = [1 / delta_t, -2 / delta_t, 1 / delta_t]
|
|
282
|
+
g = np.convolve(ramp, d)
|
|
283
|
+
a = np.convolve(g, ox)
|
|
284
|
+
b = np.convolve(g, oy)
|
|
285
|
+
c = np.convolve(g, oz)
|
|
286
|
+
# Cut d coefficients
|
|
287
|
+
for i in range(len(d)):
|
|
288
|
+
if math.fabs(d[i]) < min_coef:
|
|
289
|
+
d = d[0: i+1]
|
|
290
|
+
break
|
|
291
|
+
# Cut number of coeficients, se producen coeficientes extraños para muros ligeros a partir del n. de roots
|
|
292
|
+
a = np.resize(a, n_coef-1)
|
|
293
|
+
b = np.resize(b, n_coef-1)
|
|
294
|
+
c = np.resize(c, n_coef-1)
|
|
295
|
+
# Cut a, b, c coefficients
|
|
296
|
+
for i in range(len(a)):
|
|
297
|
+
if (
|
|
298
|
+
math.fabs(a[i]) < min_coef
|
|
299
|
+
and math.fabs(b[i]) < min_coef
|
|
300
|
+
and math.fabs(c[i]) < min_coef
|
|
301
|
+
):
|
|
302
|
+
a = a[0: i+1]
|
|
303
|
+
b = b[0: i+1]
|
|
304
|
+
c = c[0: i+1]
|
|
305
|
+
break
|
|
306
|
+
self._coef_T_a = a
|
|
307
|
+
self._coef_T_b = b
|
|
308
|
+
self._coef_T_c = c
|
|
309
|
+
self._coef_Q = d
|