Coverage for teaser/logic/buildingobjects/useconditions.py: 90%

273 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2025-04-29 16:01 +0000

1"""This module contains UseConditions class.""" 

2import random 

3from builtins import ValueError 

4 

5import pandas as pd 

6from itertools import cycle, islice 

7from collections import OrderedDict 

8 

9import teaser.data.input.usecond_input as usecond_input 

10import teaser.data.output.usecond_output as usecond_output 

11from teaser.logic.utilities import division_from_json 

12import warnings 

13 

14class UseConditions(object): 

15 """UseConditions class contains all zone specific boundary conditions. 

16 

17 Class that contains the boundary conditions of use for buildings defined in 

18 DIN V 18599-10 ( :cite:`DeutschesInstitutfurNormung.2016`) and VDI 2078 

19 (:cite:`VereinDeutscherIngenieure.2015c`). Profiles for internal gains ( 

20 persons, lighting, machines) are taken from SIA2024 ( 

21 :cite:`SwissSocietyofEngineersandArchitects.March2006`). In addition some 

22 TEASER specific use conditions have been attached to this class. The 

23 docstring also contains how the use conditions is used. 

24 

25 Note: Most attributes description are translations from DIN V 18599-10 

26 standard 

27 

28 Attributes 

29 ---------- 

30 usage: str 

31 usage type 

32 AixLib usage: String to distinguish usages of a zone 

33 typical_length: float [m] 

34 typical length of a room in a usage zone. This value is taken from 

35 SIA 2024. Archetype usage: division of usage zones in rooms 

36 typical width: float [m] 

37 typical width of a usage zone. This value is taken from 

38 SIA 2024. Archetype usage: division of usage zones in rooms 

39 with_heating: boolean 

40 Sets if the zone is heated by ideal heater or not. 

41 with_cooling: boolean 

42 Sets if the zone is cooled by ideal cooler or not. 

43 with_ideal_thresholds: boolean 

44 Sets if the threshold temperatures for ideal heater and cooler are 

45 used. 

46 True = T_threshold_heating and T_threshold_cooling are used. 

47 This can, in most cases, prevent simultaneous heating from AHU and 

48 cooling from ideal heater and vice versa. This should only be turned 

49 on if an AHU exists. 

50 T_threshold_heating: float [K] 

51 Threshold for the outside temperature above which the ideal heater is 

52 permanently shut down regardless the inside temperature. 

53 Default is 15 °C which corresponds to the value for all buildings 

54 that are not built 

55 according to EnEV standard according to DIN EN 18599-5. 

56 T_threshold_cooling: float [K] 

57 Threshold for the outside temperature below which the ideal cooler is 

58 permanently shut down regardless the inside temperature. 

59 Default is 22 °C, since there are no european standards 

60 for cooling degree days this value is taken from the following paper: 

61 "Heating Degree Days, Cooling Degree Days and Precipitation in Europe 

62 —analysis for the CELECT-project" by Benestad, 2008. 

63 heating_profile : list [K] 

64 Heating setpoint, regarding the zone temperature, of ideal static 

65 heating for a day or similar. You can set a list of any 

66 length, TEASER will multiplicate this list for one whole year. 

67 cooling_profile : list [K] 

68 Cooling setpoint, regarding the zone temperature, of ideal static 

69 cooling for a day or similar. You can set a list of any 

70 length, TEASER will multiplicate this list for one whole year. 

71 fixed_heat_flow_rate_persons: float [W/person] 

72 fixed heat flow rate for one person in case of temperature 

73 independent calculation. Default value is 70 

74 W/person and describes 

75 the maximum heat flow rate depending on the schedule. 

76 persons : float [Persons/m2] 

77 Specific number of persons per square area. 

78 Annex: Used for internal gains 

79 internal_gains_moisture_no_people : float [g/(h m²)] 

80 internal moisture production of plants, etc. except from people. 

81 activity_degree_persons : float [met] 

82 default value is 1.2 met 

83 AixLib: used for heat flow rate calculation (internal_gains_mode=1) 

84 or heat flow rate, moisture and co2 gains (internal_gains_mode=3). Both 

85 are temperature and activity degree depending, calculation based 

86 on SIA2024 (2015) and Engineering ToolBox (2004). 

87 Annex: not used, heat flow rate is constant value 

88 fixed_heat_flow_rate_persons 

89 ratio_conv_rad_persons: float 

90 describes the ratio between convective and radiative heat transfer 

91 of the persons [convective/radiative]. Default values are derived from 

92 :cite:`VereinDeutscherIngenieure.2015c`. 

93 AixLib: Used in Zone record for internal gains 

94 Annex: Used for internal gains 

95 persons_profile: list 

96 Relative presence of persons 0-1 (e.g. 0.5 means that 50% of the total 

97 number of persons are currently in the room). Given 

98 for 24h. This value is taken from SIA 2024. You can set a list of any 

99 length, TEASER will multiplicate this list for one whole year. 

100 AixLib: Used for internal gains profile on top-level 

101 Annex: Used for internal gains 

102 machines: float [W/m2] 

103 area specific eletrical load of machines per m2. This value is taken 

104 from SIA 2024 and DIN V 18599-10 for medium occupancy. 

105 AixLib: Used in Zone record for internal gains, 

106 internalGainsMachinesSpecific 

107 Annex: Used for internal gains 

108 ratio_conv_rad_machines: float 

109 describes the ratio between convective and radiative heat transfer 

110 of the machines [convective/radiative]. Default values are derived from 

111 :cite:`Davies.2004`. 

112 AixLib: Used in Zone record for internal gains 

113 Annex: Not used, all machines are convective (see Annex examples) 

114 machines_profile: list 

115 Relative presence of machines 0-1 (e.g. 0.5 means that 50% of the total 

116 number of machines are currently used in the room). Given 

117 for 24h. This value is taken from SIA 2024. You can set a list of any 

118 length, TEASER will multiplicate this list for one whole year. 

119 AixLib: Used for internal gains profile on top-level 

120 Annex: Used for internal gains 

121 use_maintained_illuminance: bool 

122 decision variable to determine wether lighting_power will be given by 

123 fixed_lighting_power or by calculation using the variables maintained_illuminance 

124 and lighting_efficiency_lumen 

125 lighting_power: float [W/m2] 

126 spec. electr. Power for lighting 

127 Determined by use_maintained_illuminance 

128 Not needed in input json file 

129 AixLib: Used in Zone record for internal gains 

130 Annex: Not used (see Annex examples) 

131 fixed_lighting_power: float [W/m2] 

132 spec. fixed electrical power for lighting. This value is taken from SIA 2024. 

133 ratio_conv_rad_lighting : float 

134 describes the ratio between convective and radiative heat transfer 

135 of the lighting [convective/radiative]. Default values are derived from 

136 :cite:`DiLaura.2011`. 

137 AixLib: Used in Zone record for internal gains, lighting 

138 maintained_illuminance : float [Lx] 

139 maintained illuminance value for lighting. 

140 This value is partially taken from SIA 2024 (2015-10) and partially 

141 from DIN V EN 18599-10 (2018-09). 

142 lighting_efficiency_lumen: float [lm/W_el] 

143 lighting efficiency in lm/W_el, in german: Lichtausbeute 

144 lighting_profil : [float] 

145 Relative presence of lighting 0-1 (e.g. 0.5 means that 50% of the total 

146 lighting power are currently used). Typically given for 24h. This is 

147 aligned to the user profile. 

148 AixLib: Used for internal gains profile on top-level 

149 Annex: Not used (see Annex examples) 

150 min_ahu: float [m3/(m2*h)] 

151 Zone specific minimum specific air flow supplied by the AHU. 

152 AixLib: Used on Multizone level for central AHU to determine total 

153 volume flow of each zone. 

154 

155 - **Note**: The AixLib parameter "WithProfile" determines whether the 

156 (v_flow_profile combined with "min_ahu and max_ahu") or the 

157 (persons_profile combined with "min_ahu and max_ahu") 

158 is used for the AHU supply flow calculations. 

159 Per default: (v_flow_profile combined with "min_ahu and max_ahu") 

160 

161 max_ahu : float [m3/(m2*h)] 

162 Zone specific maximum specific air flow supplied by the AHU. 

163 AixLib: Used on Multizone level for central AHU to determine total 

164 volume flow of each zone. 

165 

166 - **Note**: The AixLib parameter "WithProfile" determines whether the 

167 (v_flow_profile combined with "min_ahu and max_ahu") or the 

168 (persons_profile combined with "min_ahu and max_ahu") 

169 is used for the AHU supply flow calculations. 

170 Per default: (v_flow_profile combined with "min_ahu and max_ahu") 

171 

172 with_ahu : boolean 

173 Zone is connected to central air handling unit or not 

174 AixLib: Used on Multizone level for central AHU. 

175 use_constant_infiltration : boolean 

176 choose whether window opening should be regarded. 

177 true = natural infiltration + ventilation due to a AHU 

178 false = natural infiltration + ventilation due to a AHU 

179 + window infiltration calculated by window opening model 

180 AixLib: Used on Zone level for ventilation. 

181 normative_infiltration: float [1/h] 

182 Infiltration rate for static heat load calculation. 

183 Default is 0.5 based on the DIN EN 12831-1:2017 minimal air exchange rate reference value. 

184 base_infiltration : float [1/h] 

185 base value for the natural infiltration without window openings 

186 AixLib: Used on Zone level for ventilation. 

187 max_user_infiltration : float [1/h] 

188 Additional infiltration rate for maximum persons activity 

189 AixLib: Used on Zone level for ventilation. 

190 max_overheating_infiltration : list [1/h] 

191 Additional infiltration rate when overheating appears 

192 AixLib: Used on Zone level for ventilation. 

193 max_summer_infiltration : list 

194 Additional infiltration rate in the summer with 

195 [infiltration_rate [1/h], Tmin [K], Tmax [K]]. Default values are 

196 aligned to :cite:`DINV1859910`. 

197 AixLib: Used on Zone level for ventilation. 

198 winter_reduction_infiltration : list 

199 Reduction factor of userACH for cold weather with 

200 [infiltration_rate [1/h], Tmin [K], Tmax [K]] 

201 AixLib: Used on Zone level for ventilation. 

202 Default values are 

203 aligned to :cite:`DINV1859910`. 

204 schedules: pandas.DataFrame 

205 All time dependent boundary attributes in one pandas DataFrame, used 

206 for export (one year in hourly timestamps.) Derived from json. 

207 Schedules can be adjusted by setting the following parameters: 

208 - adjusted_opening_times 

209 - first_saturday_of_year 

210 - profiles_weekend_factor 

211 - set_back_times 

212 - heating_set_back 

213 - cooling_set_back 

214 To take adjustments into account you need to call calc_schedules() 

215 function afterwards. 

216 Note: python attribute, not customizable by user (derived from Json) 

217 adjusted_opening_times: list 

218 Sets the first and last hour of opening. These will cut or extend the 

219 existing profiles (machines, lights, persons). 

220 [opening_hour, closing_hour] 

221 first_saturday_of_year: int 

222 Weekday number of first saturday of the year [1:monday;7:tuesday]. 

223 Is needed to calc which days of profile should be reduced by 

224 profiles_weekend_factor. 

225 profiles_weekend_factor: float 

226 Factor to scale the existing profiles on weekends. For a reduction use 

227 values between [0;1]. Increase is also possible. 

228 set_back_times: list 

229 Sets the first and last hour outside of which the offset is applied. 

230 List of two integers [first_hour, last_hour] 

231 heating_set_back: float [K] 

232 Set back temperature offset for heating profile. Positive (+) values 

233 increase the profile, negative (-) decrease. 

234 cooling_set_back: float [K] 

235 Set back temperature offset for cooling profile. Positive (+) values 

236 increase the profile, negative (-) decrease. 

237 

238 """ 

