EnergySystemModels 20260416003__tar.gz → 20260416005__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/PKG-INFO +1 -1
  2. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/setup.py +1 -1
  3. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PV/ProductionElectriquePV.py +158 -57
  4. energysystemmodels-20260416005/src/ThermodynamicCycles/Combustion/NG_Boiler_Efficiency_EN1295X.py +292 -0
  5. energysystemmodels-20260416005/src/ThermodynamicCycles/Combustion/NG_Heating_Value.py +131 -0
  6. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/energysystemmodels.egg-info/PKG-INFO +1 -1
  7. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/energysystemmodels.egg-info/SOURCES.txt +2 -1
  8. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/energysystemmodels.egg-info/top_level.txt +0 -1
  9. energysystemmodels-20260416003/src/EnergySystemModels/__init__.py +0 -35
  10. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/LICENSE +0 -0
  11. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/README.md +0 -0
  12. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/setup.cfg +0 -0
  13. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/AHU/AirPort/AirPort.py +0 -0
  14. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/AHU/AirPort/__init__.py +0 -0
  15. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/AHU/Coil/CoolingCoil.py +0 -0
  16. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/AHU/Coil/CoolingCoil_Expert.py +0 -0
  17. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/AHU/Coil/CoolingCoil_Sensible.py +0 -0
  18. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/AHU/Coil/CoolingCoil_Tc.py +0 -0
  19. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/AHU/Coil/HeatingCoil.py +0 -0
  20. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/AHU/Coil/HeatingCoilNUT.py +0 -0
  21. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/AHU/Coil/__init__.py +0 -0
  22. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/AHU/Connect.py +0 -0
  23. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/AHU/FreshAir/AirMix.py +0 -0
  24. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/AHU/FreshAir/FreshAir.py +0 -0
  25. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/AHU/FreshAir/__init__.py +0 -0
  26. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/AHU/GenericAHU/AirRecoveryAHU.py +0 -0
  27. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/AHU/GenericAHU/AirRecyclingAHU.py +0 -0
  28. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/AHU/GenericAHU/__init__.py +0 -0
  29. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/AHU/HeatRecovery/Heat_plate_exchanger.py +0 -0
  30. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/AHU/HeatRecovery/Thermal_wheel_exchanger.py +0 -0
  31. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/AHU/HeatRecovery/__init__.py +0 -0
  32. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/AHU/Humidification/Humidifier.py +0 -0
  33. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/AHU/Humidification/__init__.py +0 -0
  34. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/AHU/__init__.py +0 -0
  35. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/AHU/air_humide/PsychrometricChart.py +0 -0
  36. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/AHU/air_humide/__init__.py +0 -0
  37. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/AHU/air_humide/air_humide.py +0 -0
  38. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/AHU/air_humide/air_humide_NB.py +0 -0
  39. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/CEE/CEE.py +0 -0
  40. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/CEE/__init__.py +0 -0
  41. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/Distillation/Deethanizer.py +0 -0
  42. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/Distillation/PengRobinson.py +0 -0
  43. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/Distillation/__init__.py +0 -0
  44. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/Distillation/diag_enthalpy_models.py +0 -0
  45. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/Distillation/reverse_engineer_enthalpies.py +0 -0
  46. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/Facture/ATR_Transport_Distribution.py +0 -0
  47. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/Facture/SONALGAZ_Elec.py +0 -0
  48. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/Facture/SONALGAZ_gaz.py +0 -0
  49. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/Facture/TURPE.py +0 -0
  50. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/Facture/__init__.py +0 -0
  51. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/Facture/coefficients_elec.json +0 -0
  52. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/Facture/coefficients_gaz_ATRD.json +0 -0
  53. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/Facture/coefficients_gaz_ATRT.json +0 -0
  54. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/Facture/coefficients_gaz_TICGN.json +0 -0
  55. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/Facture/coefficients_gaz_TVA.json +0 -0
  56. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/Facture/coefficients_sonalgaz_elec.json +0 -0
  57. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/Facture/coefficients_sonalgaz_gaz.json +0 -0
  58. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/Facture/df_utils.py +0 -0
  59. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/Facture/test.py +0 -0
  60. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/HeatTransfer/CompositeWall.py +0 -0
  61. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/HeatTransfer/ParallelepipedicBody.py +0 -0
  62. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/HeatTransfer/PipeInsulationAnalysis.py +0 -0
  63. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/HeatTransfer/PlateHeatTransfer.py +0 -0
  64. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/HeatTransfer/__init__.py +0 -0
  65. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/IPMVP/IPMVP.py +0 -0
  66. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/IPMVP/__init__.py +0 -0
  67. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/MeteoCiel/DJU_costic.py +0 -0
  68. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/MeteoCiel/MeteoCiel_Scraping.py +0 -0
  69. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/MeteoCiel/MeteoCiel_dayScraping.py +0 -0
  70. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/MeteoCiel/__init__.py +0 -0
  71. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/NodeEditor/__init__.py +0 -0
  72. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/NodeEditor/nodeeditor/__init__.py +0 -0
  73. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/NodeEditor/nodeeditor/node_content_widget.py +0 -0
  74. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/NodeEditor/nodeeditor/node_edge.py +0 -0
  75. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/NodeEditor/nodeeditor/node_edge_dragging.py +0 -0
  76. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/NodeEditor/nodeeditor/node_edge_validators.py +0 -0
  77. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/NodeEditor/nodeeditor/node_editor_widget.py +0 -0
  78. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/NodeEditor/nodeeditor/node_editor_window.py +0 -0
  79. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/NodeEditor/nodeeditor/node_graphics_cutline.py +0 -0
  80. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/NodeEditor/nodeeditor/node_graphics_edge.py +0 -0
  81. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/NodeEditor/nodeeditor/node_graphics_edge_path.py +0 -0
  82. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/NodeEditor/nodeeditor/node_graphics_node.py +0 -0
  83. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/NodeEditor/nodeeditor/node_graphics_scene.py +0 -0
  84. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/NodeEditor/nodeeditor/node_graphics_socket.py +0 -0
  85. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/NodeEditor/nodeeditor/node_graphics_view.py +0 -0
  86. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/NodeEditor/nodeeditor/node_node.py +0 -0
  87. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/NodeEditor/nodeeditor/node_scene.py +0 -0
  88. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/NodeEditor/nodeeditor/node_scene_clipboard.py +0 -0
  89. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/NodeEditor/nodeeditor/node_scene_history.py +0 -0
  90. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/NodeEditor/nodeeditor/node_serializable.py +0 -0
  91. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/NodeEditor/nodeeditor/node_socket.py +0 -0
  92. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/NodeEditor/nodeeditor/utils.py +0 -0
  93. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/NodeEditor/setup.py +0 -0
  94. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/NodeEditor/tests/__init__.py +0 -0
  95. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/NodeEditor/tests/test_000_import.py +0 -0
  96. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/NodeEditor/tox.ini +0 -0
  97. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/OpenWeatherMap/OpenWeatherMap_call.py +0 -0
  98. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/OpenWeatherMap/OpenWeatherMap_call_location.py +0 -0
  99. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/OpenWeatherMap/SQlite_OpenWeatherMap.py +0 -0
  100. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/OpenWeatherMap/__init__.py +0 -0
  101. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/OpenWeatherMap/config.ini +0 -0
  102. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/OpenWeatherMap/config_historical.ini +0 -0
  103. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/OpenWeatherMap/data.json +0 -0
  104. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/OpenWeatherMap/get_weather.py +0 -0
  105. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PV/__init__.py +0 -0
  106. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PinchAnalysis/PinchAnalysis.py +0 -0
  107. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PinchAnalysis/__init__.py +0 -0
  108. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PinchAnalysis/functions/Two_flow_pinch_analysis.py +0 -0
  109. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PinchAnalysis/functions/__init__.py +0 -0
  110. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PinchAnalysis/functions/apply_heat_exchange_above.py +0 -0
  111. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PinchAnalysis/functions/apply_heat_exchange_below.py +0 -0
  112. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PinchAnalysis/functions/calculation_HEN.py +0 -0
  113. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PinchAnalysis/functions/calculation_allowed_combinations.py +0 -0
  114. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PinchAnalysis/functions/calculation_below_above_stream.py +0 -0
  115. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PinchAnalysis/functions/calculation_composite_curve.py +0 -0
  116. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PinchAnalysis/functions/calculation_decomposition.py +0 -0
  117. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PinchAnalysis/functions/calculation_graphical_HEN.py +0 -0
  118. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PinchAnalysis/functions/calculation_surplus_deficit.py +0 -0
  119. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PinchAnalysis/functions/check_additional_heat_exchanges.py +0 -0
  120. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PinchAnalysis/functions/draw_hen_from_df.py +0 -0
  121. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PinchAnalysis/functions/plotting.py +0 -0
  122. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PyqtSimulator/__init__.py +0 -0
  123. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PyqtSimulator/calc_conf.py +0 -0
  124. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PyqtSimulator/calc_drag_listbox.py +0 -0
  125. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PyqtSimulator/calc_node_base.py +0 -0
  126. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PyqtSimulator/calc_sub_window.py +0 -0
  127. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PyqtSimulator/calc_window.py +0 -0
  128. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PyqtSimulator/config.ini +0 -0
  129. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PyqtSimulator/main.py +0 -0
  130. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PyqtSimulator/nodes/Simple_HEX.py +0 -0
  131. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PyqtSimulator/nodes/__init__.py +0 -0
  132. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PyqtSimulator/nodes/air_input.py +0 -0
  133. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PyqtSimulator/nodes/air_mixer.py +0 -0
  134. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PyqtSimulator/nodes/air_output.py +0 -0
  135. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PyqtSimulator/nodes/compressor.py +0 -0
  136. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PyqtSimulator/nodes/compressor_m.py +0 -0
  137. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PyqtSimulator/nodes/condenser.py +0 -0
  138. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PyqtSimulator/nodes/cooling_coil.py +0 -0
  139. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PyqtSimulator/nodes/cooling_coil_expert.py +0 -0
  140. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PyqtSimulator/nodes/cooling_coil_sensible.py +0 -0
  141. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PyqtSimulator/nodes/desuperheater.py +0 -0
  142. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PyqtSimulator/nodes/evaporator.py +0 -0
  143. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PyqtSimulator/nodes/expansion_valve.py +0 -0
  144. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PyqtSimulator/nodes/heating_coil.py +0 -0
  145. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PyqtSimulator/nodes/humidificateur.py +0 -0
  146. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PyqtSimulator/nodes/input.py +0 -0
  147. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PyqtSimulator/nodes/operations.py +0 -0
  148. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PyqtSimulator/nodes/output.py +0 -0
  149. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PyqtSimulator/nodes/random_meteo.py +0 -0
  150. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PyqtSimulator/nodes/source_P_h.py +0 -0
  151. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/PyqtSimulator/nodes/turbine.py +0 -0
  152. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Aeraulic/StraightPipe.py +0 -0
  153. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Aeraulic/__init__.py +0 -0
  154. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Carnot_Cycle_Analysis.py +0 -0
  155. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Chiller.py +0 -0
  156. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Combustion/Combustor_cantera.py +0 -0
  157. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Combustion/Gaz_Boiler.py +0 -0
  158. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Combustion/__init__.py +0 -0
  159. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Compressor/Compressor.py +0 -0
  160. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Compressor/Compressor_m.py +0 -0
  161. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Compressor/__init__.py +0 -0
  162. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Condenser/Condenser.py +0 -0
  163. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Condenser/__init__.py +0 -0
  164. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Connect.py +0 -0
  165. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Desuperheater/Desuperheater.py +0 -0
  166. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Desuperheater/__init__.py +0 -0
  167. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Evaporator/Evaporator.py +0 -0
  168. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Evaporator/__init__.py +0 -0
  169. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Expansion_Valve/Expansion_Valve.py +0 -0
  170. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Expansion_Valve/__init__.py +0 -0
  171. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/FluidPort/FluidPort.py +0 -0
  172. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/FluidPort/__init__.py +0 -0
  173. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/HEX/AirCooled_HEX.py +0 -0
  174. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/HEX/DTLM_HEX.py +0 -0
  175. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/HEX/NUT_HEX.py +0 -0
  176. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/HEX/Simple_HEX.py +0 -0
  177. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/HEX/__init__.py +0 -0
  178. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Hydraulic/StraightPipe.py +0 -0
  179. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Hydraulic/TA_Valve.py +0 -0
  180. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Hydraulic/__init__.py +0 -0
  181. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Pump/Pump.py +0 -0
  182. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Pump/Pump_m.py +0 -0
  183. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Pump/__init__.py +0 -0
  184. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Sink/Sink.py +0 -0
  185. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Sink/__init__.py +0 -0
  186. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Source/Source.py +0 -0
  187. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Source/__init__.py +0 -0
  188. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Tank/MixedStorage.py +0 -0
  189. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Tank/__init__.py +0 -0
  190. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Temperature_Entropy_Chart.py +0 -0
  191. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Turbine/Turbine.py +0 -0
  192. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/Turbine/__init__.py +0 -0
  193. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/ThermodynamicCycles/__init__.py +0 -0
  194. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/TkinterGUI/Chiller.py +0 -0
  195. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/TkinterGUI/__init__.py +0 -0
  196. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/energysystemmodels.egg-info/dependency_links.txt +0 -0
  197. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/src/energysystemmodels.egg-info/requires.txt +0 -0
  198. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/test/test_api_diagnostic.py +0 -0
  199. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/test/test_api_key.py +0 -0
  200. {energysystemmodels-20260416003 → energysystemmodels-20260416005}/test/test_compare_keys.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: energysystemmodels
