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
« 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
5import pandas as pd
6from itertools import cycle, islice
7from collections import OrderedDict
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
14class UseConditions(object):
15 """UseConditions class contains all zone specific boundary conditions.
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.
25 Note: Most attributes description are translations from DIN V 18599-10
26 standard
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.
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")
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.
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")
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.
238 """
240 def __init__(self, parent=None):
241 """Construct UseConditions."""
242 self.internal_id = random.random()
244 self.parent = parent
245 self.usage = "Single office"
247 self.typical_length = 6.0
248 self.typical_width = 6.0
250 self.with_heating = True
251 self.with_cooling = False
252 self.T_threshold_heating = 288.15
253 self.T_threshold_cooling = 295.15
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
261 self.machines = 7.0
262 self.ratio_conv_rad_machines = 0.75
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
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]
279 self.min_ahu = 0.0
280 self.max_ahu = 2.6
281 self.with_ahu = False
283 self._first_saturday_of_year = 1
284 self.profiles_weekend_factor = None
286 self._set_back_times = None
287 self.heating_set_back = -2
288 self.cooling_set_back = 2
290 self._adjusted_opening_times = None
292 self._with_ideal_thresholds = False
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 ]
425 self._schedules = None
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.
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
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
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.
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
512 def load_use_conditions(self, zone_usage, data_class=None):
513 """Load typical use conditions from JSON data base.
515 Loads Use conditions specified in the JSON.
517 Parameters
518 ----------
519 zone_usage : str
520 code list for zone_usage according to 18599 or self defined
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)
529 """
530 if data_class is None:
531 data_class = self.parent.parent.parent.data
532 else:
533 data_class = data_class
535 usecond_input.load_use_conditions(
536 use_cond=self, zone_usage=zone_usage, data_class=data_class
537 )
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
546 usecond_output.save_use_conditions(use_cond=self, data_class=data_class)
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).
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
566 @property
567 def persons(self):
568 return self._persons
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
577 @property
578 def with_ideal_thresholds(self):
579 return self._with_ideal_thresholds
581 @with_ideal_thresholds.setter
582 def with_ideal_thresholds(self, value):
583 self._with_ideal_thresholds = value
585 @property
586 def heating_profile(self):
587 return self._heating_profile
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 )
601 @property
602 def cooling_profile(self):
603 return self._cooling_profile
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 )
617 @property
618 def persons_profile(self):
619 return self._persons_profile
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 )
633 @property
634 def machines_profile(self):
635 return self._machines_profile
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 )
649 @property
650 def lighting_profile(self):
651 return self._lighting_profile
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 )
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
686 @schedules.setter
687 def schedules(self, value):
688 self._schedules = value
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.
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)
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)
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
732 @property
733 def adjusted_opening_times(self):
734 return self._adjusted_opening_times
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
748 @property
749 def set_back_times(self):
750 return self._set_back_times
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
764 @property
765 def first_saturday_of_year(self):
766 return self._first_saturday_of_year
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
779 @property
780 def parent(self):
781 return self._parent
783 @parent.setter
784 def parent(self, value):
786 if value is not None:
788 ass_error_1 = "Parent has to be an instance of ThermalZone()"
790 assert type(value).__name__ == "ThermalZone", ass_error_1
792 self._parent = value
793 self._parent._use_conditions = self
795 else:
797 self._parent = None
799 @property
800 def use_maintained_illuminance(self):
801 return self._use_maintained_illuminance
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
812 @property
813 def lighting_power(self):
814 return self._lighting_power
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
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
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)