239 

240 def __init__(self, parent=None): 

241 """Construct UseConditions.""" 

242 self.internal_id = random.random() 

243 

244 self.parent = parent 

245 self.usage = "Single office" 

246 

247 self.typical_length = 6.0 

248 self.typical_width = 6.0 

249 

250 self.with_heating = True 

251 self.with_cooling = False 

252 self.T_threshold_heating = 288.15 

253 self.T_threshold_cooling = 295.15 

254 

255 self.fixed_heat_flow_rate_persons = 70 

256 self.activity_degree_persons = 1.2 

257 self._persons = 1 / 14 

258 self.internal_gains_moisture_no_people = 0.5 

259 self.ratio_conv_rad_persons = 0.5 

260 

261 self.machines = 7.0 

262 self.ratio_conv_rad_machines = 0.75 

263 

264 self._use_maintained_illuminance = False # Choose wether lighting power will be given by direct input or calculated by maintained illuminance and lighting_efficiency_lumen 

265 self._lighting_power = 10 

266 self.fixed_lighting_power = 10 

267 self.ratio_conv_rad_lighting = 0.4 

268 self.maintained_illuminance = 500 

269 self.lighting_efficiency_lumen = 100 # lighting efficiency in lm/W_el 

270 

271 self.use_constant_infiltration = False 