3
- Version: 20260416003
3
+ Version: 20260416005
4
4
  Summary: Energy systems models are the mathematical models that are developed in order to represent as reliably as possible various energy-related problems.
5
5
  Author: Zoheir HADID
6
6
  Author-email: zoheir.hadid@gmail.com
@@ -11,7 +11,7 @@ setup(
11
11
  name='energysystemmodels',
12
12
 
13
13
 
14
- version='20260416003',
14
+ version='20260416005',
15
15
 
16
16
  description='Energy systems models are the mathematical models that are developed in order to represent as reliably as possible various energy-related problems.',
17
17
 
@@ -121,7 +121,7 @@ class SolarSystem:
121
121
  self.ac = pvlib.inverter.sandia(self.dc['v_mp'], self.dc['p_mp'], self.inverter)
122
122
  #print("puissance CA (courant alternatif)", self.ac)
123
123
  # Calcul de l'énergie annuelle
124
- self.annual_energy = -self.ac.sum()
124
+ self.annual_energy = self.ac.sum()
125
125
 
126
126
  module_area = self.module['Area'] if self.module is not None else 0
127
127
  module_wc = self.module['Impo'] * self.module['Vmpo'] if self.module is not None else 0
@@ -148,30 +148,26 @@ class SolarSystem:
148
148
  'Productivite (kWh/kWc/an)',
149
149
  ])
