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