272 self.normative_infiltration = 0.5 

273 self.base_infiltration = 0.2 

274 self.max_user_infiltration = 1.0 

275 self.max_overheating_infiltration = [3.0, 2.0] 

276 self.max_summer_infiltration = [1.0, 273.15 + 10, 273.15 + 17] 

277 self.winter_reduction_infiltration = [0.5, 273.15, 273.15 + 10] 

278 

279 self.min_ahu = 0.0 

280 self.max_ahu = 2.6 

281 self.with_ahu = False 

282 

283 self._first_saturday_of_year = 1 

284 self.profiles_weekend_factor = None 

285 

286 self._set_back_times = None 

287 self.heating_set_back = -2 

288 self.cooling_set_back = 2 

289 

290 self._adjusted_opening_times = None 

291 

292 self._with_ideal_thresholds = False 

293 

294 self._heating_profile = [ 

295 294.15, 

296 294.15, 

297 294.15, 

298 294.15, 

299 294.15, 

300 294.15, 

301 294.15, 

302 294.15, 

303 294.15, 

304 294.15, 

305 294.15, 

306 294.15, 

307 294.15, 

308 294.15, 

309 294.15, 

310 294.15, 

311 294.15, 

312 294.15, 

313 294.15, 

314 294.15, 

315 294.15, 

316 294.15, 

317 294.15, 

318 294.15, 

319 ] 