150
150
 
151
- def summary(self, nb_modules=1, module_wc=None, capex_eur_kwc=1200,
152
- opex_eur_kwc=15, tarif_elec_eur_mwh=120, taux_autoconso=0.80):
151
+ def summary(self, nb_modules=1, module_wc=None,
152
+ capex_eur_m2=None, opex_eur_m2=None,
153
+ tarif_elec_eur_mwh=None, duree_vie=25):
153
154
  """Retourne un DataFrame de synthese technique et economique.
154
155
 
155
156
  Parameters
156
157
  ----------
157
158
  nb_modules : int
158
- Nombre de modules dans l'installation (defaut 1).
159
+ Nombre de modules dans l'installation.
159
160
  module_wc : float or None
160
- Puissance crete d'un module en Wc. Si None, deduite de
161
- self.module['Impo'] * self.module['Vmpo'] (parametres STC).
162
- capex_eur_kwc : float
163
- Cout d'investissement (EUR/kWc TTC pose comprise).
164
- opex_eur_kwc : float
165
- Cout d'exploitation annuel (EUR/kWc/an).
166
- tarif_elec_eur_mwh : float
167
- Prix de l'electricite evitee (EUR/MWh).
168
- taux_autoconso : float
169
- Fraction de la production autoconsommee (0-1).
170
-
171
- Returns
172
- -------
173
- pd.DataFrame
174
- Tableau a deux colonnes (Indicateur, Valeur).
161
+ Puissance crete d'un module en Wc.
162
+ capex_eur_m2 : float or None
163
+ Cout d'investissement par m2 de panneau installe (EUR/m2).
164
+ Si None, pas d'analyse economique.
165
+ opex_eur_m2 : float or None
166
+ Cout d'exploitation annuel par m2 (EUR/m2/an).
167
+ tarif_elec_eur_mwh : float or None
168
+ Prix de l'electricite valorisee (EUR/MWh).
169
+ duree_vie : int
170
+ Duree de vie du projet en annees (defaut 25 ans).
175
171
  """
176
172
  if module_wc is None:
177
173
  module_wc = self.module['Impo'] * self.module['Vmpo']
@@ -183,18 +179,9 @@ class SolarSystem:
183
179
  module_area = self.module['Area'] if self.module is not None else 1.65
184
180
  surface_m2 = nb_modules * module_area
185
181
 
