Coverage for teaser/logic/buildingobjects/buildingphysics/buildingelement.py: 83%
317 statements
« prev ^ index » next coverage.py v7.4.4, created at 2025-04-29 16:01 +0000
« prev ^ index » next coverage.py v7.4.4, created at 2025-04-29 16:01 +0000
1"""This module contains the Base class for all building elements."""
3from __future__ import division
4import warnings
5from teaser.logic.buildingobjects.buildingphysics.layer import Layer
6import teaser.data.input.buildingelement_input_json as buildingelement_input
7import numpy as np
8import random
9import re
12class BuildingElement(object):
13 """Building element class.
15 This is the base class for all building elements. Building elements are
16 all physical elements that may serve as boundaries for a thermal zone or
17 building.
19 Parameters
20 ----------
22 parent : ThermalZone()
23 The parent class of this object, the ThermalZone the BE belongs to.
24 Allows for better control of hierarchical structures.
25 Default is None.
27 Attributes
28 ----------
30 internal_id : float
31 Random id for the distinction between different elements.
32 name : str
33 Individual name
34 construction_data : str
35 Type of construction (e.g. "heavy" or "light"). Needed for
36 distinction between different constructions types in the same
37 building age period.
38 year_of_retrofit : int
39 Year of last retrofit
40 year_of_construction : int
41 Year of first construction
42 building_age_group : list
43 Determines the building age period that this building
44 element belongs to [begin, end], e.g. [1984, 1994]
45 area : float [m2]
46 Area of building element
47 tilt : float [degree]
48 Tilt against horizontal
49 orientation : float [degree]
50 Azimuth direction of building element (0 : north, 90: east, 180: south,
51 270: west)
52 inner_convection : float [W/(m2*K)]
53 Constant heat transfer coefficient of convection inner side (facing
54 the zone)
55 inner_radiation : float [W/(m2*K)]
56 Constant heat transfer coefficient of radiation inner side (facing
57 the zone)
58 outer_convection : float [W/(m2*K)]
59 Constant heat transfer coefficient of convection outer side (facing
60 the ambient or adjacent zone). Currently for all InnerWalls and
61 GroundFloors this value is set to 0.0
62 outer_radiation : float [W/(m2*K)]
63 Constant heat transfer coefficient of radiation outer side (facing
64 the ambient or adjacent zone). Currently for all InnerWalls and
65 GroundFloors this value is set to 0.0
66 layer : list
67 List of all layers of a building element (to be filled with Layer
68 objects). Use element.layer = None to delete all layers of the building
69 element
71 Calculated Attributes
73 r1 : float [K/W]
74 equivalent resistance R1 of the analogous model given in VDI 6007
75 r2 : float [K/W]
76 equivalent resistance R2 of the analogous model given in VDI 6007
77 r3 : float [K/W]
78 equivalent resistance R3 of the analogous model given in VDI 6007
79 c1 : float [J/K]
80 equivalent capacity C1 of the analogous model given in VDI 6007
81 c2 : float [J/K]
82 equivalent capacity C2 of the analogous model given in VDI 6007
83 c1_korr : float [J/K]
84 corrected capacity C1,korr for building elements in the case of
85 asymmetrical thermal load given in VDI 6007
86 u_value : float [W/m2K)
87 U-Value of building element
88 ua_value : float [W/K]
89 UA-Value of building element (Area times U-Value)
90 r_inner_conv : float [K/W]
91 Convective resistance of building element on inner side (facing the
92 zone)
93 r_inner_rad : float [K/W]
94 Radiative resistance of building element on inner side (facing the
95 zone)
96 r_inner_comb : float [K/W]
97 Combined convective and radiative resistance of building element on
98 inner side (facing the zone)
99 r_outer_conv : float [K/W]
100 Convective resistance of building element on outer side (facing
101 the ambient or adjacent zone). Currently for all InnerWalls and
102 GroundFloors this value is set to 0.0
103 r_outer_rad : float [K/W]
104 Radiative resistance of building element on outer side (facing
105 the ambient or adjacent zone). Currently for all InnerWalls and
106 GroundFloors this value is set to 0.0
107 r_outer_comb : float [K/W]
108 Combined convective and radiative resistance of building element on
109 outer side (facing the ambient or adjacent zone). Currently for all
110 InnerWalls and GroundFloors this value is set to 0.0
111 wf_out : float
112 Weightfactor of building element ua_value/ua_value_zone
113 """
115 def __init__(self, parent=None):
116 """Constructor for BuildingElement
117 """
119 self.parent = parent
121 self.internal_id = random.random()
123 self.name = None
124 self._construction_data = None
125 self._year_of_retrofit = None
126 self._year_of_construction = None
127 self.building_age_group = [None, None]
129 self._area = None
130 self._tilt = None
131 self._orientation = None
132 self._inner_convection = None
133 self._inner_radiation = None
134 self._outer_convection = None
135 self._outer_radiation = None
137 self._layer = []
139 self.r1 = 0.0
140 self.r2 = 0.0
141 self.r3 = 0.0
142 self.c1 = 0.0
143 self.c2 = 0.0
144 self.c1_korr = 0.0
145 self.ua_value = 0.0
146 self.r_conduc = 0.0
147 self.r_inner_conv = 0.0
148 self.r_inner_rad = 0.0
149 self.r_inner_comb = 0.0
150 self.r_outer_conv = 0.0
151 self.r_outer_rad = 0.0
152 self.r_outer_comb = 0.0
153 self.wf_out = 0.0
155 def calc_ua_value(self):
156 """U*A value for building element.
158 Calculates the U*A value and resistances for radiative and
159 convective heat transfer of a building element.
160 """
162 self.ua_value = 0.0
163 self.r_conduc = 0.0
164 self.r_inner_conv = 0.0
165 self.r_inner_rad = 0.0
166 self.r_inner_comb = 0.0
167 self.r_outer_conv = 0.0
168 self.r_outer_rad = 0.0
169 self.r_outer_comb = 0.0
170 r_conduc = 0.0
171 for count_layer in self.layer:
172 r_conduc += (
173 count_layer.thickness / count_layer.material.thermal_conduc
174 )
176 try:
177 self.r_conduc = r_conduc * (1 / self.area)
178 self.r_inner_conv = (1 / self.inner_convection) * (1 / self.area)
179 self.r_inner_rad = (1 / self.inner_radiation) * (1 / self.area)
180 self.r_inner_comb = 1 / (1 / self.r_inner_conv + 1 / self.r_inner_rad)
182 if self.outer_convection is not None \
183 and self.outer_radiation is not None:
185 self.r_outer_conv = (1 / self.outer_convection) * (1 / self.area)
186 self.r_outer_rad = (1 / self.outer_radiation) * (1 / self.area)
187 self.r_outer_comb = 1 / \
188 (1 / self.r_outer_conv + 1 / self.r_outer_rad)
190 self.ua_value = (1 / (
191 self.r_inner_comb + self.r_conduc + self.r_outer_comb))
192 self.u_value = self.ua_value / self.area
193 except ZeroDivisionError:
194 pass
197 def gather_element_properties(self):
198 """Helper function for matrix calculation.
200 Gathers all material properties of the building element and returns
201 them as a np.array. Needed for the calculation of the matrix in
202 equivalent_res(t_bt) especially for walls.
204 Returns
205 -------
206 number_of_layer : int
207 number of layer (length of layer list)
208 density : np.array
209 Numpy array with length of number of layer, filled with density
210 of each layer
211 thermal_conduc : np.array
212 Numpy array with length of number of layer, filled with
213 thermal_conduc of each layer
214 heat_capac : np.array
215 Numpy array with length of number of layer, filled with heat_capac
216 of each layer
217 thickness : np.array
218 Numpy array with length of number of layer, filled with thickness
219 of each layer
220 """
222 number_of_layer = len(self.layer)
223 density = np.zeros(number_of_layer)
224 thermal_conduc = np.zeros(number_of_layer)
225 heat_capac = np.zeros(number_of_layer)
226 thickness = np.zeros(number_of_layer)
228 for i in range(number_of_layer):
230 density[i] = self.layer[i].material.density
231 thermal_conduc[i] = self.layer[i].material.thermal_conduc
232 heat_capac[i] = self.layer[i].material.heat_capac
233 thickness[i] = self.layer[i].thickness
235 return number_of_layer, density, thermal_conduc, heat_capac, thickness
237 def add_layer(self, layer, position=None):
238 """Adds a layer at a certain position
240 This function adds a Layer instance to the layer list at a given
241 position
243 Parameters
244 ----------
245 layer : instance of Layer
246 Layer instance of TEASER
247 position : int
248 position in the wall starting from 0 (inner side)
250 """
251 ass_error_1 = "Layer has to be an instance of Layer()"
253 assert isinstance(layer, Layer), ass_error_1
255 if position is None:
256 self._layer.append(layer)
257 else:
258 self._layer.insert(position, layer)
260 def add_layer_list(self, layer_list):
261 """Appends a layer set to the layer list
263 The layer list has to be in correct order
265 Parameters
266 ----------
267 layer_list : list
268 list of sorted TEASER Layer instances
269 """
270 ass_error_1 = "Layer has to be an instance of Layer()"
271 for lay_count in layer_list:
273 assert isinstance(lay_count, Layer), ass_error_1
275 self._layer.append(lay_count)
277 def load_type_element(
278 self,
279 year=None,
280 construction=None,
281 data_class=None,
282 element_type=None,
283 reverse_layers=False,
284 type_element_key=None
285 ):
286 """Typical element loader.
288 Loads typical building elements according to their construction
289 year and their construction type from a json.
291 This function will only work if the parents to Building are set.
293 Parameters
294 ----------
295 year : int
296 Year of construction
298 construction : str
299 Construction type, code list ('heavy', 'light')
301 data_class : DataClass()
302 DataClass containing the bindings for TypeBuildingElement and
303 Material (typically this is the data class stored in prj.data,
304 but the user can individually change that. Default is
305 self.parent.parent.parent.data (which is data_class in current
306 project)
308 element_type : str
309 Element type to load - only to specify if the json entry for a
310 different type than type(element) is to be loaded, e.g. InnerWall
311 instead of OuterWall
313 reverse_layers : bool
314 defines if layer list should be reversed
316 type_element_key : str
317 Element to load - specify the full json entry
319 """
321 if type_element_key is None and (year is None or construction is None):
322 raise ValueError("specify either year and construction "
323 "or type_element_key")
325 if data_class is None:
326 data_class = self.parent.parent.parent.data
327 else:
328 data_class = data_class
330 self.layer = None
331 self._inner_convection = None
332 self._inner_radiation = None
333 self._outer_convection = None
334 self._outer_radiation = None
336 if type_element_key:
337 try:
338 buildingelement_input.load_type_element_by_key(
339 element=self, type_element_key=type_element_key,
340 data_class=data_class, reverse_layers=reverse_layers
341 )
342 except KeyError:
343 warnings.warn(
344 ('Type element ' + type_element_key + ' was not found. '
345 + 'Going back to default element for year and construction'
346 + '...')
347 )
348 type_element_key = None
350 if not type_element_key:
351 buildingelement_input.load_type_element(
352 element=self, year=year, construction=construction,
353 data_class=data_class, element_type=element_type,
354 reverse_layers=reverse_layers
355 )
357 def save_type_element(self, data_class=None):
358 """Typical element saver.
360 Saves typical building elements according to their construction
361 year and their construction type in the the json file for type building
362 elements. If the Project parent is set, it automatically saves it to
363 the file given in Project.data. Alternatively you can specify a path to
364 a file of TypeBuildingElements. If this file does not exist,
365 a new file is created.
367 Parameters
368 ----------
370 data_class : DataClass()
371 DataClass containing the bindings for TypeBuildingElement and
372 Material (typically this is the data class stored in prj.data,
373 but the user can individually change that. Default is
374 self.parent.parent.parent.data (which is data_class in current
375 project)
377 """
379 if data_class is None:
380 data_class = self.parent.parent.parent.data
381 else:
382 data_class = data_class
384 import teaser.data.output.buildingelement_output as \
385 buildingelement_output
387 buildingelement_output.save_type_element(element=self,
388 data_class=data_class)
390 def delete_type_element(self, data_class=None):
391 """Deletes typical element.
393 Deletes typical building elements according to their construction
394 year and their construction type in the the json file for type building
395 elements. If the Project parent is set, it automatically saves it to
396 the file given in Project.data. Alternatively you can specify a path to
397 a file of TypeBuildingElements. If this file does not exist,
398 a new file is created.
400 Parameters
401 ----------
403 data_class : DataClass()
404 DataClass containing the bindings for TypeBuildingElement and
405 Material (typically this is the data class stored in prj.data,
406 but the user can individually change that. Default is
407 self.parent.parent.parent.data (which is data_class in current
408 project)
410 """
412 if data_class is None:
413 data_class = self.parent.parent.parent.data
414 else:
415 data_class = data_class
417 import teaser.data.output.buildingelement_output as \
418 buildingelement_output
420 buildingelement_output.delete_type_element(element=self,
421 data_class=data_class)
423 def set_calc_default(self):
424 """Sets all calculated values of the Building Element to zero
425 """
426 self.r1 = 0.0
427 self.r2 = 0.0
428 self.r3 = 0.0
429 self.c1 = 0.0
430 self.c2 = 0.0
431 self.c1_korr = 0.0
432 self.ua_value = 0.0
433 self.r_conduc = 0.0
434 self.r_inner_conv = 0.0
435 self.r_inner_rad = 0.0
436 self.r_inner_comb = 0.0
437 self.r_outer_conv = 0.0
438 self.r_outer_rad = 0.0
439 self.r_outer_comb = 0.0
441 @property
442 def name(self):
443 return self._name
445 @name.setter
446 def name(self, value):
447 if isinstance(value, str):
449 if value:
450 regex = re.compile('[^a-zA-z0-9]')
451 self._name = regex.sub('', value)
452 if self._name == "None":
453 self._name = "BuildinElement" + str(
454 random.randint(1, 500000))
455 elif value is None:
456 self._value = "BuildinElement" + str(random.randint(1, 500000))
457 else:
458 try:
459 value = str(value)
460 regex = re.compile('[^a-zA-z0-9]')
461 self._name = regex.sub('', value)
462 except ValueError:
463 print("Can't convert name to string")
465 @property
466 def year_of_retrofit(self):
467 return self._year_of_retrofit
469 @year_of_retrofit.setter
470 def year_of_retrofit(self, value):
472 if isinstance(value, int):
473 pass
474 elif value is None:
475 pass
476 else:
477 try:
478 value = int(value)
479 except:
480 raise ValueError("Can't convert year of retrofit to float")
482 if value is not None:
483 if self.year_of_construction is not None:
484 self._year_of_retrofit = value
485 else:
486 raise ValueError("Specify year of construction first")
488 @property
489 def orientation(self):
490 return self._orientation
492 @orientation.setter
493 def orientation(self, value):
495 self._orientation = value
496 if type(self).__name__ == "OuterWall":
497 if (self.parent is not None and self.parent.parent is not None and
498 self.area is not None):
499 self.parent.parent.fill_outer_area_dict()
500 elif type(self).__name__ == "Window":
501 if (self.parent is not None and self.parent.parent is not None and
502 self.area is not None):
503 self.parent.parent.fill_window_area_dict()
505 @property
506 def layer(self):
507 return self._layer
509 @layer.setter
510 def layer(self, value):
512 if value is None:
513 self._layer = []
515 if self.inner_convection is not None and\
516 self.inner_radiation is not None and\
517 self.area is not None:
518 self.calc_ua_value()
520 @property
521 def inner_convection(self):
522 return self._inner_convection
524 @inner_convection.setter
525 def inner_convection(self, value):
527 if isinstance(value, float):
528 pass
529 elif value is None:
530 pass
531 else:
532 try:
533 value = float(value)
534 except:
535 raise ValueError("Can't convert inner convection to float")
537 if value is not None:
538 self._inner_convection = value
539 if self.inner_convection is not None and\
540 self.inner_radiation is not None and\
541 self.area is not None:
542 self.calc_ua_value()
544 @property
545 def inner_radiation(self):
546 return self._inner_radiation
548 @inner_radiation.setter
549 def inner_radiation(self, value):
551 if isinstance(value, float):
552 pass
553 elif value is None:
554 pass
555 else:
556 try:
557 value = float(value)
558 except:
559 raise ValueError("Can't convert inner radiation to float")
561 if value is not None:
562 self._inner_radiation = value
563 if self.inner_convection is not None and\
564 self.inner_radiation is not None and\
565 self.area is not None:
566 self.calc_ua_value()
568 @property
569 def outer_convection(self):
570 return self._outer_convection
572 @outer_convection.setter
573 def outer_convection(self, value):
575 if isinstance(value, float):
576 pass
577 elif value is None:
578 pass
579 else:
580 try:
581 value = float(value)
582 except:
583 raise ValueError("Can't convert outer convection to float")
585 if value is not None:
586 self._outer_convection = value
587 if self.inner_convection is not None and\
588 self.inner_radiation is not None and\
589 self.area is not None:
590 self.calc_ua_value()
592 @property
593 def outer_radiation(self):
594 return self._outer_radiation
596 @outer_radiation.setter
597 def outer_radiation(self, value):
599 if isinstance(value, float):
600 pass
601 elif value is None:
602 pass
603 else:
604 try:
605 value = float(value)
606 except:
607 raise ValueError("Can't convert outer radiation to float")
609 if value is not None:
610 self._outer_radiation = value
611 if self.inner_convection is not None and\
612 self.inner_radiation is not None and\
613 self.area is not None:
614 self.calc_ua_value()
616 @property
617 def area(self):
618 return self._area
620 @area.setter
621 def area(self, value):
623 if isinstance(value, float):
624 pass
625 elif value is None:
626 pass
627 else:
628 try:
629 value = float(value)
630 except:
631 raise ValueError("Can't convert element area to float")
633 if value is not None:
634 self._area = value
635 if type(self).__name__ == "OuterWall"\
636 or type(self).__name__ == "Rooftop" \
637 or type(self).__name__ == "GroundFloor":
638 if (self.parent is not None and self.parent.parent is not None and
639 self.orientation is not None):
640 self.parent.parent.fill_outer_area_dict()
641 elif type(self).__name__ == "Window":
642 if self.parent is not None and self.orientation is not None:
643 self.parent.parent.fill_window_area_dict()
644 if self.inner_convection is not None and\
645 self.inner_radiation is not None and\
646 self.area is not None:
647 self.calc_ua_value()
649 @property
650 def tilt(self):
651 return self._tilt
653 @tilt.setter
654 def tilt(self, value):
656 if isinstance(value, float):
657 self._tilt = value
658 elif value is None:
659 self._tilt = value
660 else:
661 try:
662 value = float(value)
663 self._tilt = value
664 except:
665 raise ValueError("Can't convert tilt to float")
667 @property
668 def year_of_construction(self):
669 if self._year_of_construction is None:
670 return self.parent.parent.year_of_construction
671 return self._year_of_construction
673 @year_of_construction.setter
674 def year_of_construction(self, value):
676 if isinstance(value, float):
677 self._year_of_construction = value
678 elif value is None:
679 self._year_of_construction = value
680 else:
681 try:
682 value = int(value)
683 self._year_of_construction = value
684 except:
685 raise ValueError("Can't convert year to int")
687 @property
688 def construction_data(self):
689 return self._construction_data
691 @construction_data.setter
692 def construction_data(self, value):
694 self._construction_data = value