Coverage for teaser/logic/buildingobjects/thermalzone.py: 73%

436 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"""This module includes the ThermalZone class 

5""" 

6from __future__ import division 

7import math 

8import random 

9import re 

10import warnings 

11from teaser.logic.buildingobjects.calculation.one_element import OneElement 

12from teaser.logic.buildingobjects.calculation.two_element import TwoElement 

13from teaser.logic.buildingobjects.calculation.three_element import ThreeElement 

14from teaser.logic.buildingobjects.calculation.four_element import FourElement 

15from teaser.logic.buildingobjects.calculation.five_element import FiveElement 

16 

17 

18class ThermalZone(object): 

19 """Thermal zone class. 

20 

21 This class is used to manage information and parameter calculation for 

22 thermal zones. Each thermal zone has one specific calculation method, 

23 which is specific to the used model (model_attr). For new model 

24 implementation this attribute can be assigned to new classes. 

25 

26 Parameters 

27 ---------- 

28 parent: Building() 

29 The parent class of this object, the Building the zone belongs to. 

30 Allows for better control of hierarchical structures. If not None it 

31 adds this ThermalZone instance to Building.thermal_zones. 

32 Default is None 

33 

34 Attributes 

35 ---------- 

36 

37 internal_id : float 

38 Random id for the distinction between different zones. 

39 name : str 

40 Individual name. 

41 area : float [m2] 

42 Thermal zone area. 

43 volume : float [m3] 

44 Thermal zone volume. 

45 outer_walls : list 

46 List of OuterWall instances. 

47 doors : list 

48 List of Door instances. 

49 rooftops : list 

50 List of Rooftop instances. 

51 ground_floors : list 

52 List of GroundFloor instances. 

53 windows : list 

54 List of Window instances. 

55 inner_walls : list 

56 List of InnerWall instances. 

57 floors : list 

58 List of Floor instances. 

59 ceilings: list 

60 List of Ceiling instances. 

61 interzonal_walls : list 

62 List of InterzonalWall instances. 

63 interzonal_floors : list 

64 List of InterzonalFloor instances. 

65 interzonal_ceilings: list 

66 List of InterzonalCeiling instances. 

67 use_conditions : UseConditions 

68 Instance of UseConditions with all relevant information for the usage 

69 of the thermal zone 

70 model_attr : Union[OneElement, TwoElement, ThreeElement, FourElement, 

71 FiveElement] 

72 Instance of OneElement(), TwoElement(), ThreeElement(), 

73 FourElement(), or FiveElement() that holds all calculation functions 

74 and attributes needed for the specific model. 

75 t_inside : float [K] 

76 Normative indoor temperature for static heat load calculation. 

77 The input of t_inside is ALWAYS in Kelvin 

78 t_outside : float [K] 

79 Normative outdoor temperature for static heat load calculation. 

80 The input of t_inside is ALWAYS in Kelvin 

81 t_ground : float [K] 

82 Temperature directly at the outer side of ground floors for static 

83 heat load calculation. 

84 The input of t_ground is ALWAYS in Kelvin 

85 t_ground_amplitude : float [K] 

86 Temperature amplitude of the ground over the year 

87 time_to_minimal_t_ground : int [s] 

88 Time between simulation time 0 (not: start_time) and the minimum of 

89 the ground temperature if the sine option for ground temperature is 

90 chosen. Default: 6004800 (noon of Mar 11 as published by Virginia Tech 

91 (https://www.builditsolar.com/Projects/Cooling/EarthTemperatures.htm) 

92 for a depth of 5 ft) 

93 density_air : float [kg/m3] 

94 average density of the air in the thermal zone 

95 heat_capac_air : float [J/K] 

96 average heat capacity of the air in the thermal zone 

97 number_of_floors : float 

98 number of floors of the zone. If None, parent's number_of_floors is used 

99 height_of_floors : float [m] 

100 average height of floors of the zone. If None, parent's height_of_floors 

101 is used 

102 

103 """ 

104 

105 def __init__(self, parent=None): 

106 """Constructor for ThermalZone 

107 """ 

108 

109 self.parent = parent 

110 

111 self.internal_id = random.random() 

112 self.name = None 

113 self._area = None 

114 self._volume = None 

115 self._outer_walls = [] 

116 self._doors = [] 

