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

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""" 

5 

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 

21 

22 

23logger = logging.getLogger(__name__) 

24 

25 

26class AutoSettingNameMeta(type): 

27 """Sets the name to every SimulationSetting attribute based on its instance 

28 name. 

29 """ 

30 

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 

39 

40 

41class SettingsManager(dict): 

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

43 

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. 

49 

50 Args: 

51 bound_simulation_settings: instance of sim_settings this manager is 

52 bound to. E.g. BuildingSimSettings. 

53 """ 

54 defaults = {} 

55 

56 def __init__(self, bound_simulation_settings): 

57 super().__init__() 

58 self.bound_simulation_settings = bound_simulation_settings 

59 self._create_settings() 

60 

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 

67 

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 

71 

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) 

76 

77 for attribute_name in dir(bound_simulation_settings_class): 

78 attribute = getattr(bound_simulation_settings_class, attribute_name) 

79 

80 if isinstance(attribute, Setting): 

81 yield attribute_name 

82 

83 

84class Setting(BaseModel, validate_assignment=True, validate_default=True): 

85 """Base class for all simulation settings. 

86 

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)). 

89 

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. 

92 

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 """ 

100 

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) 

106 

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. 

110  

111 Example: 

112  

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 

122 

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 

127 

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

129 

130 

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 

135 

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.') 

142 

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.') 

147 

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}") 

152 

153 return self 

154 

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 

164 

165 

166class ChoiceSetting(Setting): 

167 value: Union[str, List[str], Enum, None] 

168 choices: dict 

169 multiple_choice: bool = False 

170 

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 ) 

178 

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 

188 

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) 

200 

201 return self 

202 

203 

204class PathSetting(Setting): 

205 value: Optional[Union[DirectoryPath, FilePath]] 

206 

207 

208class BooleanSetting(Setting): 

209 value: Optional[bool] 

210 

211 

212class GuidListSetting(Setting): 

213 value: Optional[List[str]] = None 

214 

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 

224 

225 

226class BaseSimSettings(metaclass=AutoSettingNameMeta): 

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

228 all simulations""" 

229 

230 def __init__(self, filters: list = None): 

231 self.manager = SettingsManager(bound_simulation_settings=self) 

232 

233 self.relevant_elements = {} 

234 self.simulated = False 

235 

236 def load_default_settings(self): 

237 """loads default values for all settings""" 

238 

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

240 default = self.manager.defaults[setting.name] 

241 setting.value = default 

242 

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.') 

302 

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.") 

313 

314 

315 dymola_simulation = BooleanSetting( 

316 value=False, 

317 description="Run a Simulation with Dymola after model export?", 

318 for_frontend=True, 

319 ) 

320 

321 create_external_elements = BooleanSetting( 

322 value=False, 

323 description='Create external elements?', 

324 for_frontend=True 

325 ) 

326 

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 ) 

336 

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 ) 

354 

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 ) 

372 

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 ) 

386 

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 ) 

396 

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 ) 

409 

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 ) 

418 

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 ) 

436 

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 ) 

451 

452 

453class PlantSimSettings(BaseSimSettings): 

454 def __init__(self): 

455 super().__init__( 

456 ) 

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

458 

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 ) 

485 

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 ) 

493 

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 ) 

500 

501 

502class BuildingSimSettings(BaseSimSettings): 

503 

504 def __init__(self): 

505 super().__init__() 

506 self.relevant_elements = {*bps_elements.items, 

507 Material} 

508 

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 ) 

648 

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" 

873 

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 )