Coverage for teaser/logic/archetypebuildings/bmvbs/office.py: 99%
191 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# created June 2015
2# by TEASER4 Development Team
5import math
6import collections
7from teaser.logic.archetypebuildings.nonresidential import NonResidential
8from teaser.logic.buildingobjects.useconditions import UseConditions as UseCond
9from teaser.logic.buildingobjects.buildingphysics.ceiling import Ceiling
10from teaser.logic.buildingobjects.buildingphysics.floor import Floor
11from teaser.logic.buildingobjects.buildingphysics.groundfloor import GroundFloor
12from teaser.logic.buildingobjects.buildingphysics.innerwall import InnerWall
13from teaser.logic.buildingobjects.buildingphysics.outerwall import OuterWall
14from teaser.logic.buildingobjects.buildingphysics.rooftop import Rooftop
15from teaser.logic.buildingobjects.buildingphysics.window import Window
16from teaser.logic.buildingobjects.thermalzone import ThermalZone
17import teaser.data.utilities as datahandling
20class Office(NonResidential):
21 """Archetype Office Building according to BMVBS
23 Subclass from NonResidential archetype class to represent office buildings.
25 The office module contains a multi zone building according to BMVBS (see
26 :cite:`BundesministeriumfurVerkehrBauundStadtentwicklung.December2010`).
27 This German office building contains 6 usage zones (zones with similar
28 thermal behaviour). Each zone has 4 outer walls, 4 windows, a roof and a
29 ground floor. Depending on zone usage (typical length and width), an
30 interior
31 wall area is assigned. Exterior wall
32 surfaces are estimated based on
33 :cite:`BundesministeriumfurVerkehrBauundStadtentwicklung.December2010`.
34 Refinements of the archetype follow the approach of :cite:`Kaag.March2008`.
35 number_of_floors and height_of_floors are
36 mandatory parameters. Additional information can be passed
37 to the archetype (e.g. floor layout and window layout).
39 All default values are given according to
40 :cite:`BundesministeriumfurVerkehrBauundStadtentwicklung.December2010` and
41 :cite:`Kaag.March2008` if not stated otherwise.
43 In detail the net leased area is divided into the following thermal zone
44 areas:
46 #. Office (50% of net leased area)
47 #. Floor (25% of net leased area)
48 #. Storage (15% of net leased area)
49 #. Meeting (4% of net leased area)
50 #. Restroom (4% of net leased area)
51 #. ICT (2% of net leased area)
53 Parameters
54 ----------
56 parent: Project()
57 The parent class of this object, the Project the Building belongs to.
58 Allows for better control of hierarchical structures. If not None it
59 adds this Building instance to Project.buildings.
60 (default: None)
61 name : str
62 Individual name
63 year_of_construction : int
64 Year of first construction
65 height_of_floors : float [m]
66 Average height of the buildings' floors
67 number_of_floors : int
68 Number of building's floors above ground
69 net_leased_area : float [m2]
70 Total net leased area of building. This is area is NOT the footprint
71 of a building
72 with_ahu : Boolean
73 If set to True, an empty instance of BuildingAHU is instantiated and
74 assigned to attribute central_ahu. This instance holds information for
75 central Air Handling units. Default is False.
76 internal_gains_mode: int [1, 2, 3]
77 mode for the internal gains calculation done in AixLib:
79 1. Temperature and activity degree dependent heat flux calculation for persons. The
80 calculation is based on SIA 2024 (default)
81 2. Temperature and activity degree independent heat flux calculation for persons, the max.
82 heatflowrate is prescribed by the parameter
83 fixed_heat_flow_rate_persons.
84 3. Temperature and activity degree dependent calculation with
85 consideration of moisture and co2. The moisture calculation is
86 based on SIA 2024 (2015) and regards persons and non-persons, the co2 calculation is based on
87 Engineering ToolBox (2004) and regards only persons.
89 office_layout : int
90 Structure of the floor plan of office buildings, default is 1,
91 which is representative for one elongated floor.
93 1. elongated 1 floor
94 2. elongated 2 floors
95 3. compact (e.g. for a square base building)
97 inner_wall_approximation_approach : str
98 'teaser_default' (default) sets length of inner walls = typical
99 length * height of floors + 2 * typical width * height of floors
100 'typical_minus_outer' sets length of inner walls = 2 * typical
101 length * height of floors + 2 * typical width * height of floors
102 - length of outer or interzonal walls
103 'typical_minus_outer_extended' like 'typical_minus_outer', but also
104 considers that
105 a) a non-complete "average room" reduces its circumference
106 proportional to the square root of the area
107 b) rooftops, windows and ground floors (= walls with border to
108 soil) may have a vertical share
110 window_layout : int
111 Structure of the window facade type, default is 0, which is a generic facade
112 representing a statistical mean value of window area. This is the foundation
113 for calculating the other window layouts with correction factors.
115 0. generic facade
116 1. punctuated facade (individual windows)
117 2. banner facade (continuous windows)
118 3. full glazing
120 construction_data : str
121 Construction type of used wall constructions default is "iwu_heavy")
123 - iwu_heavy: heavy construction
124 - iwu_light: light construction
126 Notes
127 -----
128 The listed attributes are just the ones that are set by the user
129 calculated values are not included in this list. Changing these values is
130 expert mode.
132 Attributes
133 ----------
135 zone_area_factors : dict
136 This dictionary contains the name of the zone (str), the
137 zone area factor (float), the zone usage from BoundaryConditions json
138 (str) (Default see doc string above), and may contain a dictionary with
139 keyword-attribute-like changes to zone parameters that are usually
140 inherited from parent: 'number_of_floors', 'height_of_floors'
141 outer_wall_names : dict
142 This dictionary contains a random name for the outer walls,
143 their orientation and tilt. Default is a building in north-south
144 orientation)
145 roof_names : dict
146 This dictionary contains the name of the roofs, their orientation
147 and tilt. Default is one flat roof.
148 ground_floor_names : dict
149 This dictionary contains the name of the ground floors, their
150 orientation and tilt. Default is one ground floor.
151 window_names : dict
152 This dictionary contains the name of the window, their
153 orientation and tilt. Default is a building in north-south
154 orientation)
155 inner_wall_names : dict
156 This dictionary contains the name of the inner walls, their
157 orientation and tilt. Default is one cumulated inner wall.
158 ceiling_names : dict
159 This dictionary contains the name of the ceilings, their
160 orientation and tilt. Default is one cumulated ceiling.
161 floor_names : dict
162 This dictionary contains the name of the floors, their
163 orientation and tilt. Default is one cumulated floor.
164 gross_factor : float
165 gross factor used to correct the rooftop and floor area (default is
166 1.15)
167 est_factor_wall_area : float
168 estimation factor to calculate outer wall area
169 est_exponent_wall : float
170 estimation factor exponent to calculate outer wall area
171 est_factor_win_area : float
172 estimation factor to calculate window area
173 est_exponent_win : float
174 estimation factor exponent to calculate window area
175 """
177 def __init__(
178 self,
179 parent,
180 name=None,
181 year_of_construction=None,
182 number_of_floors=None,
183 height_of_floors=None,
184 net_leased_area=None,
185 with_ahu=False,
186 internal_gains_mode=1,
187 office_layout=None,
188 inner_wall_approximation_approach='teaser_default',
189 window_layout=None,
190 construction_data=None,
191 ):
192 """Constructor of Office archetype
193 """
194 super(Office, self).__init__(
195 parent,
196 name,
197 year_of_construction,
198 net_leased_area,
199 with_ahu,
200 internal_gains_mode,
201 )
203 self.office_layout = office_layout
204 self.inner_wall_approximation_approach \
205 = inner_wall_approximation_approach
206 self.window_layout = window_layout
207 self.construction_data = construction_data
208 self.number_of_floors = number_of_floors
209 self.height_of_floors = height_of_floors
210 # Parameters are default values for current
211 # calculation following Lichtmess
213 # [area factor, usage type(has to be set)]
214 self.zone_area_factors = collections.OrderedDict()
215 self.zone_area_factors["Office"] = [
216 0.5,
217 "Group Office (between 2 and 6 employees)",
218 ]
219 self.zone_area_factors["Floor"] = [0.25, "Traffic area"]
220 self.zone_area_factors["Storage"] = [
221 0.15,
222 "Stock, technical equipment, archives",
223 ]
224 self.zone_area_factors["Meeting"] = [0.04, "Meeting, Conference, seminar"]
225 self.zone_area_factors["Restroom"] = [
226 0.04,
227 "WC and sanitary rooms in non-residential buildings",
228 ]
229 self.zone_area_factors["ICT"] = [0.02, "Data center"]
231 # [tilt, orientation]
232 self.outer_wall_names = {
233 "Exterior Facade North": [90, 0],
234 "Exterior Facade East": [90, 90],
235 "Exterior Facade South": [90, 180],
236 "Exterior Facade West": [90, 270],
237 }
239 self.roof_names = {"Rooftop": [0, -1]}
241 self.ground_floor_names = {"Ground Floor": [0, -2]}
243 self.window_names = {
244 "Window Facade North": [90, 0],
245 "Window Facade East": [90, 90],
246 "Window Facade South": [90, 180],
247 "Window Facade West": [90, 270],
248 }
250 self.inner_wall_names = {"InnerWall": [90, 0]}
252 self.ceiling_names = {"Ceiling": [0, -1]}
254 self.floor_names = {"Floor": [0, -2]}
256 self.gross_factor = 1.15 # based on :cite:`Liebchen.2007`
257 self.est_factor_wall_area = 0.7658
258 self.est_exponent_wall = 0.9206
259 self.est_factor_win_area = 0.074
260 self.est_exponent_win = 1.0889
262 # estimated intermediate calculated values
263 self._est_outer_wall_area = 0
264 self._est_win_area = 0
265 self._est_roof_area = 0
266 self._est_floor_area = 0
267 self._est_facade_area = 0
268 self._est_width = 0
269 self._est_length = 0
271 if self.window_layout == 0:
272 self.corr_factor_wall = 1.0
273 self.corr_factor_win = 1.0
274 elif self.window_layout == 1:
275 self.corr_factor_wall = 0.75
276 self.corr_factor_win = 0.25
277 elif self.window_layout == 2:
278 self.corr_factor_wall = 0.5
279 self.corr_factor_win = 0.5
280 elif self.window_layout == 3:
281 self.corr_factor_wall = 0.1
282 self.corr_factor_win = 0.9
283 else:
284 raise ValueError("window_layout value has to be between 0 - 3")
286 if self.office_layout == 0 or self.office_layout == 1:
287 self._est_width = 13.0
288 elif self.office_layout == 2:
289 self._est_width = 15.0
290 elif self.office_layout == 3:
291 self._est_width = math.sqrt(
292 (self.net_leased_area / self.number_of_floors) * self.gross_factor
293 )
294 else:
295 raise ValueError("office_layout value has to be between 0 - 3")
296 if self.net_leased_area is not None and self.number_of_floors is not None:
297 self._est_length = (
298 (self.net_leased_area / self.number_of_floors) * self.gross_factor
299 ) / self._est_width
300 else:
301 pass
303 # default values for AHU
304 if self.with_ahu is True:
305 self.central_ahu.temperature_profile = (
306 7 * [293.15] + 12 * [295.15] + 5 * [293.15]
307 )
308 # according to :cite:`DeutschesInstitutfurNormung.2016`
309 self.central_ahu.min_relative_humidity_profile = 24 * [0.45] #
310 # according to :cite:`DeutschesInstitutfurNormung.2016b` and
311 # :cite:`DeutschesInstitutfurNormung.2016`
312 self.central_ahu.max_relative_humidity_profile = 24 * [0.65]
313 self.central_ahu.v_flow_profile = (
314 7 * [0.0] + 12 * [1.0] + 5 * [0.0]
315 ) # according to user
316 # profile in :cite:`DeutschesInstitutfurNormung.2016`
318 def generate_archetype(self):
319 """Generates an office building.
321 With given values, this class generates an office archetype building
322 according to TEASER requirements.
323 """
324 # help area for the correct building area setting while using typeBldgs
325 self.thermal_zones = None
326 type_bldg_area = self.net_leased_area
327 self.net_leased_area = 0.0
328 # create zones with their corresponding area, name and usage
329 for key, value in self.zone_area_factors.items():
330 zone = ThermalZone(self)
331 zone.area = type_bldg_area * value[0]
332 try:
333 zone.number_of_floors = value[2]['number_of_floors']
334 except (KeyError, IndexError):
335 pass
336 try:
337 zone.height_of_floors = value[2]['height_of_floors']
338 except (KeyError, IndexError):
339 pass
340 zone.name = key
341 use_cond = UseCond(zone)
342 use_cond.load_use_conditions(value[1], data_class=self.parent.data)
343 zone.use_conditions = use_cond
345 # statistical estimation of the facade
347 self._est_outer_wall_area = (
348 self.est_factor_wall_area * type_bldg_area ** self.est_exponent_wall
349 )
350 self._est_win_area = (
351 self.est_factor_win_area * type_bldg_area ** self.est_exponent_win
352 )
353 self._est_roof_area = (
354 type_bldg_area / self.number_of_floors
355 ) * self.gross_factor
356 self._est_floor_area = (
357 type_bldg_area / self.number_of_floors
358 ) * self.gross_factor
360 # manipulation of wall according to facade design
361 # (received from window_layout)
363 self._est_facade_area = self._est_outer_wall_area + self._est_win_area
365 if not self.window_layout == 0:
366 self._est_outer_wall_area = self._est_facade_area * self.corr_factor_wall
367 self._est_win_area = self._est_facade_area * self.corr_factor_win
368 else:
369 pass
371 # set the facade area to the four orientations
373 for key, value in self.outer_wall_names.items():
374 # North and South
375 if value[1] == 0 or value[1] == 180:
376 self.outer_area[value[1]] = self._est_outer_wall_area * (
377 self._est_length / (2 * self._est_width + 2 * self._est_length)
378 )
379 # East and West
380 elif value[1] == 90 or value[1] == 270:
382 self.outer_area[value[1]] = self._est_outer_wall_area * (
383 self._est_width / (2 * self._est_width + 2 * self._est_length)
384 )
385 for zone in self.thermal_zones:
386 # create wall and set building elements
387 outer_wall = OuterWall(zone)
388 outer_wall.load_type_element(
389 year=self.year_of_construction,
390 construction=self.construction_data.value,
391 data_class=self.parent.data,
392 )
393 outer_wall.name = key
394 outer_wall.tilt = value[0]
395 outer_wall.orientation = value[1]
397 for key, value in self.window_names.items():
399 if value[1] == 0 or value[1] == 180:
401 self.window_area[value[1]] = self._est_win_area * (
402 self._est_length / (2 * self._est_width + 2 * self._est_length)
403 )
405 elif value[1] == 90 or value[1] == 270:
407 self.window_area[value[1]] = self._est_win_area * (
408 self._est_width / (2 * self._est_width + 2 * self._est_length)
409 )
411 """
412 There is no real classification for windows, so this is a bit hard
413 code - will be fixed sometime.
414 """
415 for zone in self.thermal_zones:
416 window = Window(zone)
417 window.load_type_element(
418 self.year_of_construction,
419 "Kunststofffenster, " "Isolierverglasung",
420 data_class=self.parent.data,
421 )
422 window.name = key
423 window.tilt = value[0]
424 window.orientation = value[1]
426 for key, value in self.roof_names.items():
428 self.outer_area[value[1]] = self._est_roof_area
430 for zone in self.thermal_zones:
431 roof = Rooftop(zone)
432 roof.load_type_element(
433 year=self.year_of_construction,
434 construction=self.construction_data.value,
435 data_class=self.parent.data,
436 )
437 roof.name = key
438 roof.tilt = value[0]
439 roof.orientation = value[1]
441 for key, value in self.ground_floor_names.items():
443 self.outer_area[value[1]] = self._est_floor_area
445 for zone in self.thermal_zones:
446 ground_floor = GroundFloor(zone)
447 ground_floor.load_type_element(
448 year=self.year_of_construction,
449 construction=self.construction_data.value,
450 data_class=self.parent.data,
451 )
452 ground_floor.name = key
453 ground_floor.tilt = value[0]
454 ground_floor.orientation = value[1]
456 for key, value in self.inner_wall_names.items():
458 for zone in self.thermal_zones:
459 inner_wall = InnerWall(zone)
460 inner_wall.load_type_element(
461 year=self.year_of_construction,
462 construction=self.construction_data.value,
463 data_class=self.parent.data,
464 )
465 inner_wall.name = key
466 inner_wall.tilt = value[0]
467 inner_wall.orientation = value[1]
469 if self.number_of_floors > 1:
471 for key, value in self.ceiling_names.items():
473 for zone in self.thermal_zones:
474 ceiling = Ceiling(zone)
475 ceiling.load_type_element(
476 year=self.year_of_construction,
477 construction=self.construction_data.value,
478 data_class=self.parent.data,
479 )
480 ceiling.name = key
481 ceiling.tilt = value[0]
482 ceiling.orientation = value[1]
483 # zone.inner_walls.append(ceiling)
485 for key, value in self.floor_names.items():
487 for zone in self.thermal_zones:
488 floor = Floor(zone)
489 floor.load_type_element(
490 year=self.year_of_construction,
491 construction=self.construction_data.value,
492 data_class=self.parent.data,
493 )
494 floor.name = key
495 floor.tilt = value[0]
496 floor.orientation = value[1]
497 else:
498 pass
500 for key, value in self.outer_area.items():
501 self.set_outer_wall_area(value, key)
502 for key, value in self.window_area.items():
503 self.set_window_area(value, key)
505 for zone in self.thermal_zones:
506 zone.set_inner_wall_area()
507 zone.set_volume_zone()
509 @property
510 def office_layout(self):
511 return self._office_layout
513 @office_layout.setter
514 def office_layout(self, value):
515 if value is not None:
516 self._office_layout = value
517 else:
518 self._office_layout = 0
520 @property
521 def window_layout(self):
522 return self._window_layout
524 @window_layout.setter
525 def window_layout(self, value):
526 if value is not None:
527 self._window_layout = value
528 else:
529 self._window_layout = 0
531 @property
532 def construction_data(self):
533 return self._construction_data
535 @construction_data.setter
536 def construction_data(self, value):
537 self._construction_data = datahandling.check_construction_data_setter_iwu(value)