117 self._rooftops = [] 

118 self._ground_floors = [] 

119 self._windows = [] 

120 self._inner_walls = [] 

121 self._floors = [] 

122 self._ceilings = [] 

123 self._interzonal_walls = [] 

124 self._interzonal_floors = [] 

125 self._interzonal_ceilings = [] 

126 self._use_conditions = None 

127 self._t_inside = 293.15 

128 self._t_outside = 261.15 

129 self.density_air = 1.25 

130 self.heat_capac_air = 1002 

131 self._t_ground = 286.15 

132 self._t_ground_amplitude = 0 

133 self.time_to_minimal_t_ground = 6004800 

134 

135 self._number_of_floors = None 

136 self._height_of_floors = None 

137 

138 def calc_zone_parameters( 

139 self, 

140 number_of_elements=2, 

141 merge_windows=False, 

142 t_bt=5, 

143 t_bt_layer=7 

144 ): 

145 """RC-Calculation for the thermal zone 

146 

147 Based on the input parameters (used model) this function instantiates 

148 the corresponding calculation Class (e.g. TwoElement) and calculates 

149 the zone parameters. Currently, the function is able to distinguishes 

150 between the number of elements, we distinguish between: 

151 

152 - one element: all outer walls are aggregated into one element, 

153 inner wall are neglected 

154 - two elements: exterior and interior walls are aggregated 

155 - three elements: like 2, but floor or roofs are aggregated 

156 separately 

157 - four elements: roofs and floors are aggregated separately 

158 - five elements: includes borders to adjacent zones 

159 

160 For all four options we can choose if the thermal conduction through 

161 the window is considered in a separate resistance or not. 

162 

163 Parameters 

164 ---------- 

165 number_of_elements : int 

166 defines the number of elements, that area aggregated, between 1 

167 and 5, default is 2 

168 

169 merge_windows : bool 

170 True for merging the windows into the outer walls, False for 

171 separate resistance for window, default is False (Only 

172 supported for IBPSA) 

173 

174 t_bt : float 

175 Time constant according to VDI 6007 (default t_bt = 5) 

176 t_bt_layer: float 

177 Time constant according to VDI 6007 for aggragation of layers (default t_bt = 7) 

178 """ 

179 

180 if number_of_elements == 1: 

181 self.model_attr = OneElement( 

182 thermal_zone=self, 

183 merge_windows=merge_windows, 

184 t_bt=t_bt, 

185 t_bt_layer=t_bt_layer) 

186 self.model_attr.calc_attributes() 

187 elif number_of_elements == 2: 

188 self.model_attr = TwoElement( 

189 thermal_zone=self, 

190 merge_windows=merge_windows, 

191 t_bt=t_bt, 

192 t_bt_layer=t_bt_layer) 

193 self.model_attr.calc_attributes() 

194 elif number_of_elements == 3: 

195 self.model_attr = ThreeElement( 

196 thermal_zone=self, 

197 merge_windows=merge_windows, 

198 t_bt=t_bt, 

199 t_bt_layer=t_bt_layer) 

200 self.model_attr.calc_attributes() 

201 elif number_of_elements == 4: 

202 self.model_attr = FourElement( 

203 thermal_zone=self, 

204 merge_windows=merge_windows, 

205 t_bt=t_bt, 

206 t_bt_layer=t_bt_layer 

207 ) 

208 self.model_attr.calc_attributes() 

209 elif number_of_elements == 5: 

210 self.model_attr = FiveElement( 

211 thermal_zone=self, 

212 merge_windows=merge_windows, 

213 t_bt=t_bt) 

214 self.model_attr.calc_attributes() 

215 

216 def find_walls(self, orientation, tilt): 

217 """Returns all outer walls with given orientation and tilt 

218 

219 This function returns a list of all OuterWall elements with the 

220 same orientation and tilt. 

221 

222 Parameters 

223 ---------- 

224 orientation : float [degree] 

225 Azimuth of the desired walls. 

226 tilt : float [degree] 

227 Tilt against the horizontal of the desired walls. 

228 

229 Returns 

230 ------- 

231 elements : list 

232 List of OuterWalls instances with desired orientation and tilt. 

233 """ 

234 elements = [] 

235 for i in self.outer_walls: 

236 if i.orientation == orientation and i.tilt == tilt: 