320 self._cooling_profile = [ 

321 294.15, 

322 294.15, 

323 294.15, 

324 294.15, 

325 294.15, 

326 294.15, 

327 294.15, 

328 294.15, 

329 294.15, 

330 294.15, 

331 294.15, 

332 294.15, 

333 294.15, 

334 294.15, 

335 294.15, 

336 294.15, 

337 294.15, 

338 294.15, 

339 294.15, 

340 294.15, 

341 294.15, 

342 294.15, 

343 294.15, 

344 294.15, 

345 ] 

346 self._persons_profile = [ 

347 0.0, 

348 0.0, 

349 0.0, 

350 0.0, 

351 0.0, 

352 0.0, 

353 0.0, 

354 0.2, 

355 0.4, 

356 0.6, 

357 0.8, 

358 0.8, 

359 0.4, 

360 0.6, 

361 0.8, 

362 0.8, 

363 0.4, 

364 0.2, 

365 0.0, 

366 0.0, 

367 0.0, 

368 0.0, 

369 0.0, 

370 0.0, 

371 ] 

372 self._machines_profile = [ 

373 0.1, 

374 0.1, 

375 0.1, 

376 0.1, 

377 0.1, 

378 0.1, 

379 0.1, 

380 0.2, 

381 0.4, 

382 0.6, 

383 0.8, 

384 0.8, 

385 0.4, 

386 0.6, 

387 0.8, 

388 0.8, 

389 0.4, 

390 0.2, 

391 0.1, 

392 0.1, 

393 0.1, 

394 0.1, 

395 0.1, 

396 0.1, 

397 ] 

