Coverage for bim2sim/sim_settings.py: 90%

260 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-05-16 08:28 +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""" 

5import logging 

6import ast 

7import os.path 

8from pathlib import Path 

9from typing import Union 

10import sys 

11 

12from bim2sim.utilities import types 

13from bim2sim.utilities.types import LOD 

14from bim2sim.elements.base_elements import Material 

15from bim2sim.elements import bps_elements as bps_elements, \ 

16 hvac_elements as hvac_elements 

17from bim2sim.elements.mapping.ifc2python import check_guid 

18 

19 

20logger = logging.getLogger(__name__) 

21 

22 

23class AutoSettingNameMeta(type): 

24 """Adds the name to every SimulationSetting attribute based on its instance 

25 name. 

26 

27 This makes the definition of an extra attribute 'name' obsolete, as the 

28 attributes 'name' is automatic defined based on the instance name. 

29 

30 

31 Example: 

32 >>> # create new simulation settings for your awesome simulation 

33 >>> class MyAwesomeSimulationSettings(BaseSimSettings): 

34 ... def __init__(self): 

35 ... super().__init__() 

36 

37 >>> # create a new simulation setting, name will be taken automatic 

38 from 

39 >>> # instance name 

40 >>> make_simulation_extra_fast = Setting( 

41 ... default=True, 

42 ... choices={ 

43 ... True: 'This simulation will be incredible fast.', 

44 ... False: 'This simulation will be increbdile slow.' 

45 ... }, 

46 ... description='Run the simulation in extra fast mode?', 

47 ... for_frontend=True 

48 ... ) 

49 

50 >>> # create a SimulationSettings instance and get the value 

51 >>> my_awesome_settings = MyAwesomeSimulationSettings() 

52 >>> # get initial value which is always none 

53 >>> print(my_awesome_settings.make_simulation_extra_fast) 

54 None 

55 >>> # set default values and get the value 

56 >>> my_awesome_settings.load_default_settings() 

57 >>> print(my_awesome_settings.make_simulation_extra_fast) 

58 True 

59""" 

60 

61 def __init__(cls, name, bases, namespace): 

62 super(AutoSettingNameMeta, cls).__init__(name, bases, namespace) 

63 # get all namespace objects 

64 for name, obj in namespace.items(): 

65 # filter for settings of simulaiton 

66 if isinstance(obj, Setting): 

67 # provide name of the setting as attribute 

68 obj.name = name 

69 

70 

71class SettingsManager(dict): 

72 """Manages the different settings of a SimulationSettings instance. 

73 

74 The manager is needed to maintain the different attributes of a simulation 

75 (e.g. choices) while making the read and write access to the setting still 

76 easy. This way you can call sim_settings.<setting_name> and get the value 

77 directly while under sim_settings.manager.<setting_name> you can still find 

78 all information. 

79 

80 Args: 

81 bound_simulation_settings: instance of sim_settings this manager is 

82 bound to. E.g. BuildingSimSettings. 

83 """ 

84 

85 def __init__(self, bound_simulation_settings): 

86 super().__init__() 

87 self.bound_simulation_settings = bound_simulation_settings 

88 self._create_settings() 

89 

90 def _create_settings(self): 

91 """Add all listed settings from the simulation in its attributes.""" 

92 for name in self.names: 

93 setting = getattr(type(self.bound_simulation_settings), name) 

94 setting.initialize(self) 

95 

96 @property 

97 def names(self): 

98 """Returns a generator object with all settings that the 

99 bound_simulation_settings owns.""" 

100 return (name for name in dir(type(self.bound_simulation_settings)) 

101 if 

102 isinstance(getattr(type(self.bound_simulation_settings), name), 

103 Setting)) 

104 

105 

106class Setting: 

107 """Define specific settings regarding model creation and simulation. 

108 

109 Args: 

110 default: default value that will be applied when calling load_default() 

111 choices: dict of possible choice for this setting as key and a 

112 description per choice as value 

113 description: description of what the settings does as Str 

114 for_frontend: should this setting be shown in the frontend 

115 multiple_choice: allows multiple choice 

116 any_string: any string is allowed instead of a given choice 

117 mandatory: whether a setting needs to be set 

118 """ 

119 

120 def __init__( 

121 self, 

122 default=None, 

123 description: Union[str, None] = None, 

124 for_frontend: bool = False, 

125 any_string: bool = False, 

126 mandatory=False 

127 ): 

128 self.name = None # set by AutoSettingNameMeta 

129 self.default = default 

130 self.value = None 

131 self.description = description 

132 self.for_webapp = for_frontend 

133 self.any_string = any_string 

134 self.mandatory = mandatory 

135 self.manager = None 

136 

137 def initialize(self, manager): 

138 """Link between manager stored setting and direct setting of simulation 

139 """ 

140 if not self.name: 

141 raise AttributeError("Attribute.name not set!") 

142 self.check_setting_config() 

143 self.manager = manager 

144 self.manager[self.name] = self 

145 self.manager[self.name].value = None 

146 

147 def check_setting_config(self): 

148 """Checks if the setting is configured correctly""" 

149 return True 

150 

151 def load_default(self): 

152 if not self.value: 

153 self.value = self.default 

154 

155 def __get__(self, bound_simulation_settings, owner): 

156 """This is the get function that provides the value of the 

157 simulation setting when calling sim_settings.<setting_name>""" 

158 if bound_simulation_settings is None: 

159 return self 

160 

161 return self._inner_get(bound_simulation_settings) 

162 

163 def _inner_get(self, bound_simulation_settings): 

164 """Gets the value for the setting from the manager.""" 

165 return bound_simulation_settings.manager[self.name].value 

166 

167 def _inner_set(self, bound_simulation_settings, value): 

168 """Sets the value for the setting inside the manager.""" 

169 bound_simulation_settings.manager[self.name].value = value 

170 

171 def check_value(self, bound_simulation_settings, value): 

172 """Checks the value that should be set for correctness 

173 

174 Args: 

175 bound_simulation_settings: the sim setting belonging to the value 

176 value: value that should be checked for correctness 

177 Returns: 

178 True: if check was successful 

179 Raises: 

180 ValueError: if check was not successful 

