Coverage for bim2sim/plugins/PluginAixLib/bim2sim_aixlib/models/__init__.py: 31%

222 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-03-12 17:09 +0000

1"""Package for Python representations of AixLib models""" 

2from bim2sim.elements.aggregation import hvac_aggregations 

3from bim2sim.export import modelica 

4from bim2sim.elements import hvac_elements as hvac 

5from bim2sim.elements.mapping.units import ureg 

6from bim2sim.export.modelica import check_numeric 

7 

8MEDIUM_WATER = 'AixLib.Media.Water' 

9 

10 

11class AixLib(modelica.ModelicaElement): 

12 library = "AixLib" 

13 

14 

15class Boiler(AixLib): 

16 # TODO: The model BoilerGeneric does not exist in AixLib 

17 path = "AixLib.Fluid.BoilerCHP.BoilerGeneric" 

18 represents = [hvac.Boiler] 

19 

20 def __init__(self, element): 

21 super().__init__(element) 

22 self._set_parameter(name='redeclare package Medium', 

23 unit=None, 

24 required=False, 

25 value=MEDIUM_WATER) 

26 self._set_parameter(name='dTWaterNom', 

27 unit=ureg.kelvin, 

28 required=True, 

29 attributes=['dT_water'], 

30 check=check_numeric(min_value=0 * ureg.kelvin)) 

31 self._set_parameter(name='TRetNom', 

32 unit=ureg.kelvin, 

33 required=True, 

34 attributes=['return_temperature'], 

35 check=check_numeric(min_value=0 * ureg.kelvin)) 

36 self._set_parameter(name='QNom', 

37 unit=ureg.watt, 

38 required=True, 

39 attributes=['rated_power'], 

40 check=check_numeric(min_value=0 * ureg.watt)) 

41 self._set_parameter(name='PLRMin', 

42 unit=ureg.dimensionless, 

43 required=True, 

44 attributes=['min_PLR'], 

45 check=check_numeric( 

46 min_value=0 * ureg.dimensionless)) 

47 

48 def get_port_name(self, port): 

49 if port.verbose_flow_direction == 'SINK': 

50 return 'port_a' 

51 if port.verbose_flow_direction == 'SOURCE': 

52 return 'port_b' 

53 else: 

54 return super().get_port_name(port) # ToDo: Gas connection 

55 

56 

57class Radiator(AixLib): 

58 path = "AixLib.Fluid.HeatExchangers.Radiators.RadiatorEN442_2" 

59 represents = [hvac.SpaceHeater] 

60 

61 def __init__(self, element): 

62 super().__init__(element) 

63 self._set_parameter(name='redeclare package Medium', 

64 unit=None, 

65 required=False, 

66 value=MEDIUM_WATER) 

67 self._set_parameter(name='Q_flow_nominal', 

68 unit=ureg.watt, 

69 required=True, 

70 attributes=['rated_power'], 

71 check=check_numeric(min_value=0 * ureg.watt)) 

72 self._set_parameter(name='T_a_nominal', 

73 unit=ureg.celsius, 

74 required=True, 

75 check=check_numeric(min_value=0 * ureg.celsius), 

76 attributes=['flow_temperature']) 

77 self._set_parameter(name='T_b_nominal', 

78 unit=ureg.celsius, 

79 required=True, 

80 check=check_numeric(min_value=0 * ureg.celsius), 

81 attributes=['return_temperature']) 

82 

83 def get_port_name(self, port): 

84 if port.verbose_flow_direction == 'SINK': 

85 return 'port_a' 

86 if port.verbose_flow_direction == 'SOURCE': 

87 return 'port_b' 

88 else: 

89 return super().get_port_name(port) 

90 

91 

92class Pump(AixLib): 

93 path = "AixLib.Fluid.Movers.SpeedControlled_y" 

94 represents = [hvac.Pump] 

95 

96 def __init__(self, element): 

97 super().__init__(element) 

98 self._set_parameter(name='redeclare package Medium', 

99 unit=None, 

100 required=False, 

101 value=MEDIUM_WATER) 

102 self._set_parameter(name='V_flow', 

103 unit=ureg.m ** 3 / ureg.s, 

104 required=True, 

105 export=False, 

106 function=lambda rated_volume_flow: 

107 [0 * rated_volume_flow, 

108 1 * rated_volume_flow, 

109 2 * rated_volume_flow]) 