237 elements.append(i) 

238 else: 

239 pass 

240 return elements 

241 

242 def find_doors(self, orientation, tilt): 

243 """Returns all outer walls with given orientation and tilt 

244 

245 This function returns a list of all Doors elements with the 

246 same orientation and tilt. 

247 

248 Parameters 

249 ---------- 

250 orientation : float [degree] 

251 Azimuth of the desired walls. 

252 tilt : float [degree] 

253 Tilt against the horizontal of the desired walls. 

254 

255 Returns 

256 ------- 

257 elements : list 

258 List of Doors instances with desired orientation and tilt. 

259 """ 

260 elements = [] 

261 for i in self.doors: 

262 if i.orientation == orientation and i.tilt == tilt: 

263 elements.append(i) 

264 else: 

265 pass 

266 return elements 

267 

268 def find_rts(self, orientation, tilt): 

269 """Returns all rooftops with given orientation and tilt 

270 

271 This function returns a list of all Rooftop elements with the 

272 same orientation and tilt. 

273 

274 Parameters 

275 ---------- 

276 orientation : float [degree] 

277 Azimuth of the desired rooftops. 

278 tilt : float [degree] 

279 Tilt against the horizontal of the desired rooftops. 

280 

281 Returns 

282 ------- 

283 elements : list 

284 List of Rooftop instances with desired orientation and tilt. 

285 """ 

286 elements = [] 

287 for i in self.rooftops: 

288 if i.orientation == orientation and i.tilt == tilt: 

289 elements.append(i) 

290 else: 

291 pass 

292 return elements 

293 

294 def find_gfs(self, orientation, tilt): 

295 """Returns all ground floors with given orientation and tilt 

296 

297 This function returns a list of all GroundFloor elements with the 

298 same orientation and tilt. 

299 

300 Parameters 

301 ---------- 

302 orientation : float [degree] 

303 Azimuth of the desired ground floors. 

304 tilt : float [degree] 

305 Tilt against the horizontal of the desired ground floors. 

306 

307 Returns 

308 ------- 

309 elements : list 

310 List of GroundFloor instances with desired orientation and tilt. 

311 """ 

312 elements = [] 

313 for i in self.ground_floors: 

314 if i.orientation == orientation and i.tilt == tilt: 

315 elements.append(i) 

316 else: 

317 pass 

318 return elements 

319 

320 def find_wins(self, orientation, tilt): 

321 """Returns all windows with given orientation and tilt 

322 

323 This function returns a list of all Window elements with the 

324 same orientation and tilt. 

325 

326 Parameters 

327 ---------- 

328 orientation : float [degree] 

329 Azimuth of the desired windows. 

330 tilt : float [degree] 

331 Tilt against the horizontal of the desired windows. 

332 

333 Returns 

334 ------- 

335 elements : list 

336 List of Window instances with desired orientation and tilt. 

337 """ 

338 elements = [] 

339 for i in self.windows: 

340 if i.orientation == orientation and i.tilt == tilt: 

341 elements.append(i) 

342 else: 

343 pass 

344 return elements 

345 

346 def find_izes_outer(self, orientation=None, tilt=None, add_reversed=False): 

347 """Returns all interzonal elements with given orientation and tilt 

348 

349 This function returns a list of all InterzonalWall, InterzonalCeiling 

350 and InterzonalFloor elements that are to be lumped with outer elements 

351 or exported as borders to adjacent zones, optionally reduced to those 

352 with given orientation and tilt. 

353 

354 Parameters 

355 ---------- 

356 orientation : float [degree] 

357 Azimuth of the desired elements. 

358 tilt : float [degree] 

359 Tilt against the horizontal of the desired elements. 

360 add_reversed : bool 

361 also consider the elements with method_interzonal_export 

362 'outer_reversed' (only for lumping to borders with adjacent zones) 

363 

364 Returns 

365 ------- 

366 elements : list 

367 List of InterzonalWall, InterzonalCeiling and InterzonalFloor 

368 instances with desired orientation and tilt. 

369 """ 

370 elements = [] 

371 for i in self.interzonal_elements: 

372 if ((i.orientation == orientation or orientation is None) 

373 and (i.tilt == tilt or tilt is None)): 

374 if i.interzonal_type_export == 'outer_ordered' or ( 

375 add_reversed and i.interzonal_type_export == 'outer_reversed' 

376 ): 