181 """ 

182 return True 

183 

184 def __set__(self, bound_simulation_settings, value): 

185 """This is the set function that sets the value in the simulation 

186 setting when calling sim_settings.<setting_name> = <value>""" 

187 if self.check_value(bound_simulation_settings, value): 

188 self._inner_set(bound_simulation_settings, value) 

189 

190 

191class NumberSetting(Setting): 

192 def __init__( 

193 self, 

194 default=None, 

195 description: Union[str, None] = None, 

196 for_frontend: bool = False, 

197 any_string: bool = False, 

198 min_value: float = None, 

199 max_value: float = None 

200 ): 

201 super().__init__(default, description, for_frontend, any_string) 

202 self.min_value = min_value 

203 self.max_value = max_value 

204 

205 def check_setting_config(self): 

206 """Make sure min and max values are reasonable""" 

207 if not self.min_value: 

208 self.min_value = sys.float_info.epsilon 

209 logger.info(f'No min_value given for sim_setting {self}, assuming' 

210 f'smallest float epsilon.') 

211 if not self.max_value: 

212 self.max_value = float('inf') 

213 logger.info(f'No max_value given for sim_setting {self}, assuming' 

214 f'biggest float inf.') 

215 if self.default: 

216 if self.default > self.max_value or self.default < self.min_value: 

217 raise AttributeError( 

218 f"The specified limits for min_value, max_value and" 

219 f"default are contradictory min: {self.min_value} " 

220 f"max: {self.max_value}") 

221 if self.min_value > self.max_value: 

222 raise AttributeError( 

223 f"The specified limits for min_value and max_value are " 

224 f"contradictory min: {self.min_value} max: {self.max_value}") 

225 else: 

226 return True 

227 

228 def check_value(self, bound_simulation_settings, value): 

229 """Checks the value that should be set for correctness 

230 

231 Checks if value is in limits. 

232 Args: 

233 bound_simulation_settings: the sim setting belonging to the value 

234 value: value that should be checked for correctness 

235 Returns: 

236 True: if check was successful 

237 Raises: 

238 ValueError: if check was not successful 

239 """ 

240 # None is allowed for settings that should not be used at all but have 

241 # number values if used 

242 if value is None: 

243 return True 

244 if not isinstance(value, (float, int)): 

245 raise ValueError("The provided value is not a number.") 

246 if self.min_value <= value <= self.max_value: 

247 return True 

248 else: 

249 raise ValueError( 

250 f"The provided value is not inside the limits: min: " 

251 f"{self.min_value}, max: {self.max_value}, value: {value}") 

252 

253 

254class ChoiceSetting(Setting): 

255 def __init__( 

256 self, 

257 default=None, 

258 description: Union[str, None] = None, 

259 for_frontend: bool = False, 

260 any_string: bool = False, 

261 choices: dict = None, 

262 multiple_choice: bool = False 

263 ): 

264 super().__init__(default, description, for_frontend, any_string) 

265 self.choices = choices 

266 self.multiple_choice = multiple_choice 

267 

268 def check_setting_config(self): 

269 """make sure str choices don't hold '.' as this is seperator for enums. 

270 """ 

271 for choice in self.choices: 

272 if isinstance(choice, str) and '.' in choice: 

273 if '.' in choice: 

274 raise AttributeError( 

275 f"Provided setting {choice} has a choice with " 

276 f"character" 

277 f" '.', this is prohibited.") 

278 return True 

279 

280 def check_value(self, bound_simulation_settings, value): 

281 """Checks the value that should be set for correctness 

282 

283 Checks if the selected value is in choices. 

284 Args: 

285 bound_simulation_settings: the sim setting belonging to the value 

286 value: value that should be checked for correctness 

287 Returns: 

288 True: if check was successful 

289 Raises: 

290 ValueError: if check was not successful 

291 """ 

292 choices = bound_simulation_settings.manager[self.name].choices 

293 if isinstance(value, list): 

294 if not self.multiple_choice: 

295 raise ValueError(f'Only one choice is allowed for setting' 

296 f' {self.name}, but {len(value)} choices ' 

297 f'are given.') 

298 for val in value: 

299 self.check_value(bound_simulation_settings, val) 

300 return True 

301 else: 

302 if self.any_string and not isinstance(value, str): 

303 raise ValueError(f'{value} is no valid value for setting ' 

304 f'{self.name}, please enter a string.') 

305 elif value not in choices and not self.any_string: 

306 raise ValueError(f'{value} is no valid value for setting ' 

307 f'{self.name}, select one of {choices}.') 

308 else: 

309 return True 

310 

311 

312class PathSetting(Setting): 

313 def check_value(self, bound_simulation_settings, value): 

314 """Checks the value that should be set for correctness 

315 

316 Checks if the value is a valid path 

317 Args: 

318 bound_simulation_settings: the sim setting belonging to the value 

319 value: value that should be checked for correctness 

320 Returns: 

321 True: if check was successful 

322 Raises: 

323 ValueError: if check was not successful 

324 """ 

325 # check for existence 

326 # TODO #556 Do not check default path for existence because this might 

327 # not exist on system. This is a hack and should be solved when 

328 # improving communication between config and settings 

329 if not value == self.default: 

330 if not value.exists(): 

331 raise FileNotFoundError( 

332 f"The path provided for '{self.name}' does not exist." 

333 f" Please check the provided setting path which is: " 

334 f"{str(value)}") 

335 return True 

336 

337 def __set__(self, bound_simulation_settings, value): 

338 """This is the set function that sets the value in the simulation 

339 setting 

340 when calling sim_settings.<setting_name> = <value>""" 

341 if not isinstance(value, Path): 

342 if value is not None: 

343 try: 

344 value = Path(value) 

345 except TypeError: 

346 raise TypeError( 

347 f"Could not convert the simulation setting for " 

348 f"{self.name} into a path, please check the path.") 

349 # if default value is None this is ok 

350 elif value == self.default: 

351 pass 

352 else: 

353 raise ValueError(f"No Path provided for setting {self.name}.") 

354 if self.check_value(bound_simulation_settings, value): 

355 self._inner_set(bound_simulation_settings, value) 

356 

357 

358class BooleanSetting(Setting): 

359 def check_value(self, bound_simulation_settings, value): 

360 if not isinstance(value, bool) and value is not None: 

361 raise ValueError(f"The provided value {value} for sim_setting " 

362 f"{self.name} is not a Boolean") 

363 else: 

364 return True 

365 

366 

367class GuidListSetting(Setting): 

368 """Define a setting that accepts a list of IFC GUIDs. 