110 self._set_parameter(name='dp', 

111 unit=ureg.pascal, 

112 required=True, 

113 export=False, 

114 function=lambda rated_pressure_difference: 

115 [2 * rated_pressure_difference, 

116 1 * rated_pressure_difference, 

117 0 * rated_pressure_difference]) 

118 self._set_parameter(name='per', 

119 unit=None, 

120 required=False, 

121 value={'pressure': { 

122 'V_flow': self.parameters['V_flow'], 

123 'dp': self.parameters['dp']}}) 

124 

125 def get_port_name(self, port): 

126 if port.verbose_flow_direction == 'SINK': 

127 return 'port_a' 

128 if port.verbose_flow_direction == 'SOURCE': 

129 return 'port_b' 

130 else: 

131 return super().get_port_name(port) 

132 

133 

134class Consumer(AixLib): 

135 path = "AixLib.Systems.HydraulicModules.SimpleConsumer" 

136 represents = [hvac_aggregations.Consumer] 

137 

138 def __init__(self, element): 

139 super().__init__(element) 

140 self._set_parameter(name='redeclare package Medium_con', 

141 unit=None, 

142 required=False, 

143 value=MEDIUM_WATER) 

144 self._set_parameter(name='capacity', 

145 unit=ureg.joule / ureg.kelvin, 

146 required=False, 

147 check=check_numeric( 

148 min_value=0 * ureg.joule / ureg.kelvin), 

149 attributes=['heat_capacity']) 

150 self._set_parameter(name='V', 

151 unit=ureg.meter ** 3, 

152 required=False, 

153 check=check_numeric(min_value=0 * ureg.meter ** 3), 

154 attributes=['volume']) 

155 self._set_parameter(name='Q_flow_fixed', 

156 unit=ureg.watt, 

157 required=False, 

158 check=check_numeric(min_value=0 * ureg.watt), 

159 attributes=['rated_power']) 

160 self._set_parameter(name='V_flow_nominal', 

161 unit=ureg.meter ** 3 / ureg.s, 

162 required=True, 

163 export=False, 

164 check=check_numeric( 

165 min_value=0 * ureg.meter ** 3 / ureg.s), 

166 attributes=['rated_volume_flow']) 

167 self._set_parameter(name='m_flow_nominal', 

168 unit=ureg.kg / ureg.s, 

169 required=False, 

170 function=lambda: 

171 (self.parameters['V_flow_nominal'].value * 998 

172 * ureg.kg / ureg.meter ** 3)) 

173 

174 def get_port_name(self, port): 

175 if port.verbose_flow_direction == 'SINK': 

176 return 'port_a' 

177 if port.verbose_flow_direction == 'SOURCE': 

178 return 'port_b' 

179 else: 

180 return super().get_port_name(port) 

181 

182 

183class ConsumerHeatingDistributorModule(AixLib): 

184 # TODO: the model does not exists in AiLib 

185 path = ("AixLib.Systems.ModularEnergySystems.Modules.ModularConsumer." 

186 "ConsumerDistributorModule") 

187 represents = [hvac_aggregations.ConsumerHeatingDistributorModule] 

188 

189 def __init__(self, 

190 element: hvac_aggregations.ConsumerHeatingDistributorModule): 

191 super().__init__(element) 

192 n_consumers = len(element.whitelist_elements) 

193 self._set_parameter(name='redeclare package Medium_con', 

194 unit=None, 

195 required=False, 

196 value=MEDIUM_WATER) 

197 self._set_parameter(name='T_start', 

198 unit=ureg.kelvin, 

199 required=False, 

200 check=check_numeric(min_value=0 * ureg.kelvin), 

201 attributes=['return_temperature']) 

202 self._set_parameter(name='n_consumers', 

203 unit=ureg.dimensionless, 

204 required=False, 

205 value=n_consumers) 

206 self._set_parameter(name='functionality', 

207 unit=None, 

208 required=False, 

209 value='Q_flow_fixed') 

210 self._set_parameter(name='demandType', 

211 unit=None, 

212 required=False, 

213 attributes=['demand_type']) 