398 self._lighting_profile = [ 

399 0.0, 

400 0.0, 

401 0.0, 

402 0.0, 

403 0.0, 

404 0.0, 

405 1.0, 

406 1.0, 

407 1.0, 

408 1.0, 

409 1.0, 

410 1.0, 

411 1.0, 

412 1.0, 

413 1.0, 

414 1.0, 

415 1.0, 

416 1.0, 

417 0.0, 

418 0.0, 

419 0.0, 

420 0.0, 

421 0.0, 

422 0.0, 

423 ] 

424 

425 self._schedules = None 

426 

427 def adjust_profile_by_opening(self, profile): 

428 """Adjusts the given profile by opening times specified for use 

429 condition with the parameter self.set_back_times. 

430 

431 Parameters 

432 ---------- 

433 profile : list 

434 list with the given profile (lighting, machines, persons) 

435 """ 

436 new_profile = [] 

437 # split profile into daily profiles 

438 profile_len = len(profile) 

439 n_sublists = profile_len // 24 

440 daily_profiles = (profile[i * 24:(i + 1) * 24] for i in 

441 range(n_sublists)) 

442 opening_hour_index = self.adjusted_opening_times[0] - 1 

443 closing_hour_index = self.adjusted_opening_times[1] - 1 

444 

445 for profile_day in daily_profiles: 

446 baseload = profile_day[0] 

447 for i, value in enumerate(profile_day): 

448 # check if runtime variable(time) is inside opening times 

449 # +/- delta times 

450 if opening_hour_index <= i <= closing_hour_index: 

451 if value == baseload: 

452 # start new iteration of profile_day from beginning 

453 for j, value2 in enumerate(profile_day): 

454 # search first value which is > baseload 

455 # if 

456 if ( 

457 value2 > baseload and i < ( 

458 closing_hour_index - opening_hour_index) / 2 

459 ): 

460 profile_day[i] = profile_day[j] 

461 break 

462 elif ( 

463 value2 > baseload and i >= ( 

464 closing_hour_index - opening_hour_index) / 2 

465 ): 

466 # value is overwritten every time, 

467 # so that last value that is > baseload 

468 # is used 

469 profile_day[i] = value2 

470 else: 

471 pass 

472 else: 

473 pass 

474 elif not ( 

475 opening_hour_index <= i <= closing_hour_index) and \ 

476 value != baseload: 

477 # if time is not inside opening times, set value to 

478 # baseload 

479 profile_day[i] = baseload 

480 new_profile.extend(profile_day) 

481 return new_profile 

482 

483 def adjust_profile_by_weekend(self, profile): 

484 """Scales the given profile on weekends. Factor for scaling is taken 

485 from self.profiles_weekend_factor. 

486 

487 Parameters 

488 ---------- 

489 profile : list 

490 list with the given profile (lighting, machines, persons) 

491 """ 

492 new_profile = [] 

493 # check if profile is at least week profile (other cases 

494 # than 24, 168,8760 are excluded already) 

495 if len(profile) == 24: 

496 profile = profile * 7 

497 n_sublists = len(profile) // 24 

498 daily_profiles = (profile[i * 24:(i + 1) * 24] for i in 

499 range(n_sublists)) 

500 weekend_days = [] 

501 for i in range(self.first_saturday_of_year, 365, 7): 