377 elements.append(i) 

378 else: 

379 pass 

380 return elements 

381 

382 def set_inner_wall_area(self): 

383 """Sets the inner wall area according to zone area 

384 

385 Sets the inner wall area according to zone area size if type building 

386 approach is used. This function covers Floors, Ceilings and InnerWalls. 

387 Approximation approach depends on the building's 

388 inner_wall_approximation_approach attribute. 

389 

390 """ 

391 

392 ass_error_1 = "You need to specify parent for thermal zone" 

393 

394 assert self.parent is not None, ass_error_1 

395 

396 for floor in self.floors: 

397 floor.area = ((self.number_of_floors - 1) / self.number_of_floors) \ 

398 * self.area 

399 for ceiling in self.ceilings: 

400 ceiling.area = ((self.number_of_floors - 1) 

401 / self.number_of_floors) * self.area 

402 

403 typical_area = self.use_conditions.typical_length * \ 

404 self.use_conditions.typical_width 

405 

406 avg_room_nr = self.area / typical_area 

407 

408 approximation_approach \ 

409 = self.parent.inner_wall_approximation_approach 

410 if approximation_approach not in ( 

411 'teaser_default', 

412 'typical_minus_outer', 

413 'typical_minus_outer_extended' 

414 ): 

415 warnings.warn(f'Inner wall approximation approach ' 

416 f'{approximation_approach} unknown. ' 

417 f'Falling back to teaser_default.') 

418 approximation_approach = 'teaser_default' 

419 if approximation_approach == 'typical_minus_outer': 

420 wall_area = ((int(avg_room_nr) 

421 + math.sqrt(avg_room_nr - int(avg_room_nr))) 

422 * (2 * self.use_conditions.typical_length 

423 * self.height_of_floors 

424 + 2 * self.use_conditions.typical_width 

425 * self.height_of_floors)) 

426 for other_verticals in self.outer_walls + self.interzonal_walls\ 

427 + self.windows + self.doors: 

428 wall_area -= other_verticals.area 

429 wall_area = max(0.01, wall_area) 

430 elif approximation_approach == 'typical_minus_outer_extended': 

431 wall_area = ((int(avg_room_nr) 

432 + math.sqrt(avg_room_nr - int(avg_room_nr))) 

433 * (2 * self.use_conditions.typical_length 

434 * self.height_of_floors 

435 + 2 * self.use_conditions.typical_width 

436 * self.height_of_floors)) 

437 for other_verticals in self.outer_walls + self.interzonal_walls\ 

438 + self.doors: 

439 wall_area -= other_verticals.area 

440 for pot_vert_be in self.rooftops + self.windows: 

441 wall_area -= pot_vert_be.area \ 

442 * math.sin(pot_vert_be.tilt * math.pi / 180) 

443 wall_area -= max(0.0, sum(gf.area for gf in self.ground_floors) 

444 - self.area) 

445 wall_area = max(0.01, wall_area) 

446 else: 

447 wall_area = (avg_room_nr 

448 * (self.use_conditions.typical_length 

449 * self.height_of_floors 

450 + 2 * self.use_conditions.typical_width 

451 * self.height_of_floors)) 

452 

453 for wall in self.inner_walls: 

454 wall.area = wall_area 

455 

456 def set_volume_zone(self): 

457 """Sets the zone volume according to area and height of floors 

458 

459 Sets the volume of a zone according area and height of floors 

460 (building attribute). 

461 """ 

462 

463 ass_error_1 = "you need to specify parent for thermal zone" 

464 

465 assert self.parent is not None, ass_error_1 

466 

467 self.volume = self.area * self.height_of_floors 

468 

469 def retrofit_zone( 

470 self, 

471 type_of_retrofit=None, 

472 window_type=None, 

473 material=None): 

474 """Retrofits all walls and windows in the zone. 

475 

476 Function call for all elements facing the ambient or ground. 

477 Distinguishes if the parent building is a archetype of type 'iwu' or 

478 'tabula_de'. If TABULA is used, it will use the pre-defined wall 

479 constructions of TABULA. 

480 

481 This function covers OuterWall, Rooftop, GroundFloor and Window. 

482 

483 Parameters 

484 ---------- 

485 type_of_retrofit : str 

486 The classification of retrofit, if the archetype building 

487 approach of TABULA is used. 

488 window_type : str 

489 Default: EnEv 2014 

490 material : str 

491 Default: EPS035 

492 """ 