214 self._set_parameter(name='capacity', 

215 unit=ureg.joule / ureg.kelvin, 

216 required=False, 

217 check=check_numeric( 

218 min_value=0 * ureg.joule / ureg.kelvin), 

219 attributes=['heat_capacity']) 

220 self._set_parameter(name='Q_flow_nom', 

221 unit=ureg.watt, 

222 required=False, 

223 check=check_numeric(min_value=0 * ureg.watt), 

224 attributes=['rated_power']) 

225 self._set_parameter(name='dT_nom', 

226 unit=ureg.kelvin, 

227 required=False, 

228 check=check_numeric(min_value=0 * ureg.kelvin), 

229 attributes=['dT_water']) 

230 self._set_parameter(name='hasFeedback', 

231 unit=None, 

232 required=False, 

233 attributes=['t_control']) 

234 self._set_parameter(name='TInSetSou', 

235 unit=None, 

236 required=False, 

237 value=("AixLib.Systems.ModularEnergySystems." 

238 "Modules.ModularConsumer.Types." 

239 "InputTypeConstant")) 

240 self._set_parameter(name='TInSet', 

241 unit=ureg.kelvin, 

242 required=False, 

243 check=check_numeric(min_value=0 * ureg.kelvin), 

244 attributes=['flow_temperature']) 

245 self._set_parameter(name='k_ControlConsumerValve', 

246 unit=None, 

247 required=False, 

248 value=0.1 * n_consumers) 

249 self._set_parameter(name='Ti_ControlConsumerValve', 

250 unit=None, 

251 required=False, 

252 value=10 * n_consumers) 

253 self._set_parameter(name='dp_Valve', 

254 unit=ureg.pascal, 

255 required=False, 

256 value=1000 * n_consumers) 

257 self._set_parameter(name='hasPump', 

258 unit=None, 

259 required=False, 

260 attributes=['has_pump']) 

261 self._set_parameter(name='TOutSet', 

262 unit=ureg.kelvin, 

263 required=False, 

264 check=check_numeric(min_value=0 * ureg.kelvin), 

265 attributes=['return_temperature']) 

266 self._set_parameter(name='TOutSetSou', 

267 unit=None, 

268 required=False, 

269 value=("AixLib.Systems.ModularEnergySystems." 

270 "Modules.ModularConsumer.Types.InputType." 

271 "Constant")) 

272 self._set_parameter(name='k_ControlConsumerPump', 

273 unit=None, 

274 required=False, 

275 value=0.1 * n_consumers) 

276 self._set_parameter(name='Ti_ControlConsumerPump', 

277 unit=None, 

278 required=False, 

279 value=10 * n_consumers) 

280 self._set_parameter(name='dp_nominalConPump', 

281 unit=ureg.pascal, 

282 required=False, 

283 value=10000 * n_consumers) 

284 

285 def get_port_name(self, port): 

286 if port.verbose_flow_direction == 'SINK': 

287 return 'port_a' 

288 if port.verbose_flow_direction == 'SOURCE': 

289 return 'port_b' 

290 else: 

291 return super().get_port_name(port) 

292 

293 

294class BoilerAggregation(AixLib): 

295 # TODO: the model does not exists in AiLib 

296 """Modelica AixLib representation of the GeneratorOneFluid aggregation.""" 

297 path = "AixLib.Systems.ModularEnergySystems.Modules.ModularBoiler." \ 

298 "ModularBoiler" 

299 represents = [hvac_aggregations.GeneratorOneFluid] 

300 

301 def __init__(self, element): 

302 super().__init__(element) 

303 self._set_parameter(name='redeclare package Medium', 

304 unit=None, 

305 required=False, 

306 value=MEDIUM_WATER) 

307 self._set_parameter(name='hasPump', 

308 unit=None, 

309 required=False, 

310 attributes=['has_pump']) 

311 self._set_parameter(name='hasFeedback', 

312 unit=None, 

313 required=False, 

314 attributes=['has_bypass']) 

315 self._set_parameter(name='QNom', 

316 unit=ureg.watt, 

317 required=False, 

318 check=check_numeric(min_value=0 * ureg.watt), 

319 attributes=['rated_power']) 