186
- capex = puissance_kwc * capex_eur_kwc
187
- opex = puissance_kwc * opex_eur_kwc
188
- eco_annuelle = prod_totale_mwh * taux_autoconso * tarif_elec_eur_mwh
189
- gain_net = eco_annuelle - opex
190
- tri = capex / gain_net if gain_net > 0 else np.inf
191
-
192
182
  rows = [
183
+ ("--- SIMULATION ---", ""),
193
184
  ("Site", self.name),
194
- ("Latitude", f"{self.latitude:.3f} deg"),
195
- ("Longitude", f"{self.longitude:.3f} deg"),
196
- ("Azimut", f"{self.azimut} deg"),
197
- ("Inclinaison", f"{self.inclinaison} deg"),
198
185
  ("Module", self.module.name if self.module is not None else "N/A"),
199
186
  ("Surface module", f"{module_area:.3f} m2"),
200
187
  ("Nb modules", nb_modules),
@@ -203,15 +190,106 @@ class SolarSystem:
203
190
  ("Production / module", f"{prod_module_kwh:.1f} kWh/an"),
204
191
  ("Productivite specifique", f"{productivite:.0f} kWh/kWc/an"),
205
192
  ("Production totale", f"{prod_totale_mwh:.1f} MWh/an"),
206
- ("Taux autoconsommation", f"{taux_autoconso*100:.0f} %"),
207
- ("CAPEX", f"{capex/1000:.0f} kEUR"),
208
- ("OPEX annuel", f"{opex/1000:.1f} kEUR/an"),
209
- ("Economies annuelles", f"{eco_annuelle/1000:.1f} kEUR/an"),
210
- ("Gain net annuel", f"{gain_net/1000:.1f} kEUR/an"),
211
- ("TRI simple", f"{tri:.1f} ans"),
212
193
  ]
194
+
195
+ if all(v is not None for v in [capex_eur_m2, opex_eur_m2,
196
+ tarif_elec_eur_mwh]):
197
+ capex = surface_m2 * capex_eur_m2
198
+ opex = surface_m2 * opex_eur_m2
199
+ valorisation = prod_totale_mwh * tarif_elec_eur_mwh
200
+ gain_net = valorisation - opex
201
+ payback = capex / gain_net if gain_net > 0 else np.inf
202
+ roi = ((gain_net * duree_vie - capex) / capex * 100) if capex > 0 else 0
203
+
204
+ # TRI : taux r tel que sum(gain_net/(1+r)^t, t=1..N) = capex
205
+ tri = 0.0
206
+ if gain_net > 0 and capex > 0:
207
+ from scipy.optimize import brentq
208
+ def npv(r):
209
+ return sum(gain_net / (1 + r) ** t
210
+ for t in range(1, duree_vie + 1)) - capex
211
+ try:
212
+ tri = brentq(npv, -0.5, 5.0) * 100
213
+ except Exception:
214
+ tri = 0.0
215
+
216
+ rows += [
217
+ ("--- HYPOTHESES ECO ---", ""),
218
+ ("CAPEX", f"{capex_eur_m2} EUR/m2"),
219
+ ("OPEX", f"{opex_eur_m2} EUR/m2/an"),
220
+ ("Tarif electricite", f"{tarif_elec_eur_mwh} EUR/MWh"),
221
+ ("Duree de vie", f"{duree_vie} ans"),
222
+ ("--- RESULTATS ECO ---", ""),
223
+ ("Investissement", f"{capex/1000:.0f} kEUR"),
224
+ ("Valorisation annuelle", f"{valorisation/1000:.1f} kEUR/an"),
225
+ ("OPEX annuel", f"{opex/1000:.1f} kEUR/an"),
226
+ ("Gain net annuel", f"{gain_net/1000:.1f} kEUR/an"),
227
+ ("Payback", f"{payback:.1f} ans"),
228
+ ("ROI ({0} ans)".format(duree_vie), f"{roi:.0f} %"),
229
+ ("TRI", f"{tri:.1f} %"),
230
+ ]
231
+
213
232
  return pd.DataFrame(rows, columns=["Indicateur", "Valeur"])
214
233
 
234
+ def plot(self, figsize=(14, 5), nb_modules=1):
235
+ """Trace la production horaire AC et le profil mensuel."""
236
+ ac_kw = self.ac * nb_modules / 1000
237
+
238
+ fig, (ax1, ax2) = plt.subplots(1, 2, figsize=figsize)
239
+
240
+ ax1.plot(ac_kw.values, linewidth=0.3)
241
+ ax1.set_xlabel('Heure de l\'annee')
242
+ ax1.set_ylabel('Production AC (kW)')
243
+ ax1.set_title(f'{self.name} - Production horaire ({nb_modules} modules)')
244
+ ax1.grid(True, alpha=0.3)
245
+
246
+ monthly = ac_kw.resample('ME').sum()
247
+ total_mwh = abs(ac_kw.sum()) / 1000
248
+ ax2.bar(range(len(monthly)), monthly.values, color='orange')
249
+ ax2.set_xticks(range(len(monthly)))
250
+ ax2.set_xticklabels(['J','F','M','A','M','J','J','A','S','O','N','D'][:len(monthly)])
251
+ ax2.set_ylabel('Production mensuelle (kWh)')
252
+ ax2.set_title(f'{self.name} - Profil mensuel | Total : {total_mwh:.1f} MWh/an')
253
+ ax2.grid(axis='y', alpha=0.3)
254
+
255
+ plt.tight_layout()
256
+ plt.show()
257
+ return fig
258
+
259
+ def to_excel(self, filename=None, nb_modules=1):
260
+ """Exporte la production horaire en fichier Excel.
261
+
262
+ Parameters
263
+ ----------
264
+ filename : str or None
265
+ Chemin du fichier. Si None, genere '{name}_production.xlsx'.
266
+ nb_modules : int
267
+ Nombre de modules (defaut 1).
268
+
269
+ Returns
270
+ -------
271
+ str
272
+ Chemin du fichier cree.
273
+ """
274
+ if filename is None:
275
+ filename = f"{self.name.replace(' ', '_')}_production.xlsx"
276
+
277
+ df_export = pd.DataFrame({
278
+ 'Production_AC_kW': self.ac * nb_modules / 1000,
279
+ 'Irradiance_POA_W_m2': self.total_irradiance['poa_global'],
280
+ 'T_cellule_C': self.cell_temperature,
281
+ 'T_air_C': self.weather['temp_air'],
282
+ })
283
+ df_export.index.name = 'datetime_utc'
284
+
285
+ with pd.ExcelWriter(filename, engine='openpyxl') as writer:
286
+ df_export.to_excel(writer, sheet_name='Horaire')
287
+ df_export.resample('ME').sum().to_excel(writer, sheet_name='Mensuel')
288
+ self.df.to_excel(writer, sheet_name='Synthese')
289
+
290
+ print(f"Export : {filename}")
291
+ return filename
292
+
215
293
  def plot_annual_energy(self):