369 

370 This setting type is used for storing collections of IFC GUIDs, 

371 where each GUID is validated to ensure it follows IFC GUID format. 

372 

373 Args: 

374 default: default list of GUIDs (if any) that will be applied when calling load_default() 

375 description: description of what the settings does as Str 

376 for_frontend: should this setting be shown in the frontend 

377 mandatory: whether a setting needs to be set 

378 """ 

379 

380 def __init__( 

381 self, 

382 default=None, 

383 description: Union[str, None] = None, 

384 for_frontend: bool = False, 

385 mandatory=False 

386 ): 

387 super().__init__(default, description, for_frontend, False, mandatory) 

388 # Initialize with empty list if default is None 

389 if default is None: 

390 self.default = [] 

391 

392 def check_value(self, bound_simulation_settings, value): 

393 """Checks if each GUID in the list is valid according to IFC standards. 

394 

395 Args: 

396 bound_simulation_settings: the sim setting belonging to the value 

397 value: list of GUIDs that should be checked for correctness 

398 

399 Returns: 

400 True: if all GUIDs in the list are valid 

401 

402 Raises: 

403 ValueError: if any GUID in the list is invalid or value is not a list 

404 """ 

405 # None is allowed for optional settings 

406 if value is None: 

407 return True 

408 

409 # Check if value is a list 

410 if not isinstance(value, list): 

411 raise ValueError( 

412 f"The value for {self.name} must be a list of GUIDs, but got {type(value).__name__}") 

413 

414 # Empty list is valid 

415 if len(value) == 0: 

416 return True 

417 

418 # Check each GUID in the list 

419 for i, guid in enumerate(value): 

420 if not isinstance(guid, str): 

421 raise ValueError( 

422 f"GUID at index {i} must be a string, but got {type(guid).__name__}") 

423 

424 # Use the existing check_guid function to validate the GUID format 

425 if not check_guid(guid): 

426 raise ValueError( 

427 f"Invalid IFC GUID format at index {i}: '{guid}'") 

428 

429 return True 

430 

431 

432class BaseSimSettings(metaclass=AutoSettingNameMeta): 

433 """Specification of basic bim2sim simulation settings which are common for 

434 all simulations""" 

435 

436 def __init__(self, 

437 filters: list = None): 

438 self.manager = SettingsManager(bound_simulation_settings=self) 

439 

440 self.relevant_elements = {} 

441 self.simulated = False 

442 self.load_default_settings() 

443 

444 def load_default_settings(self): 

445 """loads default values for all settings""" 

446 for setting in self.manager.values(): 

447 setting.load_default() 

448 

449 def update_from_config(self, config): 

450 """Updates the simulation settings specification from the config 