320 self._set_parameter(name='PLRMin', 

321 unit=ureg.dimensionless, 

322 required=False, 

323 check=check_numeric( 

324 min_value=0 * ureg.dimensionless), 

325 attributes=['min_PLR']) 

326 self._set_parameter(name='TRetNom', 

327 unit=ureg.kelvin, 

328 required=False, 

329 check=check_numeric( 

330 min_value=0 * ureg.kelvin), 

331 attributes=['return_temperature']) 

332 self._set_parameter(name='dTWaterNom', 

333 unit=ureg.kelvin, 

334 required=False, 

335 check=check_numeric(min_value=0 * ureg.kelvin), 

336 attributes=['dT_water']) 

337 self._set_parameter(name='dp_Valve', 

338 unit=ureg.pascal, 

339 required=False, 

340 value=10000) 

341 

342 def get_port_name(self, port): 

343 if port.verbose_flow_direction == 'SINK': 

344 return 'port_a' 

345 if port.verbose_flow_direction == 'SOURCE': 

346 return 'port_b' 

347 else: 

348 return super().get_port_name(port) 

349 

350 

351class Distributor(AixLib): 

352 path = "AixLib.Fluid.HeatExchangers.ActiveWalls.Distributor" 

353 represents = [hvac.Distributor] 

354 

355 def __init__(self, element: hvac.Distributor): 

356 super().__init__(element) 

357 n_ports = self.get_n_ports() 

358 self._set_parameter(name='redeclare package Medium', 

359 unit=None, 

360 required=False, 

361 value=MEDIUM_WATER) 

362 self._set_parameter(name='n', 

363 unit=None, 

364 required=False, 

365 value=n_ports) 

366 self._set_parameter(name='m_flow_nominal', 

367 unit=ureg.kg / ureg.s, 

368 required=False, 

369 check=check_numeric(min_value=0 * ureg.kg / ureg.s), 

370 attributes=['rated_mass_flow']) 

371 

372 def get_n_ports(self): 

373 ports = {port.guid: port for port in self.element.ports if 

374 port.connection} 

375 return len(ports) / 2 - 1 

376 

377 def get_port_name(self, port): 

378 try: 

379 index = self.element.ports.index(port) 

380 except ValueError: 

381 # unknown port 

382 index = -1 

383 if (index % 2) == 0: 

384 return "port_a_consumer" 

385 elif (index % 2) == 1: 

386 return "port_b_consumer" 

387 else: 

388 return super().get_port_name(port) 

389 

390 @staticmethod 

391 def get_new_port_name(distributor, other_inst, distributor_port, 

392 other_port, distributors_n, distributors_ports): 

393 if distributor not in distributors_n: 

394 distributors_n[distributor] = 0 

395 distributors_ports[distributor] = {} 

396 distributors_n[distributor] += 1 

397 if type(other_inst.element) is hvac_aggregations.GeneratorOneFluid: 

398 list_name = distributor_port.split('.')[:-1] + \ 

399 ['mainReturn' if 'port_a' in other_port 

400 else 'mainFlow'] 

401 else: 

402 port_key = other_port.split('.')[-1] 

403 if port_key not in distributors_ports[distributor]: 

404 distributors_ports[distributor][port_key] = 0 

405 distributors_ports[distributor][port_key] += 1 

406 n = distributors_ports[distributor][port_key] 

407 list_name = distributor_port.split('.')[:-1] + \ 

408 ['flowPorts[%d]' % n if 'port_a' in other_port 

409 else 'returnPorts[%d]' % n] 

410 return '.'.join(list_name) 

411 

412 

413class ThreeWayValve(AixLib): 

414 path = "AixLib.Fluid.Actuators.Valves.ThreeWayEqualPercentageLinear" 

415 represents = [hvac.ThreeWayValve] 

416 

417 def __init__(self, element): 

418 super().__init__(element) 

419 self._set_parameter(name='redeclare package Medium_con', 

420 unit=None, 

421 required=False, 

422 value=MEDIUM_WATER) 

423 self._set_parameter(name='m_flow_nominal', 

424 unit=ureg.kg / ureg.s, 

425 required=True, 

426 check=check_numeric( 

427 min_value=0 * ureg.kg / ureg.s), 

428 attributes=['nominal_mass_flow_rate']) 