493 

494 if type_of_retrofit is None: 

495 type_of_retrofit = 'retrofit' 

496 

497 if type(self.parent).__name__ in [ 

498 "SingleFamilyHouse", "TerracedHouse", "MultiFamilyHouse", 

499 "ApartmentBlock"]: 

500 for wall_count in self.outer_walls \ 

501 + self.rooftops + self.ground_floors + self.doors + \ 

502 self.windows: 

503 if "adv_retrofit" in wall_count.construction_data: 

504 warnings.warn( 

505 "already highest available standard" 

506 + self.parent.name + wall_count.name) 

507 elif "standard" in wall_count.construction_data: 

508 wall_count.load_type_element( 

509 year=self.parent.year_of_construction, 

510 construction=wall_count.construction_data.replace( 

511 "standard", type_of_retrofit)) 

512 else: 

513 wall_count.load_type_element( 

514 year=self.parent.year_of_construction, 

515 construction=wall_count.construction_data.replace( 

516 "retrofit", type_of_retrofit)) 

517 else: 

518 

519 for element_count in ( 

520 self.outer_walls 

521 + self.rooftops 

522 + self.ground_floors 

523 + self.interzonal_elements 

524 ): 

525 element_count.retrofit_wall( 

526 self.parent.year_of_retrofit, 

527 material) 

528 for win_count in self.windows: 

529 win_count.replace_window( 

530 self.parent.year_of_retrofit, 

531 window_type) 

532 

533 def delete(self): 

534 """Deletes the actual thermal zone safely. 

535 

536 This deletes the current thermal Zone and also refreshes the 

537 thermal_zones list in the parent Building. 

538 """ 

539 for index, tz in enumerate(self.parent.thermal_zones): 

540 if tz.internal_id == self.internal_id: 

541 self.parent.net_leased_area -= self.area 

542 self.parent.thermal_zones.pop(index) 

543 

544 break 

545 

546 def add_element(self, building_element): 

547 """Adds a building element to the corresponding list 

548 

549 This function adds a BuildingElement instance to the the list 

550 depending on the type of the Building Element 

551 

552 Parameters 

553 ---------- 

554 building_element : BuildingElement() 

555 inherited objects of BuildingElement() instance of TEASER 

556 

557 """ 

558 

559 ass_error_1 = ("building_element has to be an instance of OuterWall," 

560 " Rooftop, GroundFloor, Window, InnerWall, " 

561 "Ceiling or Floor") 

562 

563 assert type(building_element).__name__ in ( 

564 "OuterWall", "Rooftop", "GroundFloor", 

565 "InnerWall", "Ceiling", "Floor", 

566 "InterzonalWall", "InterzonalCeiling", "InterzonalFloor", 

567 "Window"), ass_error_1 

568 

569 if type(building_element).__name__ == "OuterWall": 

570 self._outer_walls.append(building_element) 

571 elif type(building_element).__name__ == "GroundFloor": 

572 self._ground_floors.append(building_element) 

573 elif type(building_element).__name__ == "Rooftop": 

574 self._rooftops.append(building_element) 

575 elif type(building_element).__name__ == "InnerWall": 

576 self._inner_walls.append(building_element) 

577 elif type(building_element).__name__ == "Ceiling": 

578 self._ceilings.append(building_element) 

579 elif type(building_element).__name__ == "Floor": 

580 self._floors.append(building_element) 

581 elif type(building_element).__name__ == "InterzonalWall": 

582 self._interzonal_walls.append(building_element) 

583 elif type(building_element).__name__ == "InterzonalCeiling": 

584 self._interzonal_ceilings.append(building_element) 

585 elif type(building_element).__name__ == "InterzonalFloor": 

586 self._interzonal_floors.append(building_element) 

587 elif type(building_element).__name__ == "Window": 

588 self._windows.append(building_element) 

589 

590 @property 

591 def parent(self): 

592 return self.__parent 

593 

594 @parent.setter 

595 def parent(self, value): 

596 from teaser.logic.buildingobjects.building import Building 

597 import inspect 

598 if value is not None: 

599 if inspect.isclass(Building): 

