Coverage for bim2sim/sim_settings.py: 93%
215 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-17 11:04 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-17 11:04 +0000
1"""Module for defining simulation model specific process settings.
2This targets both, settings to set for the later simulation and settings for
3the model generation process in bim2sim.
4"""
6import logging
7import ast
8import os.path
9from typing import Union, Optional, List
10import sys
11from pydantic import BaseModel, Field, model_validator, field_validator, FilePath, DirectoryPath
12from pydantic_core import PydanticCustomError
13from typing_extensions import Self
14from enum import Enum
15from bim2sim.utilities import types
16from bim2sim.utilities.types import LOD
17from bim2sim.elements.base_elements import Material
18from bim2sim.elements import bps_elements as bps_elements, \
19 hvac_elements as hvac_elements
20from bim2sim.elements.mapping.ifc2python import check_guid
23logger = logging.getLogger(__name__)
26class AutoSettingNameMeta(type):
27 """Sets the name to every SimulationSetting attribute based on its instance
28 name.
29 """
31 def __init__(cls, name, bases, namespace):
32 super(AutoSettingNameMeta, cls).__init__(name, bases, namespace)
33 # get all namespace objects
34 for name, obj in namespace.items():
35 # filter for settings of simulation
36 if isinstance(obj, Setting):
37 # provide name of the setting as attribute
38 obj.name = name
41class SettingsManager(dict):
42 """Manages the different settings of a SimulationSettings instance.
44 The manager is needed to maintain the different attributes of a simulation
45 (e.g. choices) while making the read and write access to the setting still
46 easy. This way you can call sim_settings.<setting_name> and get the value
47 directly while under sim_settings.manager.<setting_name> you can still find
48 all information.
50 Args:
51 bound_simulation_settings: instance of sim_settings this manager is
52 bound to. E.g. BuildingSimSettings.
53 """
54 defaults = {}
56 def __init__(self, bound_simulation_settings):
57 super().__init__()
58 self.bound_simulation_settings = bound_simulation_settings
59 self._create_settings()
61 def _create_settings(self):
62 """Add all listed settings from the simulation in its attributes."""
63 for name in self.names:
64 # Loads setting by name
65 setting = getattr(type(self.bound_simulation_settings), name)
66 self[name] = setting
68 # Store predefined default values in the defaults dict,
69 # so they can be set back to their defaults later
70 self.defaults[setting.name] = setting.value
72 @property
73 def names(self):
74 """Returns a generator object with all settings that the bound_simulation_settings owns."""
75 bound_simulation_settings_class = type(self.bound_simulation_settings)
77 for attribute_name in dir(bound_simulation_settings_class):
78 attribute = getattr(bound_simulation_settings_class, attribute_name)
80 if isinstance(attribute, Setting):
81 yield attribute_name
84class Setting(BaseModel, validate_assignment=True, validate_default=True):
85 """Base class for all simulation settings.
87 The attribute "value", which contains the payload of the simulation setting, is added in
88 the derived classes for the different data types (e.g. NumberSetting(Setting)).
90 The value, which is assigned to the attribute "value", when instancing the setting, serves
91 as a default value and can be changed according to the use case, if necessary.
93 Args:
94 name: Name of the setting. Is set automatically by AutoSettingNameMeta(type)
95 description: description the setting
96 for_frontend: should this setting be shown in the frontend
97 any_string: any string is allowed instead of a given choice
98 mandatory: whether a setting needs to be set
99 """
101 name: str = Field(default="set automatically")
102 description: Optional[str] = None
103 for_frontend: bool = Field(default=False)
104 any_string: bool = Field(default=False)
105 mandatory: bool = Field(default=False)
107 def __set__(self, bound_simulation_settings, value):
108 """Creates a new attribute with the name and value of the simulation setting instance,
109 stored in sim_settings. This makes it easier to access the setting's payload.
111 Example:
113 sim_settings = {e.g. BuildingSimSettings}
114 ...
115 example_setting_name = {bool}True
116 manager {SettingsManager}
117 'example_setting_name' = {BooleanSetting}(name='ahu_heating_overwrite',value=True, ...)
118 ...
119 ...
120 """
121 bound_simulation_settings.manager[self.name].value = value
123 def __get__(self, bound_simulation_settings, owner):
124 """Allows direct access to the setting's value"""
125 if bound_simulation_settings is None:
126 return self
128 return bound_simulation_settings.manager[self.name].value
131class NumberSetting(Setting):
132 value: Optional[Union[float, int]]
133 min_value: Optional[Union[float, int]] = None
134 max_value: Optional[Union[float, int]] = None
136 @model_validator(mode='after')
137 def check_setting_config(self) -> Self:
138 if self.min_value is None:
139 self.min_value = sys.float_info.epsilon
140 logger.info(f'No min_value given for sim_setting {self}, assuming'
141 f'smallest float epsilon.')
143 if self.max_value is None:
144 self.max_value = float('inf')
145 logger.info(f'No max_value given for sim_setting {self}, assuming'
146 f'biggest float inf.')
148 if self.min_value > self.max_value:
149 raise PydanticCustomError("contradictory_limits",
150 f"The specified limits for min_value and max_value are " # type: ignore[misc]
151 f"contradictory min: {self.min_value} max: {self.max_value}")
153 return self
155 @model_validator(mode='after')
156 def check_limits(self) -> Self:
157 if self.value is not None:
158 if not (self.min_value <= self.value <= self.max_value):
159 raise PydanticCustomError(
160 "value_out_of_range",
161 f"value ({self.value}) must be between {self.min_value} and {self.max_value}" # type: ignore[misc]
162 )
163 return self
166class ChoiceSetting(Setting):
167 value: Union[str, List[str], Enum, None]
168 choices: dict
169 multiple_choice: bool = False
171 def _check_for_value_in_choices(self, value):
172 if value not in self.choices:
173 if not self.any_string:
174 raise PydanticCustomError(
175 "value_not_in_choices",
176 f'{value} is no valid value for setting {self.name}, select one of {self.choices}.' # type: ignore[misc]
177 )
179 @field_validator('choices', mode='after')
180 @classmethod
181 def check_setting_config(cls, choices):
182 for choice in choices:
183 # Check for string type, to exclude enums
184 if isinstance(choice, str) and "." in choice:
185 raise PydanticCustomError("illegal_character",
186 f"Provided setting {choice} contains character '.', this is prohibited.") # type: ignore[misc]
187 return choices
189 @model_validator(mode='after')
190 def check_content(self):
191 if isinstance(self.value, list):
192 if not self.multiple_choice:
193 raise PydanticCustomError("one_choice_allowed", f'Only one choice is allowed for setting' # type: ignore[misc]
194 f' {self.name}, but {len(self.value)} choices are given.') # type: ignore[misc]
195 else:
196 for val in self.value:
197 self._check_for_value_in_choices(val)
198 else:
199 self._check_for_value_in_choices(self.value)
201 return self
204class PathSetting(Setting):
205 value: Optional[Union[DirectoryPath, FilePath]]
208class BooleanSetting(Setting):
209 value: Optional[bool]
212class GuidListSetting(Setting):
213 value: Optional[List[str]] = None
215 @field_validator('value', mode='after')
216 @classmethod
217 def check_value(cls, value):
218 if value is not None:
219 for i, guid in enumerate(value):
220 if not check_guid(guid):
221 raise PydanticCustomError("invalid_guid",
222 f"Invalid IFC GUID format at index {i}: '{guid}'") # type: ignore[misc]
223 return value
226class BaseSimSettings(metaclass=AutoSettingNameMeta):
227 """Specification of basic bim2sim simulation settings which are common for
228 all simulations"""
230 def __init__(self, filters: list = None):
231 self.manager = SettingsManager(bound_simulation_settings=self)
233 self.relevant_elements = {}
234 self.simulated = False
236 def load_default_settings(self):
237 """loads default values for all settings"""
239 for setting in self.manager.values():
240 default = self.manager.defaults[setting.name]
241 setting.value = default
243 def update_from_config(self, config):
244 """Updates the simulation settings specification from the config
245 file"""
246 n_loaded_settings = 0
247 for cat, settings in config.items():
248 # don't load settings which are not simulation relevant
249 if cat.lower() not in [
250 self.__class__.__name__.lower(),
251 'Generic Simulation Settings'
252 ]:
253 continue
254 cat_from_cfg = config[cat]
255 for setting in settings:
256 if not hasattr(self, setting):
257 raise AttributeError(
258 f'{setting} is no allowed setting for '
259 f'simulation {self.__class__.__name__} ')
260 else:
261 set_from_cfg = cat_from_cfg.get(setting)
262 if set_from_cfg is None:
263 continue
264 elif isinstance(set_from_cfg, str):
265 # convert to readable python object
266 try:
267 # todo ast.literal_eval is safer but not safe.
268 set_from_cfg = ast.literal_eval(str(set_from_cfg))
269 except (ValueError, SyntaxError):
270 logger.warning(f'Failed literal evaluation of '
271 f'{set_from_cfg}. Proceeding.')
272 if isinstance(set_from_cfg, str):
273 # handle all strings that are file paths, before
274 # handling Enums
275 if os.path.isfile(set_from_cfg):
276 val = set_from_cfg
277 # handle Enums (will not be found by literal_eval)
278 elif isinstance(set_from_cfg, str) and \
279 '.' in set_from_cfg:
280 enum_type, enum_val = set_from_cfg.split('.')
281 # convert str to enum
282 try:
283 enum_type = getattr(types, enum_type)
284 val = getattr(enum_type, enum_val)
285 except AttributeError:
286 raise AttributeError(
287 f" Tried to create the enumeration "
288 f"{enum_type} but it doesn't exist.")
289 else:
290 # handle all other strings
291 val = set_from_cfg
292 else:
293 # handle all other data types
294 val = set_from_cfg
295 setattr(self, setting, val)
296 n_loaded_settings += 1
297 else:
298 raise TypeError(
299 f'Config entry for {setting} is no string. '
300 f'Please use strings only in config.')
301 logger.info(f'Loaded {n_loaded_settings} settings from config file.')
303 def check_mandatory(self):
304 """Check if mandatory settings have a value."""
305 for setting in self.manager.values():
306 if setting.mandatory:
307 if not setting.value:
308 raise ValueError(
309 f"Attempted to run project. Simulation setting "
310 f"{setting.name} is not specified, "
311 f"but is marked as mandatory. Please configure "
312 f"{setting.name} before running your project.")
315 dymola_simulation = BooleanSetting(
316 value=False,
317 description="Run a Simulation with Dymola after model export?",
318 for_frontend=True,
319 )
321 create_external_elements = BooleanSetting(
322 value=False,
323 description='Create external elements?',
324 for_frontend=True
325 )
327 max_wall_thickness = NumberSetting(
328 value=0.3,
329 max_value=0.60,
330 min_value=1e-3,
331 description='Choose maximum wall thickness as a tolerance for mapping '
332 'opening boundaries to their base surface (Wall). '
333 'Choose 0.3m as a default value.',
334 for_frontend=True
335 )
337 group_unidentified = ChoiceSetting(
338 value='fuzzy',
339 choices={
340 'fuzzy': 'Use fuzzy search to find ifc name similarities',
341 'name': 'Only group elements with exact same ifc name',
342 'name_and_description': 'Only group elements with the same ifc'
343 ' name and ifc description'
344 },
345 description='To reduce the number of decisions by user to identify '
346 'elements which can not be identified automatically by '
347 'the '
348 'system, you can either use simple grouping by same name '
349 'of'
350 ' IFC element or fuzzy search to group based on'
351 ' similarities in name.',
352 for_frontend=True,
353 )
355 group_unidentified_ = ChoiceSetting(
356 value='fuzzy',
357 choices={
358 'fuzzy': 'Use fuzzy search to find ifc name similarities',
359 'name': 'Only group elements with exact same ifc name',
360 'name_and_description': 'Only group elements with the same ifc'
361 ' name and ifc description'
362 },
363 description='To reduce the number of decisions by user to identify '
364 'elements which can not be identified automatically by '
365 'the '
366 'system, you can either use simple grouping by same name '
367 'of'
368 ' IFC element or fuzzy search to group based on'
369 ' similarities in name.',
370 for_frontend=True
371 )
373 fuzzy_threshold = NumberSetting(
374 value=0.7,
375 min_value=0.5,
376 max_value=0.9,
377 description='If you want to use fuzzy search in the '
378 'group_unidentified '
379 'setting, you can set the threshold here. A low '
380 'threshold means'
381 ' a small similarity is required for grouping. A too low '
382 'value '
383 'might result in grouping elements which do not represent '
384 'the same IFC type.'
385 )
387 reset_guids = BooleanSetting(
388 value=False,
389 description='Reset GlobalIDs from imported IFC if duplicate '
390 'GlobalIDs occur in the IFC. As EnergyPlus evaluates all'
391 'GlobalIDs upper case only, this might also be '
392 'applicable if duplicate non-case-sensitive GlobalIDs '
393 'occur.',
394 for_frontend=True
395 )
397 weather_file_path = PathSetting(
398 value=None,
399 description='Path to the weather file that should be used for the '
400 'simulation. If no path is provided, we will try to get '
401 'the'
402 'location from the IFC and download a fitting weather'
403 ' file. For Modelica provide .mos files, for EnergyPlus '
404 '.epw files. If the format does not fit, we will try to '
405 'convert.',
406 for_frontend=True,
407 mandatory=True
408 )
410 building_rotation_overwrite = NumberSetting(
411 value=0,
412 min_value=0,
413 max_value=359,
414 description='Overwrite the (clockwise) building rotation angle in '
415 'degrees.',
416 for_frontend=True
417 )
419 add_space_boundaries = BooleanSetting(
420 value=False,
421 description='Add space boundaries. Only required for building '
422 'performance simulation and co-simulations.',
423 for_frontend=True
424 )
425 correct_space_boundaries = BooleanSetting(
426 value=False,
427 description='Apply geometric correction to space boundaries.',
428 for_frontend=True
429 )
430 close_space_boundary_gaps = BooleanSetting(
431 value=False,
432 description='Close gaps in the set of space boundaries by adding '
433 'additional 2b space boundaries.',
434 for_frontend=True
435 )
437 stories_to_load_guids = GuidListSetting(
438 value=[],
439 description='List of IFC GUIDs for the specific stories that should '
440 'be loaded. If empty, all stories will be considered '
441 'for loading. This setting is useful for large buildings '
442 'to reduce computational time. Note that loading single '
443 'storeys may lead to missing ceilings if the related '
444 'slab is assigned to the storey above, which may require '
445 'corrections to boundary conditions.'
446 ' It is recommended to include GUIDs of neighboring'
447 ' storeys to reduce boundary condition errors.',
448 for_frontend=True,
449 mandatory=False
450 )
453class PlantSimSettings(BaseSimSettings):
454 def __init__(self):
455 super().__init__(
456 )
457 self.relevant_elements = {*hvac_elements.items, Material}
459 # Todo maybe make every aggregation its own setting with LOD in the future,
460 # but currently we have no usage for this afaik.
461 aggregations = ChoiceSetting(
462 value=[
463 'UnderfloorHeating',
464 'PipeStrand',
465 'Consumer',
466 'ParallelPump',
467 'ConsumerHeatingDistributorModule',
468 'GeneratorOneFluid',
469 ],
470 choices={
471 'UnderfloorHeating': 'Aggregate underfloor heating circuits',
472 'Consumer': 'Aggregate consumers',
473 'PipeStrand': 'Aggregate strands of pipes',
474 'ParallelPump': 'Aggregate parallel pumps',
475 'ConsumerHeatingDistributorModule': 'Aggregate consumer and '
476 'distributor to one module',
477 'GeneratorOneFluid': 'Aggregate the generator and its circuit to '
478 'one module',
479 },
480 description="Which aggregations should be applied on the hydraulic "
481 "network",
482 multiple_choice=True,
483 for_frontend=True
484 )
486 tolerance_connect_by_position = NumberSetting(
487 value=10,
488 description="Tolerance for distance for which ports should be "
489 "connected. Based on there position in IFC.",
490 for_frontend=True,
491 min_value=1
492 )
494 verify_connection_by_position = BooleanSetting(
495 value=True,
496 description="Choose if connection of elements via IfcDistributionPorts"
497 " should be validated by the geometric position of the "
498 "ports."
499 )
502class BuildingSimSettings(BaseSimSettings):
504 def __init__(self):
505 super().__init__()
506 self.relevant_elements = {*bps_elements.items,
507 Material}
509 layers_and_materials = ChoiceSetting(
510 value=LOD.low,
511 choices={
512 LOD.low: 'Override materials with predefined setups',
513 # LOD.full: 'Get all information from IFC and enrich if needed'
514 },
515 description='Select how existing Material information in IFC should '
516 'be treated.',
517 for_frontend=True
518 )
519 year_of_construction_overwrite = NumberSetting(
520 value=None,
521 min_value=0,
522 max_value=2015,
523 description="Force an overwrite of the year of construction as a "
524 "base for the selected construction set.",
525 for_frontend=True,
526 )
527 construction_class_walls = ChoiceSetting(
528 value='iwu_heavy',
529 choices={
530 'iwu_heavy': 'Wall structures according to iwu heavy standard',
531 'iwu_light': 'Wall structures according to iwu light standard',
532 'kfw_40': 'Wall structures according to kfw 40 standard',
533 'kfw_55': 'Wall structures according to kfw 55 standard',
534 'kfw_70': 'Wall structures according to kfw 70 standard',
535 'kfw_85': 'Wall structures according to kfw 85 standard',
536 'kfw_100': 'Wall structures according to kfw 100 standard',
537 'tabula_de_standard_1_SFH': 'Wall structures according to german '
538 'tabula standard 1 for single family '
539 'houses',
540 'tabula_de_standard_2_SFH': 'Wall structures according to german '
541 'tabula standard 2 for single family '
542 'houses',
543 'tabula_de_retrofit_1_SFH': 'Wall structures according to german '
544 'tabula retrofit 1 for single family '
545 'houses',
546 'tabula_de_retrofit_2_SFH': 'Wall structures according to german '
547 'tabula retrofit 2 for single family '
548 'houses',
549 'tabula_de_adv_retrofit_1_SFH': 'Wall structures according to '
550 'german tabula advanced retrofit '
551 '1 for single '
552 'family houses',
553 'tabula_de_adv_retrofit_2_SFH': 'Wall structures according to '
554 'german tabula advanced retrofit '
555 '2 for '
556 'single family houses',
557 'tabula_de_standard_1_TH': 'Wall structures according to german '
558 'tabula standard 1 for terraced houses',
559 'tabula_de_standard_2_TH': 'Wall structures according to german '
560 'tabula standard 2 for terraced houses',
561 'tabula_de_retrofit_1_TH': 'Wall structures according to german '
562 'tabula retrofit 1 for terraced houses',
563 'tabula_de_retrofit_2_TH': 'Wall structures according to german '
564 'tabula retrofit 2 for terraced houses',
565 'tabula_de_standard_1_MFH': 'Wall structures according to german '
566 'tabula standard 1 for multi family '
567 'houses',
568 'tabula_de_retrofit_1_MFH': 'Wall structures according to german '
569 'tabula retrofit 1 for multi family '
570 'houses',
571 'tabula_de_adv_retrofit_1_MFH': 'Wall structures according to '
572 'german tabula advanced retrofit '
573 '1 for multi '
574 'family houses',
575 'tabula_de_standard_1_AB': 'Wall structures according to german '
576 'tabula standard 1 for apartment '
577 'blocks',
578 'tabula_de_adv_retrofit_1_AB': 'Wall structures according to '
579 'german tabula advanced retrofit '
580 '1 for '
581 'apartment blocks',
582 'tabula_de_standard': 'Wall structures according to german '
583 'tabula standard',
584 'tabula_dk_standard_1_SFH': 'Wall structures according to danish '
585 'tabula standard 1 for single family '
586 'houses',
587 'tabula_dk_standard_2_SFH': 'Wall structures according to danish '
588 'tabula standard 2 for single family '
589 'houses',
590 'tabula_dk_retrofit_1_SFH': 'Wall structures according to danish '
591 'tabula retrofit 1 for single family '
592 'houses',
593 'tabula_dk_retrofit_2_SFH': 'Wall structures according to danish '
594 'tabula retrofit 2 for single family '
595 'houses',
596 'tabula_dk_adv_retrofit_1_SFH': 'Wall structures according to '
597 'danish tabula advanced retrofit '
598 '1 for single '
599 'family houses',
600 'tabula_dk_adv_retrofit_2_SFH': 'Wall structures according to '
601 'danish tabula advanced retrofit '
602 '2 for single '
603 'family houses',
604 'tabula_dk_standard_1_TH': 'Wall structures according to danish '
605 'tabula standard 1 for terraced houses',
606 'tabula_dk_standard_2_TH': 'Wall structures according to danish '
607 'tabula standard 2 for terraced houses',
608 'tabula_dk_retrofit_1_TH': 'Wall structures according to danish '
609 'tabula retrofit 1 for terraced houses',
610 'tabula_dk_retrofit_2_TH': 'Wall structures according to danish '
611 'tabula retrofit 1 for terraced houses',
612 'tabula_dk_adv_retrofit_1_TH': 'Wall structures according to '
613 'danish tabula advanced retrofit '
614 '1 for '
615 'terraced houses',
616 'tabula_dk_adv_retrofit_2_TH': 'Wall structures according to '
617 'danish tabula advanced retrofit '
618 '1 for '
619 'terraced houses',
620 'tabula_dk_standard_1_AB': 'Wall structures according to danish '
621 'tabula standard 1 for apartment '
622 'blocks',
623 'tabula_dk_standard_2_AB': 'Wall structures according to danish '
624 'tabula standard 2 for apartment '
625 'blocks',
626 'tabula_dk_retrofit_1_AB': 'Wall structures according to danish '
627 'tabula retrofit 1 for apartment '
628 'blocks',
629 'tabula_dk_retrofit_2_AB': 'Wall structures according to danish '
630 'tabula retrofit 2 for apartment '
631 'blocks',
632 'tabula_dk_adv_retrofit_1_AB': 'Wall structures according to '
633 'danish tabula advanced retrofit '
634 '1 for '
635 'apartment blocks',
636 'tabula_dk_adv_retrofit_2_AB': 'Wall structures according to '
637 'danish tabula advanced retrofit '
638 '2 for '
639 'apartment blocks',
640 'tabula_dk_standard': 'Wall structures according to danish '
641 'tabula standard'
642 },
643 description="Select the most fitting construction class type for"
644 "the walls of the selected building. For all settings but "
645 "kfw_* the year of construction is required.",
646 for_frontend=True
647 )
649 construction_class_windows = ChoiceSetting(
650 value='Alu- oder Stahlfenster, Waermeschutzverglasung, zweifach',
651 choices={
652 'Holzfenster, zweifach':
653 'Zeifachverglasung mit Holzfenstern',
654 'Kunststofffenster, Isolierverglasung':
655 'Isolierverglasung mit Kunststofffensern',
656 'Alu- oder Stahlfenster, Isolierverglasung':
657 'Isolierverglasung mit Alu- oder Stahlfenstern',
658 'Alu- oder Stahlfenster, Waermeschutzverglasung, zweifach':
659 'Wärmeschutzverglasung (zweifach) mit Alu- oder '
660 'Stahlfenstern',
661 'Waermeschutzverglasung, dreifach':
662 'Wärmeschutzverglasung (dreifach)',
663 'tabula_de_standard_1_SFH': 'Windows according to german tabula '
664 'standard 1 for single family '
665 'houses',
666 'tabula_de_standard_2_SFH': 'Windows according to german tabula '
667 'standard 2 for single family '
668 'houses',
669 'tabula_de_retrofit_1_SFH': 'Windows according to german tabula '
670 'retrofit 1 for single family '
671 'houses',
672 'tabula_de_retrofit_2_SFH': 'Windows according to german tabula '
673 'retrofit 2 for single family '
674 'houses',
675 'tabula_de_adv_retrofit_1_SFH': 'Windows according to german '
676 'tabula advanced retrofit 1 for '
677 'single '
678 'family houses',
679 'tabula_de_adv_retrofit_2_SFH': 'Windows according to german '
680 'tabula advanced retrofit 2 for '
681 'single family houses',
682 'tabula_de_standard_1_TH': 'Windows according to german tabula '
683 'standard 1 for terraced houses',
684 'tabula_de_standard_2_TH': 'Windows according to german tabula '
685 'standard 2 for terraced houses',
686 'tabula_de_retrofit_1_TH': 'Windows according to german tabula '
687 'retrofit 1 for terraced houses',
688 'tabula_de_retrofit_2_TH': 'Windows according to german tabula '
689 'retrofit 2 for terraced houses',
690 'tabula_de_standard_1_MFH': 'Windows according to german tabula '
691 'standard 1 for multi family houses',
692 'tabula_de_retrofit_1_MFH': 'Windows according to german tabula '
693 'retrofit 1 for multi family houses',
694 'tabula_de_adv_retrofit_1_MFH': 'Windows according to german '
695 'tabula advanced retrofit 1 for '
696 'multi '
697 'family houses',
698 'tabula_de_standard_1_AB': 'Windows according to german tabula '
699 'standard 1 for apartment blocks',
700 'tabula_de_adv_retrofit_1_AB': 'Windows according to german '
701 'tabula advanced retrofit 1 for '
702 'apartment blocks',
703 'tabula_de_standard': 'Windows according to german tabula '
704 'standard',
705 'tabula_dk_standard_1_SFH': 'Windows according to danish tabula '
706 'standard 1 for single family '
707 'houses',
708 'tabula_dk_standard_2_SFH': 'Windows according to danish tabula '
709 'standard 2 for single family '
710 'houses',
711 'tabula_dk_retrofit_1_SFH': 'Windows according to danish tabula '
712 'retrofit 1 for single family '
713 'houses',
714 'tabula_dk_retrofit_2_SFH': 'Windows according to danish tabula '
715 'retrofit 2 for single family '
716 'houses',
717 'tabula_dk_adv_retrofit_1_SFH': 'Windows according to danish '
718 'tabula advanced retrofit 1 for '
719 'single '
720 'family houses',
721 'tabula_dk_adv_retrofit_2_SFH': 'Windows according to danish '
722 'tabula advanced retrofit 2 for '
723 'single '
724 'family houses',
725 'tabula_dk_standard_1_TH': 'Windows according to danish tabula '
726 'standard 1 for terraced houses',
727 'tabula_dk_standard_2_TH': 'Windows according to danish tabula '
728 'standard 2 for terraced houses',
729 'tabula_dk_retrofit_1_TH': 'Windows according to danish tabula '
730 'retrofit 1 for terraced houses',
731 'tabula_dk_retrofit_2_TH': 'Windows according to danish tabula '
732 'retrofit 1 for terraced houses',
733 'tabula_dk_adv_retrofit_1_TH': 'Windows according to danish '
734 'tabula advanced retrofit 1 for '
735 'terraced houses',
736 'tabula_dk_adv_retrofit_2_TH': 'Windows according to danish '
737 'tabula advanced retrofit 1 for '
738 'terraced houses',
739 'tabula_dk_standard_1_AB': 'Windows according to danish tabula '
740 'standard 1 for apartment blocks',
741 'tabula_dk_standard_2_AB': 'Windows according to danish tabula '
742 'standard 2 for apartment blocks',
743 'tabula_dk_retrofit_1_AB': 'Windows according to danish tabula '
744 'retrofit 1 for apartment blocks',
745 'tabula_dk_retrofit_2_AB': 'Windows according to danish tabula '
746 'retrofit 2 for apartment blocks',
747 'tabula_dk_adv_retrofit_1_AB': 'Windows according to danish '
748 'tabula advanced retrofit 1 for '
749 'apartment blocks',
750 'tabula_dk_adv_retrofit_2_AB': 'Windows according to danish '
751 'tabula advanced retrofit 2 for '
752 'apartment blocks',
753 'tabula_dk_standard': 'Windows according to danish tabula standard'
754 },
755 description="Select the most fitting construction class type for"
756 " the windows of the selected building.",
757 )
758 construction_class_doors = ChoiceSetting(
759 value='iwu_typical',
760 choices={
761 'iwu_typical': 'Typical door data based',
762 'kfw_40': 'Doors according to kfw 40 standard',
763 'kfw_55': 'Doors according to kfw 55 standard',
764 'kfw_70': 'Doors according to kfw 70 standard',
765 'kfw_85': 'Doors according to kfw 85 standard',
766 'kfw_100': 'Doors according to kfw 100 standard',
767 'tabula_de_standard_1_SFH': 'Windows according to german tabula '
768 'standard 1 for single family '
769 'houses',
770 'tabula_de_retrofit_1_SFH': 'Windows according to german tabula '
771 'retrofit 1 for single family '
772 'houses',
773 'tabula_de_adv_retrofit_1_SFH': 'Windows according to german '
774 'tabula advanced retrofit 1 for '
775 'single '
776 'family houses',
777 'tabula_de_standard_1_TH': 'Windows according to german tabula '
778 'standard 1 for terraced houses',
779 'tabula_de_retrofit_1_TH': 'Windows according to german tabula '
780 'retrofit 1 for terraced houses',
781 'tabula_de_adv_retrofit_1_TH': 'Windows according to german '
782 'tabula advanced retrofit 1 for '
783 'terraced houses',
784 'tabula_de_standard_1_MFH': 'Windows according to german tabula '
785 'standard 1 for multi family houses',
786 'tabula_de_retrofit_1_MFH': 'Windows according to german tabula '
787 'retrofit 1 for multi family houses',
788 'tabula_de_adv_retrofit_1_MFH': 'Windows according to german '
789 'tabula advanced retrofit 1 for '
790 'multi '
791 'family houses',
792 'tabula_de_standard_1_AB': 'Windows according to german tabula '
793 'standard 1 for apartment blocks',
794 'tabula_de_retrofit_1_AB': 'Windows according to german tabula '
795 'retrofit 1 for apartment blocks',
796 'tabula_de_adv_retrofit_1_AB': 'Windows according to german '
797 'tabula advanced retrofit 1 for '
798 'apartment blocks',
799 'tabula_dk_standard_1_SFH': 'Windows according to danish tabula '
800 'standard 1 for single family '
801 'houses'
802 },
803 description="Select the most fitting construction class type for"
804 " the windows of the selected building.",
805 )
806 heating_tz_overwrite = BooleanSetting(
807 value=None,
808 description='If True, all thermal zones will be provided with heating,'
809 'if False no heating for thermal zones is provided, '
810 'regardless of information in the IFC or in the use '
811 'condition file.',
812 for_frontend=True
813 )
814 cooling_tz_overwrite = BooleanSetting(
815 value=None,
816 description='If True, all thermal zones will be provided with cooling,'
817 'if False no cooling for thermal zones is provided, '
818 'regardless of information in the IFC or in the use '
819 'condition file.',
820 for_frontend=True
821 )
822 ahu_tz_overwrite = BooleanSetting(
823 value=None,
824 description='If True, all thermal zones will be provided with AHU,'
825 'if False no AHU for thermal zones is provided, '
826 'regardless of information in the IFC or in the use '
827 'condition file.',
828 for_frontend=True
829 )
830 prj_use_conditions = PathSetting(
831 value=None,
832 description="Path to a custom UseConditions.json for the specific "
833 "project, that holds custom usage conditions for this "
834 "project. If this is used, this use_conditions file have "
835 "to hold all information. The basic UseConditions.json "
836 "file is ignored in this case.",
837 for_frontend=True
838 )
839 prj_custom_usages = PathSetting(
840 value=None,
841 description="Path to a custom customUsages.json for the specific "
842 "project, that holds mappings between space names from "
843 "IFC "
844 "and usage conditions from UseConditions.json.",
845 for_frontend=True
846 )
847 setpoints_from_template = BooleanSetting(
848 value=False,
849 description="Use template heating and cooling profiles instead of "
850 "setpoints from IFC. Defaults to False, i.e., "
851 "use original data source. Set to True, "
852 "if template-based values should be used instead.",
853 for_frontend=True
854 )
855 use_maintained_illuminance = BooleanSetting(
856 value=True,
857 description="Use maintained illuminance required per zone based on "
858 "DIN V EN 18599 information to calculate internal loads"
859 "through lighting.",
860 for_frontend=True
861 )
862 sim_results = ChoiceSetting(
863 value=[
864 "heat_demand_total", "cool_demand_total",
865 "heat_demand_rooms", "cool_demand_rooms",
866 "heat_energy_total", "cool_energy_total",
867 "heat_energy_rooms", "cool_energy_rooms",
868 "air_temp_out", "operative_temp_rooms", "air_temp_rooms",
869 "internal_gains_machines_rooms", "internal_gains_persons_rooms",
870 "internal_gains_lights_rooms", "n_persons_rooms",
871 "infiltration_rooms", "mech_ventilation_rooms",
872 "heat_set_rooms", "cool_set_rooms"
874 ],
875 choices={
876 "heat_demand_total":
877 "Total heating demand (power) as time series data",
878 "cool_demand_total":
879 "Total cooling demand (power) as time series data",
880 "heat_demand_rooms":
881 "Zone based heating demand (power) as time series data",
882 "cool_demand_rooms":
883 "Zone based cooling demand (power) as time series data",
884 "heat_energy_total":
885 "Total heating energy as time series data",
886 "cool_energy_total":
887 "Total cooling energy as time series data",
888 "heat_energy_rooms":
889 "Zone based heating energy as time series data",
890 "cool_energy_rooms":
891 "Zone cooling heating energy as time series data",
892 "air_temp_out":
893 "Outdoor air temperature as time series data",
894 "operative_temp_rooms":
895 "Zone based operative temperature as time series data",
896 "air_temp_rooms":
897 "Zone based indoor air temperature as time series data",
898 "internal_gains_machines_rooms":
899 "Internal gains through machines in W as time series data",
900 "internal_gains_persons_rooms":
901 "Internal gains through persons in W as time series data",
902 "internal_gains_lights_rooms":
903 "Internal gains through lights in W as time series data",
904 "n_persons_rooms":
905 "Total amount of occupying persons as time series data",
906 "infiltration_rooms":
907 "Infiltration into room in 1/h as time series data",
908 "mech_ventilation_rooms":
909 "Mechanical ventilation flow in m³/h as time series data",
910 "heat_set_rooms":
911 "Heating set point in °C time series data",
912 "cool_set_rooms":
913 "Cooling set point in °C time series data",
914 },
915 multiple_choice=True,
916 )
917 add_space_boundaries = BooleanSetting(
918 value=True,
919 description='Add space boundaries. Only required for building '
920 'performance simulation and co-simulations.',
921 for_frontend=True
922 )
923 correct_space_boundaries = BooleanSetting(
924 value=False,
925 description='Apply geometric correction to space boundaries.',
926 for_frontend=True
927 )
928 split_bounds = BooleanSetting(
929 value=False,
930 description='Whether to convert up non-convex space boundaries or '
931 'not.',
932 for_frontend=True
933 )
934 add_shadings = BooleanSetting(
935 value=False,
936 description='Whether to add shading surfaces if available or not.',
937 for_frontend=True
938 )
939 split_shadings = BooleanSetting(
940 value=False,
941 description='Whether to convert up non-convex shading boundaries or '
942 'not.',
943 for_frontend=True
944 )
945 close_space_boundary_gaps = BooleanSetting(
946 value=False,
947 description='Close gaps in the set of space boundaries by adding '
948 'additional 2b space boundaries.',
949 for_frontend=True
950 )
951 create_plots = BooleanSetting(
952 value=False,
953 description='Create plots for simulation results after the simulation '
954 'finished.',
955 for_frontend=True
956 )
957 set_run_period = BooleanSetting(
958 value=False,
959 description="Choose whether run period for simulation execution "
960 "should be set manually instead of running annual "
961 "simulation."
962 )
963 run_period_start_month = NumberSetting(
964 value=1,
965 min_value=1,
966 max_value=12,
967 description="Choose start month of run period. Requires "
968 "set_run_period==True for activation.",
969 for_frontend=True
970 )
971 run_period_start_day = NumberSetting(
972 value=1,
973 min_value=1,
974 max_value=31,
975 description="Choose start day of run period. Requires "
976 "set_run_period==True for activation.",
977 for_frontend=True
978 )
979 run_period_end_month = NumberSetting(
980 value=12,
981 min_value=1,
982 max_value=12,
983 description="Choose end month of run period. Requires "
984 "set_run_period==True for activation.",
985 for_frontend=True
986 )
987 run_period_end_day = NumberSetting(
988 value=31,
989 min_value=1,
990 max_value=31,
991 description="Choose end day of run period. Requires "
992 "set_run_period==True for activation.",
993 for_frontend=True
994 )
995 plot_singe_zone_guid = ChoiceSetting(
996 value='',
997 choices={'': "Skip"},
998 description="Choose the GlobalId of the IfcSpace for which results "
999 "should be plotted.",
1000 any_string=True
1001 )
1002 ahu_heating_overwrite = BooleanSetting(
1003 value=None,
1004 description="Choose if the central AHU should provide heating. "
1005 )
1006 ahu_cooling_overwrite = BooleanSetting(
1007 value=None,
1008 description="Choose if the central AHU should provide cooling."
1009 )
1010 ahu_dehumidification_overwrite = BooleanSetting(
1011 value=None,
1012 description="Choose if the central AHU should provide "
1013 "dehumidification."
1014 )
1015 ahu_humidification_overwrite = BooleanSetting(
1016 value=None,
1017 description="Choose if the central AHU should provide humidification."
1018 "otherwise this has no effect. "
1019 )
1020 ahu_heat_recovery_overwrite = BooleanSetting(
1021 value=None,
1022 description="Choose if the central AHU should zuse heat recovery."
1023 )
1024 ahu_heat_recovery_efficiency_overwrite = NumberSetting(
1025 value=None,
1026 min_value=0.5,
1027 max_value=0.99,
1028 description="Choose the heat recovery efficiency of the central AHU."
1029 )
1030 use_constant_infiltration_overwrite = BooleanSetting(
1031 value=None,
1032 description="If only constant base infiltration should be used and no "
1033 "dynamic ventilation through e.g. windows."
1034 )
1035 base_infiltration_rate_overwrite = NumberSetting(
1036 value=None,
1037 min_value=0.001,
1038 max_value=5,
1039 description="Overwrite base value for the natural infiltration in 1/h "
1040 " without window openings"
1041 )