429 self._set_parameter(name='dpValve_nominal', 

430 unit=ureg.pascal, 

431 required=True, 

432 check=check_numeric(min_value=0 * ureg.pascal), 

433 attributes=['nominal_pressure_difference']) 

434 

435 def get_port_name(self, port): 

436 try: 

437 index = self.element.ports.index(port) 

438 except ValueError: 

439 # unknown port 

440 index = -1 

441 if index == 0: 

442 return "port_1" 

443 elif index == 1: 

444 return "port_2" 

445 elif index == 2: 

446 return "port_3" 

447 else: 

448 return super().get_port_name(port) 

449 

450 

451class Heatpump(AixLib): 

452 path = "AixLib.Fluid.HeatPumps.HeatPump" 

453 represents = [hvac.HeatPump] 

454 

455 def __init__(self, element): 

456 super().__init__(element) 

457 self._set_parameter(name='redeclare package Medium_con', 

458 unit=None, 

459 required=False, 

460 value=MEDIUM_WATER) 

461 self._set_parameter(name='redeclare Medium_eva Medium_con', 

462 unit=None, 

463 required=False, 

464 value=MEDIUM_WATER) 

465 self._set_parameter(name='Q_useNominal', 

466 unit=ureg.watt, 

467 required=False, 

468 check=check_numeric(min_value=0 * ureg.watt), 

469 attributes=['rated_power']) 

470 

471 def get_port_name(self, port): 

472 # TODO: heat pumps might have 4 ports (if source is modeled in BIM) 

473 if port.verbose_flow_direction == 'SINK': 

474 return 'port_a' 

475 if port.verbose_flow_direction == 'SOURCE': 

476 return 'port_b' 

477 else: 

478 return super().get_port_name(port) 

479 

480 

481class Chiller(AixLib): 

482 path = "AixLib.Fluid.Chillers.Chiller" 

483 represents = [hvac.Chiller] 

484 

485 def __init__(self, element): 

486 super().__init__(element) 

487 self._set_parameter(name='redeclare package Medium_con', 

488 unit=None, 

489 required=False, 

490 value=MEDIUM_WATER) 

491 self._set_parameter(name='redeclare Medium_eva Medium_con', 

492 unit=None, 

493 required=False, 

494 value=MEDIUM_WATER) 

495 self._set_parameter(name='Q_useNominal', 

496 unit=ureg.watt, 

497 required=False, 

498 check=check_numeric(min_value=0 * ureg.watt), 

499 attributes=['rated_power']) 

500 

501 def get_port_name(self, port): 

502 # TODO heat pumps might have 4 ports (if source is modeld in BIM) 

503 if port.verbose_flow_direction == 'SINK': 

504 return 'port_a' 

505 if port.verbose_flow_direction == 'SOURCE': 

506 return 'port_b' 

507 else: 

508 return super().get_port_name(port) 

509 

510 

511class CHP(AixLib): 

512 path = "AixLib.Fluid.BoilerCHP.CHPNoControl" 

513 represents = [hvac.CHP] 

514 

515 def __init__(self, element): 

516 super().__init__(element) 

517 self._set_parameter(name='redeclare package Medium', 

518 unit=None, 

519 required=False, 

520 value=MEDIUM_WATER) 

521 

522 

523class Storage(AixLib): 

524 path = "AixLib.Fluid.Storage.BufferStorage" 

525 represents = [hvac.Storage] 

526 

527 def __init__(self, element): 

528 super().__init__(element) 

529 self._set_parameter(name='redeclare package Medium', 

530 unit=None, 

531 required=False, 

532 value=MEDIUM_WATER) 

533 self._set_parameter(name='hTank', 

534 unit=ureg.meter, 

535 required=True, 

536 export=False, 

537 check=check_numeric(min_value=0 * ureg.meter), 

538 attributes=['height']) 

539 self._set_parameter(name='dTank', 

540 unit=ureg.meter, 

541 required=True, 

542 export=False, 

543 check=check_numeric(min_value=0 * ureg.meter), 

544 attributes=['diameter']) 

545 self._set_parameter(name='data', 

546 unit=None, 

547 required=False, 

548 value={'hTank': self.parameters['hTank'], 

549 "dTank": self.parameters['dTank']})