216
294
  energies = pd.Series({self.name: self.annual_energy})
217
295
  energies.plot(kind='bar', rot=0)
@@ -219,7 +297,7 @@ class SolarSystem:
219
297
 
220
298
  @staticmethod
221
299
  def orientation_study(latitude, longitude, name, altitude, timezone,
222
- scenarios, save_fig=None):
300
+ scenarios):
223
301
  """Etude parametrique d'orientation et d'inclinaison.
224
302
 
225
303
  Parameters
@@ -228,16 +306,17 @@ class SolarSystem:
228
306
  Parametres du site (identiques a __init__).
229
307
  scenarios : list[dict]
230
308
  Liste de dicts avec cles 'nom', 'azimut', 'inclinaison'.
231
- Exemple : [{'nom': 'Sud 35', 'azimut': 180, 'inclinaison': 35}]
232
- save_fig : str or None
233
- Chemin du fichier image a sauvegarder. Si None, pas de sauvegarde.
234
309
 
235
310
  Returns
236
311
  -------
237
- pd.DataFrame
312
+ df : pd.DataFrame
238
313
  Tableau comparatif trie par production decroissante.
314
+ df_monthly : pd.DataFrame
315
+ Production mensuelle (kWh/kWc) par scenario (colonnes)
316
+ et par mois (index).
239
317
  """
240
318
  resultats = []
319
+ monthly_data = {}
241
320
  for sc in scenarios:
242
321
  pv = SolarSystem(
243
322
  latitude=latitude, longitude=longitude,
@@ -246,12 +325,15 @@ class SolarSystem:
246
325
  azimut=sc['azimut'], inclinaison=sc['inclinaison']
247
326
  )
248
327
  pv.retrieve_module_inverter_data()
249
- pv.retrieve_weather_data()
250
328
  pv.calculate_solar_parameters()
251
329
 
252
330
  module_wc = pv.module['Impo'] * pv.module['Vmpo']
253
- prod_kwh_kwc = pv.annual_energy / 1000 / (module_wc / 1000)
331
+ ac_kwh_kwc = pv.ac / module_wc
332
+ monthly = ac_kwh_kwc.resample('ME').sum()
333
+ monthly.index = range(1, len(monthly) + 1)
334
+ monthly_data[sc['nom']] = monthly
254
335
 