451 file""" 

452 n_loaded_settings = 0 

453 for cat, settings in config.items(): 

454 # don't load settings which are not simulation relevant 

455 if cat.lower() not in [ 

456 self.__class__.__name__.lower(), 

457 'Generic Simulation Settings' 

458 ]: 

459 continue 

460 cat_from_cfg = config[cat] 

461 for setting in settings: 

462 if not hasattr(self, setting): 

463 raise AttributeError( 

464 f'{setting} is no allowed setting for ' 

465 f'simulation {self.__class__.__name__} ') 

466 else: 

467 set_from_cfg = cat_from_cfg.get(setting) 

468 if set_from_cfg is None: 

469 continue 

470 elif isinstance(set_from_cfg, str): 

471 # convert to readable python object 

472 try: 

473 # todo ast.literal_eval is safer but not safe. 

474 set_from_cfg = ast.literal_eval(str(set_from_cfg)) 

475 except (ValueError, SyntaxError): 

476 logger.warning(f'Failed literal evaluation of ' 

477 f'{set_from_cfg}. Proceeding.') 

478 if isinstance(set_from_cfg, str): 

479 # handle all strings that are file paths, before 

480 # handling Enums 

481 if os.path.isfile(set_from_cfg): 

482 val = set_from_cfg 

483 # handle Enums (will not be found by literal_eval) 

484 elif isinstance(set_from_cfg, str) and \ 

485 '.' in set_from_cfg: 

486 enum_type, enum_val = set_from_cfg.split('.') 

487 # convert str to enum 

488 try: 

489 enum_type = getattr(types, enum_type) 

490 val = getattr(enum_type, enum_val) 

491 except AttributeError: 

492 raise AttributeError( 

493 f" Tried to create the enumeration " 

494 f"{enum_type} but it doesn't exist.") 

495 else: 

496 # handle all other strings 

497 val = set_from_cfg 

498 else: 

499 # handle all other data types 

500 val = set_from_cfg 

501 setattr(self, setting, val) 

502 n_loaded_settings += 1 

503 else: 

504 raise TypeError( 

505 f'Config entry for {setting} is no string. ' 

506 f'Please use strings only in config.') 

507 logger.info(f'Loaded {n_loaded_settings} settings from config file.') 

508 

509 def check_mandatory(self): 

510 """Check if mandatory settings have a value.""" 

511 for setting in self.manager.values(): 

512 if setting.mandatory: 

513 if not setting.value: 

514 raise ValueError( 

515 f"Attempted to run project. Simulation setting " 

516 f"{setting.name} is not specified, " 

517 f"but is marked as mandatory. Please configure " 

518 f"{setting.name} before running your project.") 

519 

520 dymola_simulation = BooleanSetting( 

521 default=False, 

522 description='Run a Simulation with Dymola after model export?', 

523 for_frontend=True 

524 ) 

525 create_external_elements = BooleanSetting( 

526 default=False, 

527 description='Create external elements?', 

528 for_frontend=True 

529 ) 

530 max_wall_thickness = NumberSetting( 

531 default=0.3, 

532 max_value=0.60, 

533 min_value=1e-3, 

534 description='Choose maximum wall thickness as a tolerance for mapping ' 

535 'opening boundaries to their base surface (Wall). ' 

536 'Choose 0.3m as a default value.', 

537 for_frontend=True 

538 ) 

539 

540 group_unidentified = ChoiceSetting( 

541 default='fuzzy', 

542 choices={ 

543 'fuzzy': 'Use fuzzy search to find ifc name similarities', 

544 'name': 'Only group elements with exact same ifc name', 

545 'name_and_description': 'Only group elements with the same ifc' 

546 ' name and ifc description' 

547 }, 

548 description='To reduce the number of decisions by user to identify ' 

549 'elements which can not be identified automatically by ' 

550 'the ' 

551 'system, you can either use simple grouping by same name ' 

552 'of' 

553 ' IFC element or fuzzy search to group based on' 

554 ' similarities in name.', 

555 for_frontend=True 

556 ) 

557 fuzzy_threshold = NumberSetting( 

558 default=0.7, 

559 min_value=0.5, 

560 max_value=0.9, 

561 description='If you want to use fuzzy search in the ' 

562 'group_unidentified ' 

563 'setting, you can set the threshold here. A low ' 

564 'threshold means' 

565 ' a small similarity is required for grouping. A too low ' 

566 'value ' 

567 'might result in grouping elements which do not represent ' 

568 'the same IFC type.' 

569 ) 

570 

571 reset_guids = BooleanSetting( 

572 default=False, 

573 description='Reset GlobalIDs from imported IFC if duplicate ' 

574 'GlobalIDs occur in the IFC. As EnergyPlus evaluates all' 

575 'GlobalIDs upper case only, this might also be ' 

576 'applicable if duplicate non-case-sensitive GlobalIDs ' 

577 'occur.', 

578 for_frontend=True 

579 ) 

580 

581 weather_file_path = PathSetting( 

582 default=None, 

583 description='Path to the weather file that should be used for the ' 

584 'simulation. If no path is provided, we will try to get ' 

585 'the' 

586 'location from the IFC and download a fitting weather' 

587 ' file. For Modelica provide .mos files, for EnergyPlus ' 

588 '.epw files. If the format does not fit, we will try to ' 

589 'convert.', 

590 for_frontend=True, 

591 mandatory=True 

592 ) 

593 

594 building_rotation_overwrite = NumberSetting( 

595 default=0, 

596 description='Overwrite the (clockwise) building rotation angle in ' 

597 'degrees.', 

598 min_value=0, 

599 max_value=359, 

600 for_frontend=True 

601 ) 

602 

603 add_space_boundaries = BooleanSetting( 

604 default=False, 

605 description='Add space boundaries. Only required for building ' 

606 'performance simulation and co-simulations.', 

607 for_frontend=True 

608 ) 

609 correct_space_boundaries = BooleanSetting( 

610 default=False, 

611 description='Apply geometric correction to space boundaries.', 

612 for_frontend=True 

613 ) 

614 close_space_boundary_gaps = BooleanSetting( 

615 default=False, 

616 description='Close gaps in the set of space boundaries by adding ' 

617 'additional 2b space boundaries.', 

618 for_frontend=True 

619 ) 

620 stories_to_load_guids = GuidListSetting( 

621 default=[], 

622 description='List of IFC GUIDs for the specific stories that should ' 

623 'be loaded. If empty, all stories will be considered ' 

624 'for loading. This setting is useful for large buildings ' 

625 'to reduce computational time. Note that loading single ' 

626 'storeys may lead to missing ceilings if the related ' 

627 'slab is assigned to the storey above, which may require ' 

628 'corrections to boundary conditions.' 

629 ' It is recommended to include GUIDs of neighboring' 

630 ' storeys to reduce boundary condition errors.', 

631 for_frontend=True, 

632 mandatory=False 

633 ) 

634 

635 

636class PlantSimSettings(BaseSimSettings): 

637 def __init__(self): 

638 super().__init__( 

639 ) 

640 self.relevant_elements = {*hvac_elements.items, Material} 

641 

642 # Todo maybe make every aggregation its own setting with LOD in the future, 

643 # but currently we have no usage for this afaik. 

644 aggregations = ChoiceSetting( 

645 default=[ 

646 'UnderfloorHeating', 

647 'PipeStrand', 

648 'Consumer', 

649 'ParallelPump', 

650 'ConsumerHeatingDistributorModule', 

651 'GeneratorOneFluid', 

652 ], 

653 choices={ 

654 'UnderfloorHeating': 'Aggregate underfloor heating circuits', 

655 'Consumer': 'Aggregate consumers', 

656 'PipeStrand': 'Aggregate strands of pipes', 

657 'ParallelPump': 'Aggregate parallel pumps', 

658 'ConsumerHeatingDistributorModule': 'Aggregate consumer and ' 

659 'distributor to one module', 

660 'GeneratorOneFluid': 'Aggregate the generator and its circuit to ' 

661 'one module', 

662 }, 

663 description="Which aggregations should be applied on the hydraulic " 

664 "network", 

665 multiple_choice=True, 

666 for_frontend=True 

667 ) 

668 

669 tolerance_connect_by_position = NumberSetting( 

670 default=10, 

671 description="Tolerance for distance for which ports should be " 

672 "connected. Based on there position in IFC.", 

673 for_frontend=True, 

674 min_value=1 

675 ) 

676 

677 verify_connection_by_position = BooleanSetting( 

678 description="Choose if connection of elements via IfcDistributionPorts" 

679 " should be validated by the geometric position of the " 

680 "ports.", 

681 default=True 

682 ) 

683 

684 

685class BuildingSimSettings(BaseSimSettings): 

686 

687 def __init__(self): 

688 super().__init__() 

689 self.relevant_elements = {*bps_elements.items, 

690 Material} 

691 

692 layers_and_materials = ChoiceSetting( 

693 default=LOD.low, 

694 choices={ 

695 LOD.low: 'Override materials with predefined setups', 

696 # LOD.full: 'Get all information from IFC and enrich if needed' 

697 }, 

698 description='Select how existing Material information in IFC should ' 

699 'be treated.', 

700 for_frontend=True 

701 ) 

702 year_of_construction_overwrite = NumberSetting( 

703 default=None, 

704 min_value=0, 

705 max_value=2015, 

706 description="Force an overwrite of the year of construction as a " 

707 "base for the selected construction set.", 

708 for_frontend=True, 

709 ) 

710 construction_class_walls = ChoiceSetting( 

711 default='iwu_heavy', 

712 choices={ 

713 'iwu_heavy': 'Wall structures according to iwu heavy standard', 

714 'iwu_light': 'Wall structures according to iwu light standard', 

715 'kfw_40': 'Wall structures according to kfw 40 standard', 

716 'kfw_55': 'Wall structures according to kfw 55 standard', 

717 'kfw_70': 'Wall structures according to kfw 70 standard', 

718 'kfw_85': 'Wall structures according to kfw 85 standard', 

719 'kfw_100': 'Wall structures according to kfw 100 standard', 

720 'tabula_de_standard_1_SFH': 'Wall structures according to german ' 

721 'tabula standard 1 for single family ' 

722 'houses', 

723 'tabula_de_standard_2_SFH': 'Wall structures according to german ' 

724 'tabula standard 2 for single family ' 

725 'houses', 

726 'tabula_de_retrofit_1_SFH': 'Wall structures according to german ' 

727 'tabula retrofit 1 for single family ' 

728 'houses', 

729 'tabula_de_retrofit_2_SFH': 'Wall structures according to german ' 

730 'tabula retrofit 2 for single family ' 

731 'houses', 

732 'tabula_de_adv_retrofit_1_SFH': 'Wall structures according to ' 

733 'german tabula advanced retrofit ' 

734 '1 for single ' 

735 'family houses', 

736 'tabula_de_adv_retrofit_2_SFH': 'Wall structures according to ' 

737 'german tabula advanced retrofit ' 

738 '2 for ' 

739 'single family houses', 

740 'tabula_de_standard_1_TH': 'Wall structures according to german ' 

741 'tabula standard 1 for terraced houses', 

742 'tabula_de_standard_2_TH': 'Wall structures according to german ' 

743 'tabula standard 2 for terraced houses', 

744 'tabula_de_retrofit_1_TH': 'Wall structures according to german ' 

745 'tabula retrofit 1 for terraced houses', 

746 'tabula_de_retrofit_2_TH': 'Wall structures according to german ' 

747 'tabula retrofit 2 for terraced houses', 

748 'tabula_de_standard_1_MFH': 'Wall structures according to german ' 

749 'tabula standard 1 for multi family ' 

750 'houses', 

751 'tabula_de_retrofit_1_MFH': 'Wall structures according to german ' 

752 'tabula retrofit 1 for multi family ' 

753 'houses', 

754 'tabula_de_adv_retrofit_1_MFH': 'Wall structures according to ' 

755 'german tabula advanced retrofit ' 

756 '1 for multi ' 

757 'family houses', 

758 'tabula_de_standard_1_AB': 'Wall structures according to german ' 

759 'tabula standard 1 for apartment ' 

760 'blocks', 

761 'tabula_de_adv_retrofit_1_AB': 'Wall structures according to ' 

762 'german tabula advanced retrofit ' 

763 '1 for ' 

764 'apartment blocks', 

765 'tabula_de_standard': 'Wall structures according to german ' 

766 'tabula standard', 

767 'tabula_dk_standard_1_SFH': 'Wall structures according to danish ' 

768 'tabula standard 1 for single family ' 

769 'houses', 

770 'tabula_dk_standard_2_SFH': 'Wall structures according to danish ' 

771 'tabula standard 2 for single family ' 

772 'houses', 

773 'tabula_dk_retrofit_1_SFH': 'Wall structures according to danish ' 

774 'tabula retrofit 1 for single family ' 

775 'houses', 

776 'tabula_dk_retrofit_2_SFH': 'Wall structures according to danish ' 

777 'tabula retrofit 2 for single family ' 

778 'houses', 

779 'tabula_dk_adv_retrofit_1_SFH': 'Wall structures according to ' 

780 'danish tabula advanced retrofit ' 

781 '1 for single ' 

782 'family houses', 

783 'tabula_dk_adv_retrofit_2_SFH': 'Wall structures according to ' 

784 'danish tabula advanced retrofit ' 

785 '2 for single ' 

786 'family houses', 

787 'tabula_dk_standard_1_TH': 'Wall structures according to danish ' 

788 'tabula standard 1 for terraced houses', 

789 'tabula_dk_standard_2_TH': 'Wall structures according to danish ' 

790 'tabula standard 2 for terraced houses', 

791 'tabula_dk_retrofit_1_TH': 'Wall structures according to danish ' 

792 'tabula retrofit 1 for terraced houses', 

793 'tabula_dk_retrofit_2_TH': 'Wall structures according to danish ' 

794 'tabula retrofit 1 for terraced houses', 

795 'tabula_dk_adv_retrofit_1_TH': 'Wall structures according to ' 

796 'danish tabula advanced retrofit ' 

797 '1 for ' 

798 'terraced houses', 

799 'tabula_dk_adv_retrofit_2_TH': 'Wall structures according to ' 

800 'danish tabula advanced retrofit ' 

801 '1 for ' 

802 'terraced houses', 

803 'tabula_dk_standard_1_AB': 'Wall structures according to danish ' 

804 'tabula standard 1 for apartment ' 

805 'blocks', 

806 'tabula_dk_standard_2_AB': 'Wall structures according to danish ' 

807 'tabula standard 2 for apartment ' 

808 'blocks', 

809 'tabula_dk_retrofit_1_AB': 'Wall structures according to danish ' 

810 'tabula retrofit 1 for apartment ' 

811 'blocks', 

812 'tabula_dk_retrofit_2_AB': 'Wall structures according to danish ' 

813 'tabula retrofit 2 for apartment ' 

814 'blocks', 

815 'tabula_dk_adv_retrofit_1_AB': 'Wall structures according to ' 

816 'danish tabula advanced retrofit ' 

817 '1 for ' 

818 'apartment blocks', 

819 'tabula_dk_adv_retrofit_2_AB': 'Wall structures according to ' 

820 'danish tabula advanced retrofit ' 

821 '2 for ' 

822 'apartment blocks', 

823 'tabula_dk_standard': 'Wall structures according to danish ' 

824 'tabula standard' 

825 }, 

826 description="Select the most fitting construction class type for" 

827 "the walls of the selected building. For all settings but " 

828 "kfw_* the year of construction is required.", 

829 for_frontend=True 

830 ) 

831 construction_class_windows = ChoiceSetting( 

832 default='Alu- oder Stahlfenster, Waermeschutzverglasung, zweifach', 

833 choices={ 

834 'Holzfenster, zweifach': 

835 'Zeifachverglasung mit Holzfenstern', 

836 'Kunststofffenster, Isolierverglasung': 

837 'Isolierverglasung mit Kunststofffensern', 

838 'Alu- oder Stahlfenster, Isolierverglasung': 

839 'Isolierverglasung mit Alu- oder Stahlfenstern', 

840 'Alu- oder Stahlfenster, Waermeschutzverglasung, zweifach': 

841 'Wärmeschutzverglasung (zweifach) mit Alu- oder ' 

842 'Stahlfenstern', 

843 'Waermeschutzverglasung, dreifach': 

844 'Wärmeschutzverglasung (dreifach)', 

845 'tabula_de_standard_1_SFH': 'Windows according to german tabula ' 

846 'standard 1 for single family ' 

847 'houses', 

848 'tabula_de_standard_2_SFH': 'Windows according to german tabula ' 

849 'standard 2 for single family ' 

850 'houses', 

851 'tabula_de_retrofit_1_SFH': 'Windows according to german tabula ' 

852 'retrofit 1 for single family ' 

853 'houses', 

854 'tabula_de_retrofit_2_SFH': 'Windows according to german tabula ' 

855 'retrofit 2 for single family ' 

856 'houses', 

857 'tabula_de_adv_retrofit_1_SFH': 'Windows according to german ' 

858 'tabula advanced retrofit 1 for ' 

859 'single ' 

860 'family houses', 

861 'tabula_de_adv_retrofit_2_SFH': 'Windows according to german ' 

862 'tabula advanced retrofit 2 for ' 

863 'single family houses', 

864 'tabula_de_standard_1_TH': 'Windows according to german tabula ' 

865 'standard 1 for terraced houses', 

866 'tabula_de_standard_2_TH': 'Windows according to german tabula ' 

867 'standard 2 for terraced houses', 

868 'tabula_de_retrofit_1_TH': 'Windows according to german tabula ' 

869 'retrofit 1 for terraced houses', 

870 'tabula_de_retrofit_2_TH': 'Windows according to german tabula ' 

871 'retrofit 2 for terraced houses', 

872 'tabula_de_standard_1_MFH': 'Windows according to german tabula ' 

873 'standard 1 for multi family houses', 

874 'tabula_de_retrofit_1_MFH': 'Windows according to german tabula ' 

875 'retrofit 1 for multi family houses', 

876 'tabula_de_adv_retrofit_1_MFH': 'Windows according to german ' 

877 'tabula advanced retrofit 1 for ' 

878 'multi ' 

879 'family houses', 

880 'tabula_de_standard_1_AB': 'Windows according to german tabula ' 

881 'standard 1 for apartment blocks', 

882 'tabula_de_adv_retrofit_1_AB': 'Windows according to german ' 

883 'tabula advanced retrofit 1 for ' 

884 'apartment blocks', 

885 'tabula_de_standard': 'Windows according to german tabula ' 

886 'standard', 

887 'tabula_dk_standard_1_SFH': 'Windows according to danish tabula ' 

888 'standard 1 for single family ' 

889 'houses', 

890 'tabula_dk_standard_2_SFH': 'Windows according to danish tabula ' 

891 'standard 2 for single family ' 

892 'houses', 

893 'tabula_dk_retrofit_1_SFH': 'Windows according to danish tabula ' 

894 'retrofit 1 for single family ' 

895 'houses', 

896 'tabula_dk_retrofit_2_SFH': 'Windows according to danish tabula ' 

897 'retrofit 2 for single family ' 

898 'houses', 

899 'tabula_dk_adv_retrofit_1_SFH': 'Windows according to danish ' 

900 'tabula advanced retrofit 1 for ' 

901 'single ' 

902 'family houses', 

903 'tabula_dk_adv_retrofit_2_SFH': 'Windows according to danish ' 

904 'tabula advanced retrofit 2 for ' 

905 'single ' 

906 'family houses', 

907 'tabula_dk_standard_1_TH': 'Windows according to danish tabula ' 

908 'standard 1 for terraced houses', 

909 'tabula_dk_standard_2_TH': 'Windows according to danish tabula ' 

910 'standard 2 for terraced houses', 

911 'tabula_dk_retrofit_1_TH': 'Windows according to danish tabula ' 

912 'retrofit 1 for terraced houses', 

913 'tabula_dk_retrofit_2_TH': 'Windows according to danish tabula ' 

914 'retrofit 1 for terraced houses', 

915 'tabula_dk_adv_retrofit_1_TH': 'Windows according to danish ' 

916 'tabula advanced retrofit 1 for ' 

917 'terraced houses', 

918 'tabula_dk_adv_retrofit_2_TH': 'Windows according to danish ' 

919 'tabula advanced retrofit 1 for ' 

920 'terraced houses', 

921 'tabula_dk_standard_1_AB': 'Windows according to danish tabula ' 

922 'standard 1 for apartment blocks', 

923 'tabula_dk_standard_2_AB': 'Windows according to danish tabula ' 

924 'standard 2 for apartment blocks', 

925 'tabula_dk_retrofit_1_AB': 'Windows according to danish tabula ' 

926 'retrofit 1 for apartment blocks', 

927 'tabula_dk_retrofit_2_AB': 'Windows according to danish tabula ' 

928 'retrofit 2 for apartment blocks', 

929 'tabula_dk_adv_retrofit_1_AB': 'Windows according to danish ' 

930 'tabula advanced retrofit 1 for ' 

931 'apartment blocks', 

932 'tabula_dk_adv_retrofit_2_AB': 'Windows according to danish ' 

933 'tabula advanced retrofit 2 for ' 

934 'apartment blocks', 

935 'tabula_dk_standard': 'Windows according to danish tabula standard' 

936 }, 

937 description="Select the most fitting construction class type for" 

938 " the windows of the selected building.", 

939 ) 

940 construction_class_doors = ChoiceSetting( 

941 default='iwu_typical', 

942 choices={ 

943 'iwu_typical': 'Typical door data based', 

944 'kfw_40': 'Doors according to kfw 40 standard', 

945 'kfw_55': 'Doors according to kfw 55 standard', 

946 'kfw_70': 'Doors according to kfw 70 standard', 

947 'kfw_85': 'Doors according to kfw 85 standard', 

948 'kfw_100': 'Doors according to kfw 100 standard', 

949 'tabula_de_standard_1_SFH': 'Windows according to german tabula ' 

950 'standard 1 for single family ' 

951 'houses', 

952 'tabula_de_retrofit_1_SFH': 'Windows according to german tabula ' 

953 'retrofit 1 for single family ' 

954 'houses', 

955 'tabula_de_adv_retrofit_1_SFH': 'Windows according to german ' 

956 'tabula advanced retrofit 1 for ' 

957 'single ' 

958 'family houses', 

959 'tabula_de_standard_1_TH': 'Windows according to german tabula ' 

960 'standard 1 for terraced houses', 

961 'tabula_de_retrofit_1_TH': 'Windows according to german tabula ' 

962 'retrofit 1 for terraced houses', 

963 'tabula_de_adv_retrofit_1_TH': 'Windows according to german ' 

964 'tabula advanced retrofit 1 for ' 

965 'terraced houses', 

966 'tabula_de_standard_1_MFH': 'Windows according to german tabula ' 

967 'standard 1 for multi family houses', 

968 'tabula_de_retrofit_1_MFH': 'Windows according to german tabula ' 

969 'retrofit 1 for multi family houses', 

970 'tabula_de_adv_retrofit_1_MFH': 'Windows according to german ' 

971 'tabula advanced retrofit 1 for ' 

972 'multi ' 

973 'family houses', 

974 'tabula_de_standard_1_AB': 'Windows according to german tabula ' 

975 'standard 1 for apartment blocks', 

976 'tabula_de_retrofit_1_AB': 'Windows according to german tabula ' 

977 'retrofit 1 for apartment blocks', 

978 'tabula_de_adv_retrofit_1_AB': 'Windows according to german ' 

979 'tabula advanced retrofit 1 for ' 

980 'apartment blocks', 

981 'tabula_dk_standard_1_SFH': 'Windows according to danish tabula ' 

982 'standard 1 for single family ' 

983 'houses' 

984 }, 

985 description="Select the most fitting construction class type for" 

986 " the windows of the selected building.", 

987 ) 

988 heating_tz_overwrite = BooleanSetting( 

989 default=None, 

990 description='If True, all thermal zones will be provided with heating,' 

991 'if False no heating for thermal zones is provided, ' 

992 'regardless of information in the IFC or in the use ' 

993 'condition file.', 

994 for_frontend=True 

995 ) 

996 cooling_tz_overwrite = BooleanSetting( 

997 default=None, 

998 description='If True, all thermal zones will be provided with cooling,' 

999 'if False no cooling for thermal zones is provided, ' 

1000 'regardless of information in the IFC or in the use ' 

1001 'condition file.', 

1002 for_frontend=True 

1003 ) 

1004 ahu_tz_overwrite = BooleanSetting( 

1005 default=None, 

1006 description='If True, all thermal zones will be provided with AHU,' 

1007 'if False no AHU for thermal zones is provided, ' 

1008 'regardless of information in the IFC or in the use ' 

1009 'condition file.', 

1010 for_frontend=True 

1011 ) 

1012 prj_use_conditions = PathSetting( 

1013 default=None, 

1014 description="Path to a custom UseConditions.json for the specific " 

1015 "project, that holds custom usage conditions for this " 

1016 "project. If this is used, this use_conditions file have " 

1017 "to hold all information. The basic UseConditions.json " 

1018 "file is ignored in this case.", 

1019 for_frontend=True 

1020 ) 

1021 prj_custom_usages = PathSetting( 

1022 default=None, 

1023 description="Path to a custom customUsages.json for the specific " 

1024 "project, that holds mappings between space names from " 

1025 "IFC " 

1026 "and usage conditions from UseConditions.json.", 

1027 for_frontend=True 

1028 ) 

1029 setpoints_from_template = BooleanSetting( 

1030 default=False, 

1031 description="Use template heating and cooling profiles instead of " 

1032 "setpoints from IFC. Defaults to False, i.e., " 

1033 "use original data source. Set to True, " 

1034 "if template-based values should be used instead.", 

1035 for_frontend=True 

1036 ) 

1037 use_maintained_illuminance = BooleanSetting( 

1038 default=True, 

1039 description="Use maintained illuminance required per zone based on " 

1040 "DIN V EN 18599 information to calculate internal loads" 

1041 "through lighting.", 

1042 for_frontend=True 

1043 ) 

1044 sim_results = ChoiceSetting( 

1045 default=[ 

1046 "heat_demand_total", "cool_demand_total", 

1047 "heat_demand_rooms", "cool_demand_rooms", 

1048 "heat_energy_total", "cool_energy_total", 

1049 "heat_energy_rooms", "cool_energy_rooms", 

1050 "air_temp_out", "operative_temp_rooms", "air_temp_rooms", 

1051 "internal_gains_machines_rooms", "internal_gains_persons_rooms", 

1052 "internal_gains_lights_rooms", "n_persons_rooms", 

1053 "infiltration_rooms", "mech_ventilation_rooms", 

1054 "heat_set_rooms", "cool_set_rooms" 

1055 

1056 ], 

1057 choices={ 

1058 "heat_demand_total": 

1059 "Total heating demand (power) as time series data", 

1060 "cool_demand_total": 

1061 "Total cooling demand (power) as time series data", 

1062 "heat_demand_rooms": 

1063 "Zone based heating demand (power) as time series data", 

1064 "cool_demand_rooms": 

1065 "Zone based cooling demand (power) as time series data", 

1066 "heat_energy_total": 

1067 "Total heating energy as time series data", 

1068 "cool_energy_total": 

1069 "Total cooling energy as time series data", 

1070 "heat_energy_rooms": 

1071 "Zone based heating energy as time series data", 

1072 "cool_energy_rooms": 

1073 "Zone cooling heating energy as time series data", 

1074 "air_temp_out": 

1075 "Outdoor air temperature as time series data", 

1076 "operative_temp_rooms": 

1077 "Zone based operative temperature as time series data", 

1078 "air_temp_rooms": 

1079 "Zone based indoor air temperature as time series data", 

1080 "internal_gains_machines_rooms": 

1081 "Internal gains through machines in W as time series data", 

1082 "internal_gains_persons_rooms": 

1083 "Internal gains through persons in W as time series data", 

1084 "internal_gains_lights_rooms": 

1085 "Internal gains through lights in W as time series data", 

1086 "n_persons_rooms": 

1087 "Total amount of occupying persons as time series data", 

1088 "infiltration_rooms": 

1089 "Infiltration into room in 1/h as time series data", 

1090 "mech_ventilation_rooms": 

1091 "Mechanical ventilation flow in m³/h as time series data", 

1092 "heat_set_rooms": 

1093 "Heating set point in °C time series data", 

1094 "cool_set_rooms": 

1095 "Cooling set point in °C time series data", 

1096 }, 

1097 multiple_choice=True, 

1098 ) 

1099 add_space_boundaries = BooleanSetting( 

1100 default=True, 

1101 description='Add space boundaries. Only required for building ' 

1102 'performance simulation and co-simulations.', 

1103 for_frontend=True 

1104 ) 

1105 correct_space_boundaries = BooleanSetting( 

1106 default=False, 

1107 description='Apply geometric correction to space boundaries.', 

1108 for_frontend=True 

1109 ) 

1110 split_bounds = BooleanSetting( 

1111 default=False, 

1112 description='Whether to convert up non-convex space boundaries or ' 

1113 'not.', 

1114 for_frontend=True 

1115 ) 

1116 add_shadings = BooleanSetting( 

1117 default=False, 

1118 description='Whether to add shading surfaces if available or not.', 

1119 for_frontend=True 

1120 ) 

1121 split_shadings = BooleanSetting( 

1122 default=False, 

1123 description='Whether to convert up non-convex shading boundaries or ' 

1124 'not.', 

1125 for_frontend=True 

1126 ) 

1127 close_space_boundary_gaps = BooleanSetting( 

1128 default=False, 

1129 description='Close gaps in the set of space boundaries by adding ' 

1130 'additional 2b space boundaries.', 

1131 for_frontend=True 

1132 ) 

1133 create_plots = BooleanSetting( 

1134 default=False, 

1135 description='Create plots for simulation results after the simulation ' 

1136 'finished.', 

1137 for_frontend=True 

1138 ) 

1139 set_run_period = BooleanSetting( 

1140 default=False, 

1141 description="Choose whether run period for simulation execution " 

1142 "should be set manually instead of running annual " 

1143 "simulation." 

1144 ) 

1145 run_period_start_month = NumberSetting( 

1146 default=1, 

1147 min_value=1, 

1148 max_value=12, 

1149 description="Choose start month of run period. Requires " 

1150 "set_run_period==True for activation.", 

1151 for_frontend=True 

1152 ) 

1153 run_period_start_day = NumberSetting( 

1154 default=1, 

1155 min_value=1, 

1156 max_value=31, 

1157 description="Choose start day of run period. Requires " 

1158 "set_run_period==True for activation.", 

1159 for_frontend=True 

1160 ) 

1161 run_period_end_month = NumberSetting( 

1162 default=12, 

1163 min_value=1, 

1164 max_value=12, 

1165 description="Choose end month of run period. Requires " 

1166 "set_run_period==True for activation.", 

1167 for_frontend=True 

1168 ) 

1169 run_period_end_day = NumberSetting( 

1170 default=31, 

1171 min_value=1, 

1172 max_value=31, 

1173 description="Choose end day of run period. Requires " 

1174 "set_run_period==True for activation.", 

1175 for_frontend=True 

1176 ) 

1177 plot_singe_zone_guid = ChoiceSetting( 

1178 default='', 

1179 choices={'': "Skip"}, 

1180 description="Choose the GlobalId of the IfcSpace for which results " 

1181 "should be plotted.", 

1182 any_string=True 

1183 ) 

1184 ahu_heating_overwrite = BooleanSetting( 

1185 default=None, 

1186 description="Choose if the central AHU should provide heating. " 

1187 ) 

1188 ahu_cooling_overwrite = BooleanSetting( 

1189 default=None, 

1190 description="Choose if the central AHU should provide cooling." 

1191 ) 

1192 ahu_dehumidification_overwrite = BooleanSetting( 

1193 default=None, 

1194 description="Choose if the central AHU should provide " 

1195 "dehumidification." 

1196 ) 

1197 ahu_humidification_overwrite = BooleanSetting( 

1198 default=None, 

1199 description="Choose if the central AHU should provide humidification." 

1200 "otherwise this has no effect. " 

1201 ) 

1202 ahu_heat_recovery_overwrite = BooleanSetting( 

1203 default=None, 

1204 description="Choose if the central AHU should zuse heat recovery." 

1205 ) 

1206 ahu_heat_recovery_efficiency_overwrite = NumberSetting( 

1207 default=None, 

1208 min_value=0.5, 

1209 max_value=0.99, 

1210 description="Choose the heat recovery efficiency of the central AHU." 

1211 ) 

1212 use_constant_infiltration_overwrite = BooleanSetting( 

1213 default=None, 

1214 description="If only constant base infiltration should be used and no " 

1215 "dynamic ventilation through e.g. windows." 

1216 ) 

1217 base_infiltration_rate_overwrite = NumberSetting( 

1218 default=None, 

1219 min_value=0.001, 

1220 max_value=5, 

1221 description="Overwrite base value for the natural infiltration in 1/h " 

1222 " without window openings" 

1223 )