502 weekend_days.append(i) 

503 weekend_days.append(i + 1) 

504 for day_nr, profile_day in enumerate(daily_profiles, 1): 

505 if day_nr in weekend_days: 

506 profile_day = \ 

507 [round((x * self.profiles_weekend_factor), 3) 

508 for x in profile_day] 

509 new_profile.extend(profile_day) 

510 return new_profile 

511 

512 def load_use_conditions(self, zone_usage, data_class=None): 

513 """Load typical use conditions from JSON data base. 

514 

515 Loads Use conditions specified in the JSON. 

516 

517 Parameters 

518 ---------- 

519 zone_usage : str 

520 code list for zone_usage according to 18599 or self defined 

521 

522 data_class : DataClass() 

523 DataClass containing the bindings for Use Conditions (typically 

524 this is the data class stored in prj.data, 

525 but the user can individually change that. Default is None which 

526 leads to an automatic setter to self.parent.parent.parent.data ( 

527 which is DataClass in current project) 

528 

529 """ 

530 if data_class is None: 

531 data_class = self.parent.parent.parent.data 

532 else: 

533 data_class = data_class 

534 

535 usecond_input.load_use_conditions( 

536 use_cond=self, zone_usage=zone_usage, data_class=data_class 

537 ) 

538 

539 def save_use_conditions(self, data_class=None): 

540 """Documentation is missing.""" 

541 if data_class is None: 

542 data_class = self.parent.parent.parent.data 

543 else: 

544 data_class = data_class 

545 

546 usecond_output.save_use_conditions(use_cond=self, data_class=data_class) 

547 

548 @staticmethod 

549 def is_periodic(profile_list): 

550 """Checks if the given profile list is periodic. 

551 Allowed periods are: 24h, 168h (7 days), 8760h (1year). 

552 

553 Parameters 

554 ---------- 

555 profile_list: list 

556 given profile as list of hourly values. 

557 """ 

558 if not isinstance(profile_list, list): 

559 profile_list = list(profile_list) 

560 profile_len = len(profile_list) 

561 if profile_len in [24, 168, 8760]: 

562 return True 

563 else: 

564 return False 

565 

566 @property 

567 def persons(self): 

568 return self._persons 

569 

570 @persons.setter 

571 def persons(self, value): 

572 if isinstance(value, OrderedDict): 

573 self._persons = division_from_json(value) 

574 else: 

575 self._persons = value 

576 

577 @property 

578 def with_ideal_thresholds(self): 

579 return self._with_ideal_thresholds 

580 

581 @with_ideal_thresholds.setter 

582 def with_ideal_thresholds(self, value): 

583 self._with_ideal_thresholds = value 

584 

585 @property 

586 def heating_profile(self): 

587 return self._heating_profile 

588 

589 @heating_profile.setter 

590 def heating_profile(self, value): 

591 if not isinstance(value, list): 

592 value = [value] * 24 

593 if self.is_periodic(value): 

594 self._heating_profile = value 

595 else: 

596 raise ValueError( 

597 f"heating profile should be periodic (24h, 168h pr 8760h), " 

598 f"but length is {len(value)}" 

599 ) 

600 

601 @property 

602 def cooling_profile(self): 

603 return self._cooling_profile 

604 

605 @cooling_profile.setter 

606 def cooling_profile(self, value): 

607 if not isinstance(value, list): 

608 value = [value] * 24 

609 if self.is_periodic(value): 

610 self._cooling_profile = value 

611 else: 

612 raise ValueError( 

613 f"cooling profile should be periodic (24h, 168h pr 8760h), " 

614 f"but length is {len(value)}" 

615 ) 

616 

617 @property 

618 def persons_profile(self): 

619 return self._persons_profile 

620 

621 @persons_profile.setter 

622 def persons_profile(self, value): 

623 if not isinstance(value, list): 

624 value = [value] * 24 

625 if self.is_periodic(value): 

626 self._persons_profile = value 

627 else: 