600 self.__parent = value 

601 self.__parent.thermal_zones.append(self) 

602 

603 @property 

604 def name(self): 

605 return self._name 

606 

607 @name.setter 

608 def name(self, value): 

609 regex = re.compile('[^a-zA-z0-9]') 

610 if isinstance(value, str): 

611 name = regex.sub('', value) 

612 else: 

613 try: 

614 name = regex.sub('', str(value)) 

615 except ValueError: 

616 print("Can't convert name to string") 

617 

618 # check if another zone with same name exists 

619 tz_names = [tz._name for tz in self.parent.thermal_zones[:-1]] 

620 if name in tz_names: 

621 i = 1 

622 while True: 

623 name_add = f"{name}_{i}" 

624 if name_add not in tz_names: 

625 name = name_add 

626 break 

627 i += 1 

628 self._name = name 

629 

630 @property 

631 def outer_walls(self): 

632 return self._outer_walls 

633 

634 @outer_walls.setter 

635 def outer_walls(self, value): 

636 if value is None: 

637 self._outer_walls = [] 

638 

639 @property 

640 def doors(self): 

641 return self._doors 

642 

643 @doors.setter 

644 def doors(self, value): 

645 if value is None: 

646 self._doors = [] 

647 

648 @property 

649 def rooftops(self): 

650 return self._rooftops 

651 

652 @rooftops.setter 

653 def rooftops(self, value): 

654 if value is None: 

655 self._rooftops = [] 

656 

657 @property 

658 def ground_floors(self): 

659 return self._ground_floors 

660 

661 @ground_floors.setter 

662 def ground_floors(self, value): 

663 if value is None: 

664 self._ground_floors = [] 

665 

666 @property 

667 def ceilings(self): 

668 return self._ceilings 

669 

670 @ceilings.setter 

671 def ceilings(self, value): 

672 if value is None: 

673 self._ceilings = [] 

674 

675 @property 

676 def floors(self): 

677 return self._floors 

678 

679 @floors.setter 

680 def floors(self, value): 

681 if value is None: 

682 self._floors = [] 

683 

684 @property 

685 def inner_walls(self): 

686 return self._inner_walls 

687 

688 @inner_walls.setter 

689 def inner_walls(self, value): 

690 

691 if value is None: 

692 self._inner_walls = [] 

693 

694 @property 

695 def interzonal_walls(self): 

696 return self._interzonal_walls 

697 

698 @interzonal_walls.setter 

699 def interzonal_walls(self, value): 

700 

701 if value is None: 

702 self._interzonal_walls = [] 

703 

704 @property 

705 def interzonal_ceilings(self): 

706 return self._interzonal_ceilings 

707 

708 @interzonal_ceilings.setter 

709 def interzonal_ceilings(self, value): 

710 

711 if value is None: 

712 self._interzonal_ceilings = [] 

713 

714 @property 

715 def interzonal_floors(self): 

716 return self._interzonal_floors 

717 

718 @interzonal_floors.setter 

719 def interzonal_floors(self, value): 

720 

721 if value is None: 

722 self._interzonal_floors = [] 

723 

724 @property 

725 def interzonal_elements(self): 

726 return self.interzonal_walls + self.interzonal_ceilings \ 

727 + self.interzonal_floors 

728 

729 @property 

730 def windows(self): 

731 return self._windows 

732 

733 @windows.setter 

734 def windows(self, value): 

735 

736 if value is None: 

737 self._windows = [] 

738 

739 @property 

740 def use_conditions(self): 

741 return self._use_conditions 

742 

743 @use_conditions.setter 

744 def use_conditions(self, value): 

745 ass_error_1 = "Use condition has to be an instance of UseConditions()" 

746 

747 assert type(value).__name__ == "UseConditions", ass_error_1 

748 

749 if value is not None: 

750 self._use_conditions = value 

751 self.typical_length = value.typical_length 

752 self.typical_width = value.typical_width 

753 self._use_conditions = value 

754 

755 @property 

756 def area(self): 

757 return self._area 

758 

759 @area.setter 

760 def area(self, value): 

761 

762 if isinstance(value, float): 

763 pass 

764 elif value is None: 

765 pass 

766 else: 

767 try: 

768 value = float(value) 

769 except: 

770 raise ValueError("Can't convert zone area to float") 

