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

1# created June 2015 

2# by TEASER4 Development Team 

3 

4 

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 

18 

19 

20class Office(NonResidential): 

21 """Archetype Office Building according to BMVBS 

22 

23 Subclass from NonResidential archetype class to represent office buildings. 

24 

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). 

38 

39 All default values are given according to 

40 :cite:`BundesministeriumfurVerkehrBauundStadtentwicklung.December2010` and 

41 :cite:`Kaag.March2008` if not stated otherwise. 

42 

43 In detail the net leased area is divided into the following thermal zone 

44 areas: 

45 

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) 

52 

53 Parameters 

54 ---------- 

55 

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: 

78 

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. 

88 

89 office_layout : int 

90 Structure of the floor plan of office buildings, default is 1, 

91 which is representative for one elongated floor. 

92 

93 1. elongated 1 floor 

94 2. elongated 2 floors 

95 3. compact (e.g. for a square base building) 

96 

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 

109 

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. 

114 

115 0. generic facade 

116 1. punctuated facade (individual windows) 

117 2. banner facade (continuous windows) 

118 3. full glazing 

119 

120 construction_data : str 

121 Construction type of used wall constructions default is "iwu_heavy") 

122 

123 - iwu_heavy: heavy construction 

124 - iwu_light: light construction 

125 

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. 

131 

132 Attributes 

133 ---------- 

134 

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 """ 

176 

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 ) 

202 

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 

212 

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"] 

230 

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 } 

238 

239 self.roof_names = {"Rooftop": [0, -1]} 

240 

241 self.ground_floor_names = {"Ground Floor": [0, -2]} 

242 

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 } 

249 

250 self.inner_wall_names = {"InnerWall": [90, 0]} 

251 

252 self.ceiling_names = {"Ceiling": [0, -1]} 

253 

254 self.floor_names = {"Floor": [0, -2]} 

255 

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 

261 

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 

270 

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") 

285 

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 

302 

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` 

317 

318 def generate_archetype(self): 

319 """Generates an office building. 

320 

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 

344 

345 # statistical estimation of the facade 

346 

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 

359 

360 # manipulation of wall according to facade design 

361 # (received from window_layout) 

362 

363 self._est_facade_area = self._est_outer_wall_area + self._est_win_area 

364 

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 

370 

371 # set the facade area to the four orientations 

372 

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: 

381 

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] 

396 

397 for key, value in self.window_names.items(): 

398 

399 if value[1] == 0 or value[1] == 180: 

400 

401 self.window_area[value[1]] = self._est_win_area * ( 

402 self._est_length / (2 * self._est_width + 2 * self._est_length) 

403 ) 

404 

405 elif value[1] == 90 or value[1] == 270: 

406 

407 self.window_area[value[1]] = self._est_win_area * ( 

408 self._est_width / (2 * self._est_width + 2 * self._est_length) 

409 ) 

410 

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] 

425 

426 for key, value in self.roof_names.items(): 

427 

428 self.outer_area[value[1]] = self._est_roof_area 

429 

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] 

440 

441 for key, value in self.ground_floor_names.items(): 

442 

443 self.outer_area[value[1]] = self._est_floor_area 

444 

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] 

455 

456 for key, value in self.inner_wall_names.items(): 

457 

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] 

468 

469 if self.number_of_floors > 1: 

470 

471 for key, value in self.ceiling_names.items(): 

472 

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) 

484 

485 for key, value in self.floor_names.items(): 

486 

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 

499 

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) 

504 

505 for zone in self.thermal_zones: 

506 zone.set_inner_wall_area() 

507 zone.set_volume_zone() 

508 

509 @property 

510 def office_layout(self): 

511 return self._office_layout 

512 

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 

519 

520 @property 

521 def window_layout(self): 

522 return self._window_layout 

523 

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 

530 

531 @property 

532 def construction_data(self): 

533 return self._construction_data 

534 

535 @construction_data.setter 

536 def construction_data(self, value): 

537 self._construction_data = datahandling.check_construction_data_setter_iwu(value)