628 raise ValueError( 

629 f"persons profile should be periodic (24h, 168h pr 8760h), " 

630 f"but length is {len(value)}" 

631 ) 

632 

633 @property 

634 def machines_profile(self): 

635 return self._machines_profile 

636 

637 @machines_profile.setter 

638 def machines_profile(self, value): 

639 if not isinstance(value, list): 

640 value = [value] * 24 

641 if self.is_periodic(value): 

642 self._machines_profile = value 

643 else: 

644 raise ValueError( 

645 f"machines profile should be periodic (24h, 168h pr 8760h), " 

646 "but length is {len(value)}" 

647 ) 

648 

649 @property 

650 def lighting_profile(self): 

651 return self._lighting_profile 

652 

653 @lighting_profile.setter 

654 def lighting_profile(self, value): 

655 if not isinstance(value, list): 

656 value = [value] * 24 

657 if self.is_periodic(value): 

658 self._lighting_profile = value 

659 else: 

660 raise ValueError( 

661 f"lighting profile should be periodic (24h, 168h pr 8760h), " 

662 "but length is {len(value)}" 

663 ) 

664 

665 @property 

666 def schedules(self): 

667 self._schedules = pd.DataFrame( 

668 index=pd.date_range("2019-01-01 00:00:00", periods=8760, 

669 freq="h").to_series().dt.strftime( 

670 "%m-%d %H:%M:%S"), 

671 data={ 

672 "heating_profile": list( 

673 islice(cycle(self._heating_profile), 8760)), 

674 "cooling_profile": list( 

675 islice(cycle(self._cooling_profile), 8760)), 

676 "persons_profile": list( 

677 islice(cycle(self._persons_profile), 8760)), 

678 "lighting_profile": list( 

679 islice(cycle(self._lighting_profile), 8760)), 

680 "machines_profile": list( 

681 islice(cycle(self._machines_profile), 8760)), 

682 }, 

683 ) 

684 return self._schedules 

685 

686 @schedules.setter 

687 def schedules(self, value): 

688 self._schedules = value 

689 

690 def calc_adj_schedules(self): 

691 """calculates adjusted schedules for use conditions. When called the 

692 profiles get adjusted due to specified conditions. Afterwards the 

693 existing schedules will be overwritten by the resulting pandas dataframe 

694 with 8760 h. 

695 

696 """ 

697 if self.adjusted_opening_times: 

698 self._machines_profile = self.adjust_profile_by_opening( 

699 self._machines_profile) 

700 self._lighting_profile = self.adjust_profile_by_opening( 

701 self._lighting_profile) 

702 self._persons_profile = self.adjust_profile_by_opening( 

703 self._persons_profile) 

704 

705 if self.profiles_weekend_factor: 

706 self._machines_profile = self.adjust_profile_by_weekend( 

707 self._machines_profile) 

708 self._lighting_profile = self.adjust_profile_by_weekend( 

709 self._lighting_profile) 

710 self._persons_profile = self.adjust_profile_by_weekend( 

711 self._persons_profile) 

712 

713 if self.set_back_times: 

714 set_back_index_morning, set_back_index_evening = \ 

715 self.set_back_times[0] - 1, self.set_back_times[1] - 1 

716 heating_profile, cooling_profile = [], [] 

717 for i, value in enumerate(self._heating_profile): 

718 if 0 <= i <= set_back_index_morning \ 

719 or set_back_index_evening <= i <= 24: 

720 heating_profile.append(value + self.heating_set_back) 

721 else: 

722 heating_profile.append(value) 

723 self._heating_profile = heating_profile 

724 for i, value in enumerate(self._cooling_profile): 

725 if 0 <= i <= set_back_index_morning \ 

726 or set_back_index_evening <= i <= 24: 

727 cooling_profile.append(value + self.cooling_set_back) 

728 else: 

729 cooling_profile.append(value) 

730 self._cooling_profile = cooling_profile 

731 

732 @property 