336
+ prod_kwh_kwc = pv.annual_energy / 1000 / (module_wc / 1000)
255
337
  resultats.append({
256
338
  'Configuration': sc['nom'],
257
339
  'Azimut (deg)': sc['azimut'],
@@ -267,23 +349,42 @@ class SolarSystem:
267
349
  df = df.sort_values('Production (kWh/kWc/an)', ascending=False)
268
350
  df = df.reset_index(drop=True)
269
351
 
270
- # Graphique comparatif
352
+ df_monthly = pd.DataFrame(monthly_data)
353
+ return df, df_monthly
354
+
355
+ @staticmethod
356
+ def plot_orientation_study(df, df_monthly, name='', save_fig=None):
357
+ """Trace les profils mensuels par orientation.
358
+
359
+ Parameters
360
+ ----------
361
+ df : pd.DataFrame
362
+ Resultat de orientation_study() (tableau annuel).
363
+ df_monthly : pd.DataFrame
364
+ Profils mensuels par scenario.
365
+ name : str
366
+ Nom du site (pour le titre).
367
+ save_fig : str or None
368
+ Chemin fichier image. Si None, pas de sauvegarde.
369
+ """
271
370
  fig, ax = plt.subplots(figsize=(12, 6))
272
- colors = ['green' if i == 0 else 'orange'
273
- for i in range(len(df))]
274
- ax.bar(range(len(df)), df['Production (kWh/kWc/an)'],
275
- color=colors)
276
- ax.set_xticks(range(len(df)))
277
- ax.set_xticklabels(df['Configuration'], rotation=45, ha='right')
278
- ax.set_ylabel('Production annuelle (kWh/kWc/an)')
279
- ax.set_title(f'Comparaison productivite PV selon orientation - {name}')
280
- ax.axhline(y=prod_max, color='green', linestyle='--', alpha=0.5,
281
- label='Production optimale')
282
- ax.legend()
283
- ax.grid(axis='y', alpha=0.3)
371
+ mois_labels = ['Jan','Fev','Mar','Avr','Mai','Jun',
372
+ 'Jul','Aou','Sep','Oct','Nov','Dec']
373
+ totaux = df.set_index('Configuration')['Production (kWh/kWc/an)']
374
+ for col in df_monthly.columns:
375
+ total = totaux.get(col, df_monthly[col].sum())
376
+ ax.plot(df_monthly.index, df_monthly[col], 'o-',
377
+ linewidth=1.5, markersize=4,
378
+ label=f'{col} ({total:.0f} kWh/kWc/an)')
379
+ ax.set_xticks(range(1, len(df_monthly) + 1))
380
+ ax.set_xticklabels(mois_labels[:len(df_monthly)])
381
+ ax.set_xlabel('Mois')
382
+ ax.set_ylabel('Production mensuelle (kWh/kWc)')
383
+ ax.set_title(f'Profil mensuel par orientation - {name}')
384
+ ax.legend(fontsize=11)
385
+ ax.grid(True, alpha=0.3)
284
386
  fig.tight_layout()
285
387
  if save_fig:
286
388
  fig.savefig(save_fig, dpi=150)
287
389
  plt.show()
288
-
289
- return df
390
+ return fig
@@ -0,0 +1,292 @@
1
+ import numpy as np
2
+ import pandas as pd
3
+ import re
4
+ from ThermodynamicCycles.Combustion.NG_Heating_Value import NG_Heating_Value
5
+
6
+
7
+ class NG_Boiler_Efficiency:
8
+ """
9
+ EN12952-15 / EN12953-11 indirect method boiler efficiency calculator.
10
+ Computes heat losses (flue gas sensible, CO, radiation, condensation)
11
+ and derives LHV/HHV efficiencies for natural gas boilers.
12
+ """
13
+
14
+ R = 8.314510 # J/mol/K
15
+ VMOL = 22.414e-3 # m3/mol at 0C, 101.325 kPa
16
+ T_NTP = 273.15 # K (Normal Temperature 0C)
17
+ P_NTP = 101.325 # kPa
18
+
19
+ # Dry air molar composition
20
+ DRY_AIR = {'CO2': 0.00033, 'O2': 0.20946, 'N2': 0.78102, 'Ar': 0.00919}
21
+
22
+ # Flue gas Cp in kJ/Nm3/K (mean values for typical boiler temperature range)
23
+ FG_CP = {'CO2': 1.7, 'H2O': 1.5, 'N2': 1.3, 'O2': 1.3, 'Ar': 0.93}
24
+
25
+ # Radiation loss constants
26
+ RADIATION_CONSTANT = {'water_tube': 0.0113, 'fire_tube': 0.0072}
27
+
28
+ # CO net calorific value kJ/Nm3
29
+ NCV_CO = 12633.0
30
+
31
+ # Latent heat of water vaporization kJ/kg at 25C
32
+ LATENT_HEAT_WATER = 2442.5
33
+
34
+ def __init__(
35
+ self,
36
+ gas_composition: dict,
37
+ ng_flow_Nm3h: float,
38
+ ng_temperature_C: float = 25.0,
39
+ ng_LHV_kWh_Nm3: float = None,
40
+ flue_gas_temperature_C: float = 180.0,
41
+ O2_measured: float = 0.035,
42
+ O2_basis: str = 'dry',
43
+ CO_ppm: float = 0.0,
44
+ ambient_temperature_C: float = 25.0,
45
+ ambient_RH: float = 0.5,
46
+ ambient_pressure_mbar: float = 1013.25,
47
+ air_temperature_C: float = None,
48
+ boiler_type: str = 'water_tube',
49
+ design_useful_heat_kW: float = 1000.0,
50
+ electrical_credit_kW: float = 0.0,
51
+ other_heat_credit_kW: float = 0.0,
52
+ T_ref: float = 25.0,
53
+ ):
54
+ self.gas_composition = gas_composition
55
+ self.ng_flow = ng_flow_Nm3h
56
+ self.ng_temperature = ng_temperature_C
57
+ self.ng_LHV_kWh_Nm3_input = ng_LHV_kWh_Nm3
58
+ self.T_fg = flue_gas_temperature_C
59
+ self.O2_measured = O2_measured
60
+ self.O2_basis = O2_basis
61
+ self.CO_ppm = CO_ppm
62
+ self.T_amb = ambient_temperature_C
63
+ self.RH = ambient_RH
64
+ self.P_atm = ambient_pressure_mbar * 100.0 # Pa
65
+ self.T_air = air_temperature_C if air_temperature_C is not None else ambient_temperature_C
66
+ self.boiler_type = boiler_type
67
+ self.design_useful_heat_kW = design_useful_heat_kW
68
+ self.electrical_credit = electrical_credit_kW
69
+ self.other_heat_credit = other_heat_credit_kW
70
+ self.T_ref = T_ref
71
+ self.df = None
72
+
73
+ def calculate(self):
74
+ # --- 1. NG properties via NG_Heating_Value ---
75
+ comps = [
76
+ NG_Heating_Value.GasComponent(formula, x)
77
+ for formula, x in self.gas_composition.items()
78
+ ]
79
+ ng_props = NG_Heating_Value(comps)
80
+ props_df = ng_props.df
81
+
82
+ def get_prop(name, unit):
83
+ row = props_df[(props_df['Property'] == name) & (props_df['Unit'] == unit)]
84
+ return row['Value'].values[0] if not row.empty else None
85
+
86
+ LHV_kWh = get_prop('LHV vol', 'kWh/Nm3') if self.ng_LHV_kWh_Nm3_input is None else self.ng_LHV_kWh_Nm3_input
87
+ HHV_kWh = get_prop('HHV vol', 'kWh/Nm3')
88
+ density = get_prop('Density real', 'kg/Nm3')
89
+ Cp_mass = ng_props.Cp_mix() # J/kg/K
90
+
91
+ # --- 2. NG heat power ---
92
+ Cp_kWh_Nm3_K = (Cp_mass * density) / 3.6e6 # J/kg/K * kg/Nm3 -> J/Nm3/K -> kWh/Nm3/K
93
+ sensible_heat = Cp_kWh_Nm3_K * (self.ng_temperature - self.T_ref)
94
+ Q_ng_LHV = (LHV_kWh + sensible_heat) * self.ng_flow # kW
95
+ Q_ng_HHV = (HHV_kWh + sensible_heat) * self.ng_flow # kW
96
+
97
+ # --- 3. Ambient air moisture ---
98
+ Psat = 610.78 * np.exp(17.2694 * self.T_amb / (self.T_amb + 237.29)) # Pa
99
+ Pw = self.RH * Psat
100
+ x_H2O_air = Pw / self.P_atm # molar fraction
101
+
102
+ # --- 4-5. Wet air composition ---
103
+ wet_air = {}
104
+ for species, xi in self.DRY_AIR.items():
105
+ wet_air[species] = xi * (1 - x_H2O_air)
106
+ wet_air['H2O'] = x_H2O_air
107
+
108
+ # --- 6. Stoichiometric combustion ---
109
+ stoich_O2 = 0.0 # Nm3 O2 per Nm3 fuel
110
+ stoich_CO2 = 0.0 # Nm3 CO2 produced per Nm3 fuel
111
+ stoich_H2O = 0.0 # Nm3 H2O produced per Nm3 fuel
112
+ stoich_N2_fuel = 0.0
113
+
114
+ for formula, x in self.gas_composition.items():
115
+ if formula == 'N2':
116
+ stoich_N2_fuel += x
117
+ continue
118
+ if formula == 'CO2':
119
+ stoich_CO2 += x
120
+ continue
121
+ if formula == 'H2':
122
+ stoich_O2 += x * 0.5
123
+ stoich_H2O += x * 1.0
124
+ continue
125
+
126
+ # Parse CnHm
127
+ clean = formula.replace("n-", "").replace("i-", "").replace("neo-", "")
128
+ matches = re.findall(r'([CHON])(\d*)', clean)
129
+ atoms = {elem: int(num) if num else 1 for elem, num in matches}
130
+ n_C = atoms.get('C', 0)
131
+ n_H = atoms.get('H', 0)
132
+
133
+ if n_C == 0 and n_H == 0:
134
+ continue
135
+
136
+ O2_needed = n_C + n_H / 4.0
137
+ stoich_O2 += x * O2_needed
138
+ stoich_CO2 += x * n_C
139
+ stoich_H2O += x * n_H / 2.0
140
+
141
+ # Stoichiometric air (Nm3 air / Nm3 fuel) based on O2 in dry air
142
+ stoich_air = stoich_O2 / self.DRY_AIR['O2']
143
+
144
+ # --- 7. Excess air from O2 measurement ---
145
+ O2_dry_frac = self.O2_measured
146
+ if self.O2_basis == 'wet':
147
+ # Convert wet O2 to dry O2 (approximate)
148
+ # Need iterative approach, but use first approximation
149
+ O2_dry_frac = self.O2_measured / (1 - x_H2O_air)
150
+
151
+ O2_dry_pct = O2_dry_frac * 100.0
152
+ O2_ref_pct = self.DRY_AIR['O2'] * 100.0
153
+
154
+ total_air_ratio = O2_ref_pct / (O2_ref_pct - O2_dry_pct)
155
+ total_air = stoich_air * total_air_ratio # Nm3 wet air / Nm3 fuel
156
+ excess_air_pct = (total_air_ratio - 1) * 100.0
157
+
158
+ # --- 8. Flue gas composition (Nm3 per Nm3 fuel) ---
159
+ fg = {}
160
+ fg['CO2'] = stoich_CO2 + total_air * wet_air.get('CO2', 0)
161
+ fg['H2O'] = stoich_H2O + total_air * wet_air.get('H2O', 0)
162
+ fg['O2'] = total_air * wet_air.get('O2', 0) - stoich_O2
163
+ fg['N2'] = stoich_N2_fuel + total_air * wet_air.get('N2', 0)
164
+ fg['Ar'] = total_air * wet_air.get('Ar', 0)
165
+
166
+ fg_total = sum(fg.values()) # Nm3 flue gas / Nm3 fuel
167
+ fg_dry = fg_total - fg['H2O']
168
+
169
+ # Flue gas flows
170
+ fg_flow = fg_total * self.ng_flow # Nm3/h
171
+ fg_flow_dry = fg_dry * self.ng_flow
172
+
173
+ # H2O volume fraction in flue gas
174
+ x_H2O_fg = fg['H2O'] / fg_total
175
+
176
+ # --- 9-10. Flue gas sensible heat loss ---
177
+ dT = self.T_fg - self.T_ref
178
+ # Weighted Cp of flue gas mixture (kJ/Nm3_mix/K)
179
+ Cp_fg_mix = sum(fg[sp] / fg_total * self.FG_CP.get(sp, 1.3) for sp in fg)
180
+
181
+ flue_gas_loss = fg_flow * Cp_fg_mix * dT / 3600.0 # kW
182
+
183
+ # CO loss
184
+ CO_loss = self.CO_ppm * 1e-6 * fg_flow_dry * self.NCV_CO / 3600.0 # kW
185
+
186
+ # Radiation loss
187
+ k_rad = self.RADIATION_CONSTANT.get(self.boiler_type, 0.0113)
188
+ radiation_loss = k_rad * self.design_useful_heat_kW ** 0.7 # kW
189
+
190
+ # --- 11. Condensation check ---
191
+ # Dew point from H2O partial pressure in flue gas
192
+ P_H2O_fg = x_H2O_fg * self.P_atm # Pa
193
+ # Inverse of Antoine: T_dew = 237.29 * ln(P/610.78) / (17.2694 - ln(P/610.78))
194
+ if P_H2O_fg > 0:
195
+ ln_ratio = np.log(P_H2O_fg / 610.78)
196
+ T_condensation = 237.29 * ln_ratio / (17.2694 - ln_ratio)
197
+ else:
198
+ T_condensation = 0.0
199
+
200
+ condensation_recovery = 0.0
201
+ if self.T_fg < T_condensation:
202
+ # Saturation pressure at flue gas temperature
203
+ Psat_fg = 610.78 * np.exp(17.2694 * self.T_fg / (self.T_fg + 237.29))
204
+ # Maximum H2O that can remain as vapor (mole fraction)
205
+ x_H2O_sat = Psat_fg / self.P_atm
206
+ # Condensed water: difference in Nm3 per Nm3 fuel
207
+ H2O_remaining_vapor = x_H2O_sat / (1 - x_H2O_sat) * (fg_total - fg['H2O'])
208
+ H2O_condensed_Nm3 = max(0, fg['H2O'] - H2O_remaining_vapor) # Nm3/Nm3_fuel
209
+ # Convert to kg: 1 Nm3 H2O = 18.015 / 22.414 = 0.8034 kg
210
+ H2O_condensed_kg_per_Nm3_fuel = H2O_condensed_Nm3 * 18.015 / 22.414
211
+ H2O_condensed_kg_h = H2O_condensed_kg_per_Nm3_fuel * self.ng_flow
212
+
213
+ # Latent heat recovery
214
+ latent_recovery = H2O_condensed_kg_h * self.LATENT_HEAT_WATER / 3600.0 # kW
215
+ # Sensible heat recovery from cooling condensate from T_condensation to T_fg
216
+ sensible_recovery = H2O_condensed_kg_h * 4.18 * (T_condensation - self.T_fg) / 3600.0 # kW
217
+ condensation_recovery = latent_recovery + sensible_recovery
218
+
219
+ # --- 12. Boiler efficiency (indirect method) ---
220
+ heat_credits = self.electrical_credit + self.other_heat_credit
221
+
222
+ # LHV basis
223
+ total_losses_LHV = flue_gas_loss + CO_loss + radiation_loss - condensation_recovery
224
+ efficiency_LHV = 1.0 - total_losses_LHV / (Q_ng_LHV + heat_credits) if (Q_ng_LHV + heat_credits) > 0 else 0.0
225
+
226
+ # HHV basis: flue gas loss must include latent heat of ALL water vapor
227
+ # Additional latent heat = total H2O in flue gas (as vapor) * latent heat
228
+ H2O_vapor_Nm3_h = fg['H2O'] * self.ng_flow # Nm3/h of water vapor
229
+ if self.T_fg < T_condensation:
230
+ H2O_vapor_Nm3_h = H2O_remaining_vapor * self.ng_flow
231
+ H2O_vapor_kg_h = H2O_vapor_Nm3_h * 18.015 / 22.414
232
+ latent_loss_all_water = H2O_vapor_kg_h * self.LATENT_HEAT_WATER / 3600.0 # kW
233
+
234
+ flue_gas_loss_HHV = flue_gas_loss + latent_loss_all_water
235
+ total_losses_HHV = flue_gas_loss_HHV + CO_loss + radiation_loss - condensation_recovery
236
+ efficiency_HHV = 1.0 - total_losses_HHV / (Q_ng_HHV + heat_credits) if (Q_ng_HHV + heat_credits) > 0 else 0.0
237
+
238
+ # --- 13. Useful heat ---
239
+ useful_heat_LHV = efficiency_LHV * Q_ng_LHV + heat_credits
240
+ useful_heat_HHV = efficiency_HHV * Q_ng_HHV + heat_credits
241
+
242
+ # --- Build output DataFrame ---
243
+ rows = [
244
+ ('--- INPUTS ---', ''),
245
+ ('NG flow (Nm3/h)', round(self.ng_flow, 2)),
246
+ ('LHV (kWh/Nm3)', round(LHV_kWh, 4)),
247
+ ('HHV (kWh/Nm3)', round(HHV_kWh, 4)),
248
+ ('T flue gas (C)', round(self.T_fg, 1)),
249
+ ('O2 dry (%)', round(O2_dry_pct, 2)),
250
+ ('Boiler type', self.boiler_type),
251
+ ('--- COMBUSTION ---', ''),
252
+ ('Air/fuel ratio (Nm3/Nm3)', round(total_air, 3)),
253
+ ('Excess air (%)', round(excess_air_pct, 1)),
254
+ ('Flue gas flow (Nm3/h)', round(fg_flow, 1)),
255
+ ('Condensation T (C)', round(T_condensation, 1)),
256
+ ('--- HEAT BALANCE (LHV) ---', ''),
257
+ ('NG heat input (kW)', round(Q_ng_LHV, 1)),
258
+ ('Flue gas loss (kW)', round(flue_gas_loss, 1)),
259
+ ('CO loss (kW)', round(CO_loss, 2)),
260
+ ('Radiation loss (kW)', round(radiation_loss, 2)),
261
+ ('Condensation recovery (kW)', round(condensation_recovery, 2)),
262
+ ('Total losses (kW)', round(total_losses_LHV, 1)),
263
+ ('Heat credits (kW)', round(heat_credits, 1)),
264
+ ('Useful heat (kW)', round(useful_heat_LHV, 1)),
265
+ ('Efficiency LHV (%)', round(efficiency_LHV * 100, 2)),
266
+ ('--- HEAT BALANCE (HHV) ---', ''),
267
+ ('Efficiency HHV (%)', round(efficiency_HHV * 100, 2)),
268
+ ]
269
+
270
+ self.df = pd.DataFrame(rows, columns=['Parameter', 'Value'])
271
+ self.df = self.df.set_index('Parameter')
272
+
273
+ # Store key results as attributes for programmatic access
274
+ self.LHV_kWh_Nm3 = LHV_kWh
275
+ self.HHV_kWh_Nm3 = HHV_kWh
276
+ self.Q_ng_LHV = Q_ng_LHV
277
+ self.Q_ng_HHV = Q_ng_HHV
278
+ self.efficiency_LHV = efficiency_LHV
279
+ self.efficiency_HHV = efficiency_HHV
280
+ self.useful_heat_LHV = useful_heat_LHV
281
+ self.useful_heat_HHV = useful_heat_HHV
282
+ self.excess_air_pct = excess_air_pct
283
+ self.air_fuel_ratio = total_air
284
+ self.fg_flow_Nm3h = fg_flow
285
+ self.T_condensation = T_condensation
286
+ self.condensation_recovery = condensation_recovery
287
+ self.flue_gas_loss = flue_gas_loss
288
+ self.radiation_loss = radiation_loss
289
+ self.CO_loss = CO_loss
290
+ self.total_losses_LHV = total_losses_LHV
291
+
292
+ return self.df