771 

772 if self.parent is not None: 

773 if self._area is None: 

774 if self.parent.net_leased_area is None: 

775 self.parent.net_leased_area = 0.0 

776 self._area = value 

777 self.parent.net_leased_area += value 

778 else: 

779 self.parent.net_leased_area -= self._area 

780 self.parent.net_leased_area += value 

781 self._area = value 

782 else: 

783 self._area = value 

784 

785 @property 

786 def number_of_floors(self): 

787 if self._number_of_floors is None: 

788 if self.parent is not None: 

789 number_of_floors = self.parent.number_of_floors 

790 else: 

791 number_of_floors = None 

792 else: 

793 number_of_floors = self._number_of_floors 

794 return number_of_floors 

795 

796 @number_of_floors.setter 

797 def number_of_floors(self, value): 

798 

799 if isinstance(value, float): 

800 pass 

801 elif value is None: 

802 pass 

803 else: 

804 try: 

805 value = float(value) 

806 except ValueError: 

807 raise ValueError("Can't convert zone number_of_floors to float") 

808 

809 self._number_of_floors = value 

810 

811 @property 

812 def height_of_floors(self): 

813 if self._height_of_floors is None: 

814 if self.parent is not None: 

815 height_of_floors = self.parent.height_of_floors 

816 else: 

817 height_of_floors = None 

818 else: 

819 height_of_floors = self._height_of_floors 

820 return height_of_floors 

821 

822 @height_of_floors.setter 

823 def height_of_floors(self, value): 

824 

825 if isinstance(value, float): 

826 pass 

827 elif value is None: 

828 pass 

829 else: 

830 try: 

831 value = float(value) 

832 except ValueError: 

833 raise ValueError("Can't convert zone height_of_floors to float") 

834 

835 self._height_of_floors = value 

836 

837 @property 

838 def volume(self): 

839 return self._volume 

840 

841 @volume.setter 

842 def volume(self, value): 

843 

844 if isinstance(value, float): 

845 pass 

846 elif value is None: 

847 pass 

848 else: 

849 try: 

850 value = float(value) 

851 except ValueError: 

852 raise ValueError("Can't convert zone volume to float") 

853 

854 if self.parent is not None: 

855 if self._volume is None: 

856 self._volume = value 

857 self.parent.volume += value 

858 else: 

859 self.parent.volume -= self._volume 

860 self.parent.volume += value 

861 self._volume = value 

862 else: 

863 self._volume = value 

864 

865 @property 

866 def t_inside(self): 

867 return self._t_inside 

868 

869 @t_inside.setter 

870 def t_inside(self, value): 

871 if isinstance(value, float): 

872 self._t_inside = value 

873 elif value is None: 

874 self._t_inside = value 

875 else: 

876 try: 

877 value = float(value) 

878 self._t_inside = value 

879 except: 

880 raise ValueError("Can't convert temperature to float") 

881 

882 @property 

883 def t_outside(self): 

884 return self._t_outside 

885 

886 @t_outside.setter 

887 def t_outside(self, value): 

888 

889 if isinstance(value, float): 

890 self._t_outside = value 

891 elif value is None: 

892 self._t_outside = value 

893 else: 

894 try: 

895 value = float(value) 

896 self._t_outside = value 

897 except: 

898 raise ValueError("Can't convert temperature to float") 

899 

900 @property 

901 def t_ground(self): 

902 return self._t_ground 

903 

904 @t_ground.setter 

905 def t_ground(self, value): 

906 

907 if isinstance(value, float): 

908 self._t_ground = value 

909 elif value is None: 

910 self._t_ground = value 

911 else: 

912 try: 

913 value = float(value) 

914 self._t_ground = value 

915 except: 

916 raise ValueError("Can't convert temperature to float") 

917 

918 @property 

919 def t_ground_amplitude(self): 

920 return self._t_ground_amplitude 

921 

922 @t_ground_amplitude.setter 

923 def t_ground_amplitude(self, value): 

924 

925 if isinstance(value, float): 

926 self._t_ground_amplitude = value 

927 elif value is None: 

928 self._t_ground_amplitude = value 

929 else: 

930 try: 

931 value = float(value) 

932 self._t_ground_amplitude = value 

933 except: 

934 raise ValueError("Can't convert temperature to float")