733 def adjusted_opening_times(self): 

734 return self._adjusted_opening_times 

735 

736 @adjusted_opening_times.setter 

737 def adjusted_opening_times(self, value): 

738 if len(value) != 2: 

739 raise ValueError(f"adjusted_opening_times must be list of length 2," 

740 f" but list of length {len(value)} was provided") 

741 elif value[0] < 0 or value[0] > 24 or value[1] < 0 or value[1] > 24: 

742 raise ValueError(f"elements of adjusted_opening_times must be " 

743 f"hours between 0 and 24. But are {value[0]} and" 

744 f" {value[1]}") 

745 else: 

746 self._adjusted_opening_times = value 

747 

748 @property 

749 def set_back_times(self): 

750 return self._set_back_times 

751 

752 @set_back_times.setter 

753 def set_back_times(self, value): 

754 if len(value) != 2: 

755 raise ValueError(f"set_back_times must be list of length 2," 

756 f" but list of length {len(value)} was provided") 

757 elif value[0] < 0 or value[0] > 24 or value[1] < 0 or value[1] > 24: 

758 raise ValueError(f"elements of set_back_times must be " 

759 f"hours between 0 and 24. But are {value[0]} and" 

760 f" {value[1]}") 

761 else: 

762 self._set_back_times = value 

763 

764 @property 

765 def first_saturday_of_year(self): 

766 return self._first_saturday_of_year 

767 

768 @first_saturday_of_year.setter 

769 def first_saturday_of_year(self, value): 

770 if value < 1 or value > 7: 

771 raise ValueError(f"first_saturday_of_year must be int between " 

772 f"[1, 7] but is {value}") 

773 elif not isinstance(value, int): 

774 raise ValueError(f"first_saturday_of_year must be int but is " 

775 f"{type(value)}") 

776 else: 

777 self._first_saturday_of_year = value 

778 

779 @property 

780 def parent(self): 

781 return self._parent 

782 

783 @parent.setter 

784 def parent(self, value): 

785 

786 if value is not None: 

787 

788 ass_error_1 = "Parent has to be an instance of ThermalZone()" 

789 

790 assert type(value).__name__ == "ThermalZone", ass_error_1 

791 

792 self._parent = value 

793 self._parent._use_conditions = self 

794 

795 else: 

796 

797 self._parent = None 

798 

799 @property 

800 def use_maintained_illuminance(self): 

801 return self._use_maintained_illuminance 

802 

803 @use_maintained_illuminance.setter 

804 def use_maintained_illuminance(self, value): 

805 if value: 

806 self._lighting_power = self.maintained_illuminance / self.lighting_efficiency_lumen 

807 else: 

808 self._lighting_power = self.fixed_lighting_power 

809 self._use_maintained_illuminance = value 

810 

811 

812 @property 

813 def lighting_power(self): 

814 return self._lighting_power 

815 

816 @lighting_power.setter 

817 def lighting_power(self, value): 

818 if self.use_maintained_illuminance: 

819 warnings.warn( 

820 "Parameter 'use_maintained_illuminance' is 'True'!\n" 

821 "Parameter 'lighting_power' will be overwritten and 'use_maintained_illuminance' will be set to 'False'.", 

822 ) 

823 self._use_maintained_illuminance = False 

824 self._lighting_power = value 

825 

826 @property 

827 def infiltration_rate(self): 

828 warnings.warn( 

829 "'infiltration_rate' is deprecated and will be removed in a future release. " 

830 "Use 'base_infiltration' instead.", 

831 DeprecationWarning, 

832 stacklevel=2) 

833 return self.base_infiltration 

834 

835 @infiltration_rate.setter 

836 def infiltration_rate(self, value): 

837 self.base_infiltration = value 

838 warnings.warn( 

839 "'infiltration_rate' is deprecated and will be removed in a future release. " 

840 "Use 'base_infiltration' instead.", 

841 DeprecationWarning, 

842 stacklevel=2) 

843