processpi 0.2.0__tar.gz → 0.2.0.1__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 (154) hide show
  1. {processpi-0.2.0 → processpi-0.2.0.1}/PKG-INFO +6 -5
  2. processpi-0.2.0.1/processpi/calculations/heat_transfer/__init__.py +59 -0
  3. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/heat_transfer/heat_exchanger_area.py +1 -1
  4. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/heat_transfer/lmtd.py +4 -5
  5. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/heat_transfer/radiation_greybody.py +2 -2
  6. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/components/acetone.py +1 -1
  7. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/components/air.py +2 -2
  8. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/components/ammonia.py +13 -0
  9. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/components/base.py +6 -4
  10. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/components/benzene.py +16 -5
  11. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/components/benzoicacid.py +1 -1
  12. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/components/bromine.py +1 -1
  13. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/components/carbonmonoxide.py +11 -0
  14. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/components/carbontetrachloride.py +1 -1
  15. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/components/chlorobenzene.py +1 -1
  16. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/components/chloroform.py +1 -1
  17. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/components/chloromethane.py +1 -1
  18. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/components/cyanogen.py +1 -1
  19. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/components/cyclohexene.py +1 -1
  20. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/components/ethane.py +10 -0
  21. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/components/ethanol.py +1 -1
  22. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/components/ethylacetate.py +1 -1
  23. processpi-0.2.0.1/processpi/components/ethylene.py +17 -0
  24. processpi-0.2.0.1/processpi/components/fluorine.py +17 -0
  25. processpi-0.2.0.1/processpi/components/fluorobenzene.py +17 -0
  26. processpi-0.2.0.1/processpi/components/formicacid.py +17 -0
  27. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/components/methanol.py +1 -1
  28. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/components/oil.py +10 -0
  29. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/components/organic_liquid.py +11 -0
  30. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/components/toluene.py +11 -2
  31. processpi-0.2.0.1/processpi/streams/__init__.py +2 -0
  32. processpi-0.2.0.1/processpi/streams/energy.py +67 -0
  33. processpi-0.2.0.1/processpi/streams/material.py +159 -0
  34. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/units/__init__.py +3 -1
  35. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/units/base.py +1 -1
  36. processpi-0.2.0.1/processpi/units/molar_flowrate.py +54 -0
  37. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/units/specific_heat.py +4 -4
  38. {processpi-0.2.0 → processpi-0.2.0.1}/processpi.egg-info/PKG-INFO +6 -5
  39. {processpi-0.2.0 → processpi-0.2.0.1}/processpi.egg-info/SOURCES.txt +8 -0
  40. {processpi-0.2.0 → processpi-0.2.0.1}/pyproject.toml +7 -4
  41. {processpi-0.2.0 → processpi-0.2.0.1}/setup.py +6 -5
  42. processpi-0.2.0/processpi/calculations/heat_transfer/__init__.py +0 -4
  43. {processpi-0.2.0 → processpi-0.2.0.1}/LICENSE +0 -0
  44. {processpi-0.2.0 → processpi-0.2.0.1}/README.md +0 -0
  45. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/__init__.py +0 -0
  46. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/__init__.py +0 -0
  47. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/base.py +0 -0
  48. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/engine.py +0 -0
  49. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/fluids/__init__.py +0 -0
  50. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/fluids/flow_type.py +0 -0
  51. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/fluids/friction_factor_colebrookwhite.py +0 -0
  52. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/fluids/optimium_pipe_dia.py +0 -0
  53. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/fluids/pressure_drop_darcy.py +0 -0
  54. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/fluids/pressure_drop_fanning.py +0 -0
  55. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/fluids/pressure_drop_hazen_williams.py +0 -0
  56. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/fluids/pump_power.py +0 -0
  57. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/fluids/reynolds_number.py +0 -0
  58. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/fluids/velocity.py +0 -0
  59. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/heat_transfer/biot.py +0 -0
  60. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/heat_transfer/boiling_chen.py +0 -0
  61. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/heat_transfer/combined_modes.py +0 -0
  62. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/heat_transfer/condensation.py +0 -0
  63. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/heat_transfer/condensation_dropwise.py +0 -0
  64. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/heat_transfer/condensation_nusselt.py +0 -0
  65. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/heat_transfer/conduction_heat_loss.py +0 -0
  66. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/heat_transfer/convection_heat_loss.py +0 -0
  67. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/heat_transfer/crossflow_tube.py +0 -0
  68. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/heat_transfer/fourier.py +0 -0
  69. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/heat_transfer/fourierlaw.py +0 -0
  70. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/heat_transfer/grashof.py +0 -0
  71. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/heat_transfer/newton_cooling.py +0 -0
  72. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/heat_transfer/ntu.py +0 -0
  73. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/heat_transfer/nusselt.py +0 -0
  74. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/heat_transfer/overall_u.py +0 -0
  75. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/heat_transfer/peclet.py +0 -0
  76. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/heat_transfer/prandtl.py +0 -0
  77. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/heat_transfer/radial_cylinder.py +0 -0
  78. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/heat_transfer/radiation_blackbody.py +0 -0
  79. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/heat_transfer/radiation_exchange.py +0 -0
  80. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/heat_transfer/radiation_viewfactor.py +0 -0
  81. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/heat_transfer/resistance.py +0 -0
  82. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/heat_transfer/reyleigh.py +0 -0
  83. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/heat_transfer/rohsenow_boiling.py +0 -0
  84. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/heat_transfer/stefan_boltzmann.py +0 -0
  85. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/mass_transfer/__init__.py +0 -0
  86. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/mass_transfer/distillation_stage_count.py +0 -0
  87. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/mass_transfer/drying_rate.py +0 -0
  88. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/reaction_engineering/__init__.py +0 -0
  89. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/reaction_engineering/catalyst_activity.py +0 -0
  90. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/reaction_engineering/reaction_rate.py +0 -0
  91. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/reaction_engineering/residence_time.py +0 -0
  92. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/thermodynamics/__init__.py +0 -0
  93. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/thermodynamics/enthalpy_change.py +0 -0
  94. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/thermodynamics/entropy_change.py +0 -0
  95. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/calculations/thermodynamics/heat_of_vaporization.py +0 -0
  96. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/components/__init__.py +0 -0
  97. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/components/aceticacid.py +0 -0
  98. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/components/acrylicacid.py +0 -0
  99. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/components/butane.py +0 -0
  100. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/components/carbondioxide.py +0 -0
  101. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/components/chlorine.py +0 -0
  102. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/components/constants.py +0 -0
  103. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/components/gas.py +0 -0
  104. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/components/inorganic_liquid.py +0 -0
  105. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/components/steam.py +0 -0
  106. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/components/vapor.py +0 -0
  107. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/components/water.py +0 -0
  108. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/constants.py +0 -0
  109. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/pipelines/__init__.py +0 -0
  110. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/pipelines/base.py +0 -0
  111. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/pipelines/design_rules.py +0 -0
  112. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/pipelines/engine.py +0 -0
  113. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/pipelines/equipment.py +0 -0
  114. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/pipelines/fittings.py +0 -0
  115. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/pipelines/insulation.py +0 -0
  116. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/pipelines/materials.py +0 -0
  117. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/pipelines/network.py +0 -0
  118. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/pipelines/nozzle.py +0 -0
  119. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/pipelines/pipelineresults.py +0 -0
  120. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/pipelines/pipes.py +0 -0
  121. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/pipelines/piping_costs.py +0 -0
  122. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/pipelines/pumps.py +0 -0
  123. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/pipelines/selection.py +0 -0
  124. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/pipelines/standards.py +0 -0
  125. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/pipelines/vessel.py +0 -0
  126. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/units/area.py +0 -0
  127. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/units/density.py +0 -0
  128. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/units/diameter.py +0 -0
  129. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/units/dimensionless.py +0 -0
  130. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/units/heat_flow.py +0 -0
  131. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/units/heat_flux.py +0 -0
  132. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/units/heat_of_vaporization.py +0 -0
  133. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/units/heat_transfer_coefficient.py +0 -0
  134. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/units/length.py +0 -0
  135. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/units/mass.py +0 -0
  136. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/units/mass_flowrate.py +0 -0
  137. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/units/power.py +0 -0
  138. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/units/pressure.py +0 -0
  139. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/units/strings.py +0 -0
  140. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/units/temperature.py +0 -0
  141. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/units/thermal_conductivity.py +0 -0
  142. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/units/thermal_resistance.py +0 -0
  143. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/units/velocity.py +0 -0
  144. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/units/viscosity.py +0 -0
  145. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/units/volume.py +0 -0
  146. {processpi-0.2.0 → processpi-0.2.0.1}/processpi/units/volumetric_flowrate.py +0 -0
  147. {processpi-0.2.0 → processpi-0.2.0.1}/processpi.egg-info/dependency_links.txt +0 -0
  148. {processpi-0.2.0 → processpi-0.2.0.1}/processpi.egg-info/requires.txt +0 -0
  149. {processpi-0.2.0 → processpi-0.2.0.1}/processpi.egg-info/top_level.txt +0 -0
  150. {processpi-0.2.0 → processpi-0.2.0.1}/setup.cfg +0 -0
  151. {processpi-0.2.0 → processpi-0.2.0.1}/tests/test_engine_network.py +0 -0
  152. {processpi-0.2.0 → processpi-0.2.0.1}/tests/test_network.py +0 -0
  153. {processpi-0.2.0 → processpi-0.2.0.1}/tests/test_pipeline_network.py +0 -0
  154. {processpi-0.2.0 → processpi-0.2.0.1}/tests/test_variables.py +0 -0
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: processpi
3
- Version: 0.2.0
3
+ Version: 0.2.0.1
4
4
  Summary: Python toolkit for chemical engineering simulations, equipment design, and unit conversions
5
- Home-page: https://github.com/varma666/ProcessPi
5
+ Home-page: https://processpi.org
6
6
  Author: Raviteja Varma Nadimpalli
7
7
  Author-email: Raviteja Varma Nadimpalli <processpi.dev@gmail.com>
8
8
  License: MIT License
@@ -27,9 +27,10 @@ License: MIT License
27
27
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
28
  SOFTWARE.
29
29
 
30
- Project-URL: Homepage, https://github.com/varma666/ProcessPi
31
- Project-URL: BugTracker, https://github.com/varma666/ProcessPi/issues
32
- Project-URL: SourceCode, https://github.com/varma666/ProcessPi
30
+ Project-URL: Homepage, https://processpi.org
31
+ Project-URL: Repository, https://github.com/varma666/ProcessPi
32
+ Project-URL: Documentation, https://processpi.org/user-guide/introduction/
33
+ Project-URL: IssueTracker, https://github.com/varma666/ProcessPi/issues
33
34
  Requires-Python: >=3.8
34
35
  Description-Content-Type: text/markdown
35
36
  License-File: LICENSE
@@ -0,0 +1,59 @@
1
+ from .conduction_heat_loss import ConductionHeatLoss
2
+ from .convection_heat_loss import ConvectionHeatLoss
3
+ from .heat_exchanger_area import HeatExchangerArea
4
+ from .biot import BiotNumber
5
+ from .nusselt import NusseltNumber
6
+ from .combined_modes import ConductionConvectionCombined
7
+ from .condensation_dropwise import DropwiseCondensation
8
+ from .condensation_nusselt import NusseltCondensation
9
+ from .condensation import CondensingVapourFilm
10
+ from .conduction_heat_loss import ConductionHeatLoss
11
+ from .convection_heat_loss import ConvectionHeatLoss
12
+ from .crossflow_tube import CrossFlowSingleTube
13
+ from .fourier import FourierNumber
14
+ from .fourierlaw import FourierLaw
15
+ from .heat_exchanger_area import HeatExchangerArea
16
+ from .lmtd import LMTD
17
+ from .newton_cooling import NewtonCooling
18
+ from .ntu import NTUHeatExchanger
19
+ from .nusselt import NusseltNumber
20
+ from .overall_u import OverallHeatTransferCoefficient
21
+ from .peclet import PecletNumber
22
+ from .prandtl import PrandtlNumber
23
+ from .radial_cylinder import RadialHeatFlowCylinder
24
+ from .radiation_blackbody import BlackbodyRadiation
25
+ from .radiation_exchange import RadiationExchange
26
+ from .radiation_greybody import GreybodyRadiation
27
+ from .radiation_viewfactor import RadiationWithViewFactor
28
+ from .resistance import ThermalResistanceSeries, ThermalResistanceParallel
29
+ from .reyleigh import RayleighNumber
30
+ from .rohsenow_boiling import RohsenowBoiling
31
+ from .stefan_boltzmann import StefanBoltzmann
32
+ __all__ = ["ConductionHeatLoss",
33
+ "ConvectionHeatLoss",
34
+ "HeatExchangerArea",
35
+ "BiotNumber",
36
+ "NusseltNumber",
37
+ "ConductionConvectionCombined",
38
+ "DropwiseCondensation",
39
+ "NusseltCondensation",
40
+ "CondensingVapourFilm",
41
+ "FourierNumber",
42
+ "FourierLaw",
43
+ "LMTD",
44
+ "NewtonCooling",
45
+ "NTUHeatExchanger",
46
+ "OverallHeatTransferCoefficient",
47
+ "PecletNumber",
48
+ "PrandtlNumber",
49
+ "RadialHeatFlowCylinder",
50
+ "BlackbodyRadiation",
51
+ "RadiationExchange",
52
+ "GreybodyRadiation",
53
+ "RadiationWithViewFactor",
54
+ "ThermalResistanceSeries",
55
+ "ThermalResistanceParallel",
56
+ "RayleighNumber",
57
+ "RohsenowBoiling",
58
+ "StefanBoltzmann"
59
+ ]
@@ -14,7 +14,7 @@ class HeatExchangerArea(CalculationBase):
14
14
  for key in required:
15
15
  if key not in self.inputs:
16
16
  raise ValueError(f"Missing required input: {key}")
17
- if self.inputs["log_mean_temp_diff"] <= 0:
17
+ if self.inputs["log_mean_temp_diff"].value <= 0:
18
18
  raise ValueError("Log mean temperature difference must be positive")
19
19
 
20
20
  def calculate(self):
@@ -2,22 +2,21 @@ import math
2
2
  from ..base import CalculationBase
3
3
  from ...units import *
4
4
 
5
- class LMTDHeatExchanger(CalculationBase):
5
+ class LMTD(CalculationBase):
6
6
  """
7
7
  Heat Exchanger using LMTD method.
8
8
  Q = U * A * ΔTlm
9
9
  """
10
10
  def validate_inputs(self):
11
- required = ["U", "area", "dT1", "dT2"]
11
+ required = ["dT1", "dT2"]
12
12
  for key in required:
13
13
  if key not in self.inputs:
14
14
  raise ValueError(f"Missing required input: {key}")
15
15
 
16
16
  def calculate(self):
17
- U = self._get_value(self.inputs["U"], "heat_transfer_coefficient")
18
- A = self._get_value(self.inputs["area"], "area")
17
+
19
18
  dT1 = self._get_value(self.inputs["dT1"], "temperature")
20
19
  dT2 = self._get_value(self.inputs["dT2"], "temperature")
21
20
 
22
21
  dTlm = dT1 if dT1 == dT2 else (dT1 - dT2) / math.log(dT1 / dT2)
23
- return HeatFlow(U * A * dTlm)
22
+ return dTlm
@@ -1,9 +1,9 @@
1
1
  from ..base import CalculationBase
2
2
  from ...units import *
3
3
 
4
- class GraybodyRadiation(CalculationBase):
4
+ class GreybodyRadiation(CalculationBase):
5
5
  """
6
- Graybody radiation with emissivity factor.
6
+ Greybody radiation with emissivity factor.
7
7
 
8
8
  **Formula:**
9
9
  q = ε * σ * T^4
@@ -51,4 +51,4 @@ class Acetone(Component):
51
51
  _viscosity_constants = [-14.918, 1023.4, 0.5961, 0, 0]
52
52
  _thermal_conductivity_constants = [0.2878, -0.000427, 0,0,0]
53
53
  _vapor_pressure_constants = [69.006, -5599.6, -7.0985, 0.0000062237, 2]
54
- _enthalpy_constants = [4.215E-7, 0.3397, 178.45, 3.639, 0] # Placeholder for enthalpy constants
54
+ _enthalpy_constants = [4.215E-7, 0.3397, 0, 0, 0] # Placeholder for enthalpy constants
@@ -46,9 +46,9 @@ class Air(Component):
46
46
  _critical_acentric_factor = 0 # Placeholder for critical acentric factor
47
47
 
48
48
  # Constants for various property correlations
49
- _density_constants = [0.26733, 132.45, 0.27341,59.15]
49
+ _density_constants = [2.8963, 0.26733, 132.45,0.2734]
50
50
  _specific_heat_constants = [-214460,9185.1,-106.12,0.41616,0]
51
51
  _viscosity_constants = [-20.077, 285.15, 1.784,-6.2382E-22, 10]
52
52
  _thermal_conductivity_constants = [0.28472, -0.0017393, 0,0,0]
53
53
  _vapor_pressure_constants = [21.662, -692.39, -0.392, 0.0047574, 1]
54
- _enthalpy_constants = [0.3822E-7, 59.15,0.6759,132.45, 0] # Placeholder for enthalpy constants
54
+ _enthalpy_constants = [0.8474E-7, 0.3822,0,0,0] # Placeholder for enthalpy constants
@@ -52,3 +52,16 @@ class Ammonia(Component):
52
52
  _thermal_conductivity_constants = [1.169, -0.002314, 0,0,0]
53
53
  _vapor_pressure_constants = [90.483, -4669.70, -11.607, 1.72E-02, 1]
54
54
  _enthalpy_constants = [3.1523E-7, 0.3914, -0.2289,0.2309, 0] # Placeholder for enthalpy constants
55
+
56
+
57
+ #@PropertyMethod
58
+ def specific_heat(self) -> SpecificHeat:
59
+ if self._specific_heat is not None:
60
+ return self._specific_heat
61
+ T = self.temperature.value
62
+ Tr = T / self._critical_temperature.value
63
+ t = 1 - Tr
64
+ cp = (self._sepcific_heat_constants[0] / t ) + (self._sepcific_heat_constants[1]) - ( 2 * self._sepcific_heat_constants[0] * self._sepcific_heat_constants[2] * t) - (self._sepcific_heat_constants[0] * self._sepcific_heat_constants[3] * (t**2)) - ((self._sepcific_heat_constants[2]**2)*(t**3)/3) - ((self._sepcific_heat_constants[2] * self._sepcific_heat_constants[3] * (t**4))/2) - ((self._sepcific_heat_constants[0]**2) * (t**5)/5)
65
+ return SpecificHeat(cp, "J/kgK")
66
+
67
+
@@ -121,9 +121,11 @@ class Component(ABC):
121
121
  if self._specific_heat is not None:
122
122
  return self._specific_heat
123
123
  T = self.temperature.value
124
- cp = sum(c * (T ** i) for i, c in enumerate(self._specific_heat_constants))
125
- cp *= self.molecular_weight
126
- return SpecificHeat(cp, "J/kgK")
124
+ cp_kmol = sum(c * (T ** i) for i, c in enumerate(self._specific_heat_constants))
125
+ cp_kg = cp_kmol / self.molecular_weight
126
+
127
+ return SpecificHeat(cp_kg, "J/kgK")
128
+
127
129
 
128
130
  # ----------------------------------------------------------------------
129
131
  # Viscosity
@@ -198,5 +200,5 @@ class Component(ABC):
198
200
  (self._enthalpy_constants[2] * tau) +
199
201
  (self._enthalpy_constants[3] * tau ** 2) +
200
202
  (self._enthalpy_constants[4] * tau ** 3))))
201
- dH *= self.molecular_weight
203
+ #dH *= self.molecular_weight
202
204
  return HeatOfVaporization(dH, "J/kg")
@@ -1,4 +1,4 @@
1
- from .base import Component
1
+ from .base import Component, PropertyMethod
2
2
  from processpi.units import *
3
3
 
4
4
 
@@ -28,9 +28,10 @@ class Benzene(Component):
28
28
  _viscosity_constants = [7.5117, 294.68, -2.794, 0, 0]
29
29
  _thermal_conductivity_constants = [0.23444, -0.00030572, 0, 0, 0]
30
30
  _vapor_pressure_constants = [83.107, -6486.2, -9.2194, 0.0000069844, 2]
31
- _enthalpy_constants = [4.5346E-7, 0.39053, 278.68, 3.4705, 0] # Placeholder for enthalpy constants
32
-
33
- def specific_heat(self, temperature: Temperature):
31
+ _enthalpy_constants = [4.5346E-7, 0.39053, 0, 0, 0] # Placeholder for enthalpy constants
32
+
33
+ @PropertyMethod
34
+ def specific_heat(self):
34
35
  """
35
36
  Calculates the specific heat of Benzene at a given temperature.
36
37
 
@@ -45,7 +46,7 @@ class Benzene(Component):
45
46
  Returns:
46
47
  SpecificHeat: The calculated specific heat in units of J/kgK.
47
48
  """
48
- T = temperature.value
49
+ T = self.temperature.value
49
50
  # Use a conditional to select the correct set of polynomial constants.
50
51
  if T <= 353.24:
51
52
  constants = self._specific_heat_constants_1
@@ -63,4 +64,14 @@ class Benzene(Component):
63
64
  # the molecular weight (g/mol), since 1 kmol is 1000 mol and 1 kg
64
65
  # is 1000 g.
65
66
  cp_kg = cp_kmol / self.molecular_weight
67
+
66
68
  return SpecificHeat(cp_kg, "J/kgK")
69
+
70
+ def httype(self):
71
+ P = self.pressure.to("Pa").value
72
+ Pvap = self.vapor_pressure().to("Pa").value
73
+ if P < Pvap:
74
+ httype = "organicvapour"
75
+ else:
76
+ httype = "organicliquid"
77
+ return httype
@@ -16,4 +16,4 @@ class BenzoicAcid(Component):
16
16
  _viscosity_constants = [-12.947,2557.9,0,0, 0]
17
17
  _thermal_conductivity_constants = [0.2391, -0.0002325, 0,0,0]
18
18
  _vapor_pressure_constants = [88.513, -11829, -8.6826, 2.32E-19, 6]
19
- _enthalpy_constants = [10.19E-7, 0.478, 395.45,7.1277, 0] # Placeholder for enthalpy constants
19
+ _enthalpy_constants = [10.19E-7, 0.478,0,0,0] # Placeholder for enthalpy constants
@@ -16,4 +16,4 @@ class Bromine(Component):
16
16
  _viscosity_constants = [16.775,-314,-3.9763,0, 0]
17
17
  _thermal_conductivity_constants = [-0.2185, 0.0042143,-0.000017753,3.10E-08,-2.01E-11]
18
18
  _vapor_pressure_constants = [108.26, -6592, -14.16,1.60E-02, 1]
19
- _enthalpy_constants = [4E-7, 0.351, 265.85,3.2323, 0] # Placeholder for enthalpy constants
19
+ _enthalpy_constants = [4E-7, 0.351, 0,0, 0] # Placeholder for enthalpy constants
@@ -15,3 +15,14 @@ class CarbonMonoxide(Component):
15
15
  _thermal_conductivity_constants = [0.2855, -0.001784,0,0,0]
16
16
  _vapor_pressure_constants = [45.698,-1076.60,-4.8814,7.57E-05, 2]
17
17
  _enthalpy_constants = [0.8585E-7, 0.4921, -0.326,0.2231, 0] # Placeholder for enthalpy constants
18
+
19
+
20
+ #@PropertyMethod
21
+ def specific_heat(self) -> SpecificHeat:
22
+ if self._specific_heat is not None:
23
+ return self._specific_heat
24
+ T = self.temperature.value
25
+ Tr = T / self._critical_temperature.value
26
+ t = 1 - Tr
27
+ cp = (self._sepcific_heat_constants[0] / t ) + (self._sepcific_heat_constants[1]) - ( 2 * self._sepcific_heat_constants[0] * self._sepcific_heat_constants[2] * t) - (self._sepcific_heat_constants[0] * self._sepcific_heat_constants[3] * (t**2)) - ((self._sepcific_heat_constants[2]**2)*(t**3)/3) - ((self._sepcific_heat_constants[2] * self._sepcific_heat_constants[3] * (t**4))/2) - ((self._sepcific_heat_constants[0]**2) * (t**5)/5)
28
+ return SpecificHeat(cp, "J/kgK")
@@ -14,4 +14,4 @@ class CarbonTetrachloride(Component):
14
14
  _viscosity_constants = [-8.0738,1121.1,-0.4726,0, 0]
15
15
  _thermal_conductivity_constants = [0.1589, -0.0001987,0,0,0]
16
16
  _vapor_pressure_constants = [78.441,-6128.10,-8.5766,6.85E-06, 2]
17
- _enthalpy_constants = [4.3252E-7, 0.37688, 250.33,3.4528, 0] # Placeholder for enthalpy constants
17
+ _enthalpy_constants = [4.3252E-7, 0.37688, 0,0, 0] # Placeholder for enthalpy constants
@@ -14,4 +14,4 @@ class ChloroBenzene(Component):
14
14
  _viscosity_constants = [0.15772,540.5,-1.6075,1.50E+14,-6.00E+00]
15
15
  _thermal_conductivity_constants = [0.1841, -0.0001917,0,0,0]
16
16
  _vapor_pressure_constants = [54.144,-6244.40,-4.5343,4.70E-18, 6]
17
- _enthalpy_constants = [5.148E-7,0.36614, 227.95,4.3707, 0] # Placeholder for enthalpy constants
17
+ _enthalpy_constants = [5.148E-7,0.36614, 0,0, 0] # Placeholder for enthalpy constants
@@ -14,4 +14,4 @@ class Chloroform(Component):
14
14
  _viscosity_constants = [-14.109,1049.2,0.5377,0,0]
15
15
  _thermal_conductivity_constants = [0.1778, -0.0002023,0,0,0]
16
16
  _vapor_pressure_constants = [146.43,-7792.30,-20.614,2.46E-02, 1]
17
- _enthalpy_constants = [4.186E-7,0.3584, 209.63,3.5047, 0] # Placeholder for enthalpy constants
17
+ _enthalpy_constants = [4.186E-7,0.3584, 0,0, 0] # Placeholder for enthalpy constants
@@ -14,4 +14,4 @@ class ChloroMethane(Component):
14
14
  _viscosity_constants = [-25.132,1381.9,2.0811,-4.50E-27,10]
15
15
  _thermal_conductivity_constants = [0.41067, -0.0008478,0,0,0]
16
16
  _vapor_pressure_constants = [64.697,-4048.10,-6.8066,1.04E-05, 2]
17
- _enthalpy_constants = [2.9745E-7,0.353, 175.43,2.452, 0] # Placeholder for enthalpy constants
17
+ _enthalpy_constants = [2.9745E-7,0.353, 0,0, 0] # Placeholder for enthalpy constants
@@ -14,4 +14,4 @@ class Cyanogen(Component):
14
14
  _viscosity_constants = [-12.086,994.23,0,0,0]
15
15
  _thermal_conductivity_constants = [0.4685, -0.00086594,0,0,0]
16
16
  _vapor_pressure_constants = [81.565,-4808.90,-9.3748,1.39E-05, 2]
17
- _enthalpy_constants = [3.384E-7,0.3707, 245.25,2.3803, 0] # Placeholder for enthalpy constants
17
+ _enthalpy_constants = [3.384E-7,0.3707, 0,0, 0] # Placeholder for enthalpy constants
@@ -14,4 +14,4 @@ class Cyclohexane(Component):
14
14
  _viscosity_constants = [-33.763,2497.2,3.2236,0,0]
15
15
  _thermal_conductivity_constants = [0.19813, -0.0002505,0,0,0]
16
16
  _vapor_pressure_constants = [51.087,-5226.40,-4.2278,19.76E-18, 6]
17
- _enthalpy_constants = [4.4902E-7,0.39881, 279.69,3.392, 0] # Placeholder for enthalpy constants
17
+ _enthalpy_constants = [4.4902E-7,0.39881, 0,0, 0] # Placeholder for enthalpy constants
@@ -15,3 +15,13 @@ class Ethane(Component):
15
15
  _thermal_conductivity_constants = [0.35758, -0.0011458,6.19E-07,0,0]
16
16
  _vapor_pressure_constants = [51.857,-2598.70,-5.1283,1.49E-05, 2]
17
17
  _enthalpy_constants = [2.1091E-7,0.60646, -0.55492,0.32799, 0] # Placeholder for enthalpy constants
18
+
19
+ #@PropertyMethod
20
+ def specific_heat(self) -> SpecificHeat:
21
+ if self._specific_heat is not None:
22
+ return self._specific_heat
23
+ T = self.temperature.value
24
+ Tr = T / self._critical_temperature.value
25
+ t = 1 - Tr
26
+ cp = (self._sepcific_heat_constants[0] / t ) + (self._sepcific_heat_constants[1]) - ( 2 * self._sepcific_heat_constants[0] * self._sepcific_heat_constants[2] * t) - (self._sepcific_heat_constants[0] * self._sepcific_heat_constants[3] * (t**2)) - ((self._sepcific_heat_constants[2]**2)*(t**3)/3) - ((self._sepcific_heat_constants[2] * self._sepcific_heat_constants[3] * (t**4))/2) - ((self._sepcific_heat_constants[0]**2) * (t**5)/5)
27
+ return SpecificHeat(cp, "J/kgK")
@@ -17,4 +17,4 @@ class Ethanol(Component):
17
17
  _viscosity_constants = [7.875, 781.98, -3.0418, 0, 0]
18
18
  _thermal_conductivity_constants = [0.2468, -0.000264, 0,0,0]
19
19
  _vapor_pressure_constants = [73.304, -7122.3, -7.1424, 0.0000028853, 2]
20
- _enthalpy_constants = [5.5789E-7, 0.31245, 159.05, 4.9694, 0] # Placeholder for enthalpy constants
20
+ _enthalpy_constants = [5.5789E-7, 0.31245, 0, 0, 0] # Placeholder for enthalpy constants
@@ -14,4 +14,4 @@ class EthlylAcetate(Component):
14
14
  _viscosity_constants = [14.354,-154.6,-3.7887,0,0]
15
15
  _thermal_conductivity_constants = [0.2501, -0.0003563,0,0,0]
16
16
  _vapor_pressure_constants = [66.824,-6227.60,-6.41,1.79E-17, 6]
17
- _enthalpy_constants = [4.933E-7,0.3847, 189.6,4.149, 0] # Placeholder for enthalpy constants
17
+ _enthalpy_constants = [4.933E-7,0.3847, 0,0, 0] # Placeholder for enthalpy constants
@@ -0,0 +1,17 @@
1
+ from .base import Component
2
+ from processpi.units import *
3
+ class Ethlylene(Component):
4
+ name = "Ethlylene"
5
+ formula = "C2H4"
6
+ molecular_weight = 28.053
7
+ _critical_temperature = Temperature(282.34, "K")
8
+ _critical_pressure = Pressure(5.41, "MPa")
9
+ _critical_volume = Volume(0.131, "m3") # Placeholder for critical volume per Kmole
10
+ _critical_zc = 0.281 # Placeholder for critical compressibility factor
11
+ _critical_acentric_factor = 0.0862 # Placeholder for critical acentric factor
12
+ _density_constants = [2.0961,0.27657,282.34,0.29174]
13
+ _specific_heat_constants = [247390,-4428,40.936,-0.1697,0.00026816]
14
+ _viscosity_constants = [1.8878,78.865,-2.1554,0,0]
15
+ _thermal_conductivity_constants = [0.4194,-0.001591,0.000001306,0,0]
16
+ _vapor_pressure_constants = [53.963,-2443,-5.5643,0.000019079,2]
17
+ _enthalpy_constants = [1.8844E-7,0.36485, 0,0, 0] # Placeholder for enthalpy constants
@@ -0,0 +1,17 @@
1
+ from .base import Component
2
+ from processpi.units import *
3
+ class Fluorine(Component):
4
+ name = "Fluorine"
5
+ formula = "F2"
6
+ molecular_weight = 37.997
7
+ _critical_temperature = Temperature(144.12, "K")
8
+ _critical_pressure = Pressure(5.172, "MPa")
9
+ _critical_volume = Volume(0.066547, "m3") # Placeholder for critical volume per Kmole
10
+ _critical_zc = 0.287 # Placeholder for critical compressibility factor
11
+ _critical_acentric_factor = 0.053 # Placeholder for critical acentric factor
12
+ _density_constants = [4.2895,0.28587,144.12,0.28776]
13
+ _specific_heat_constants = [-94585,7529.9,-139.6,1.1301,-0.0033241]
14
+ _viscosity_constants = [8.18,-75.6,-3.5148,0,0]
15
+ _thermal_conductivity_constants = [0.2758,-0.0016297,0,0,0]
16
+ _vapor_pressure_constants = [42.393,-1103.3,-4.1203,0.000057815,2]
17
+ _enthalpy_constants = [0.88757E-7,0.34072, 0,0, 0] # Placeholder for enthalpy constants
@@ -0,0 +1,17 @@
1
+ from .base import Component
2
+ from processpi.units import *
3
+ class FluoroBenzene(Component):
4
+ name = "Fluorobenzene"
5
+ formula = "C6H5F"
6
+ molecular_weight = 96.102
7
+ _critical_temperature = Temperature(560.09, "K")
8
+ _critical_pressure = Pressure(4.551, "MPa")
9
+ _critical_volume = Volume(0.269, "m3") # Placeholder for critical volume per Kmole
10
+ _critical_zc = 0.263 # Placeholder for critical compressibility factor
11
+ _critical_acentric_factor = 0.2472 # Placeholder for critical acentric factor
12
+ _density_constants = [1.0146,0.27277,560.09,0.28291]
13
+ _specific_heat_constants = [-991200,11734,-40.669,0.047333,0]
14
+ _viscosity_constants = [-10.064,1058.7,-0.17162,0,0]
15
+ _thermal_conductivity_constants = [0.20962,-0.00028034,0,0,0]
16
+ _vapor_pressure_constants = [51.915,-5439,-4.2896,8.7527E-18,6]
17
+ _enthalpy_constants = [4.582E-7,0.3717, 0,0, 0] # Placeholder for enthalpy constants
@@ -0,0 +1,17 @@
1
+ from .base import Component
2
+ from processpi.units import *
3
+ class FormicAcid(Component):
4
+ name = "Formic Acid"
5
+ formula = "CH2O2"
6
+ molecular_weight = 46.026
7
+ _critical_temperature = Temperature(588, "K")
8
+ _critical_pressure = Pressure(5.81, "MPa")
9
+ _critical_volume = Volume(0.125, "m3") # Placeholder for critical volume per Kmole
10
+ _critical_zc = 0.149 # Placeholder for critical compressibility factor
11
+ _critical_acentric_factor = 0.3173 # Placeholder for critical acentric factor
12
+ _density_constants = [1.938,0.24225,588,0.24435]
13
+ _specific_heat_constants = [78060,71.54,0,0,0]
14
+ _viscosity_constants = [-48.529,3394.7,5.3903,0,0]
15
+ _thermal_conductivity_constants = [0.302,-0.000108,0,0,0]
16
+ _vapor_pressure_constants = [50.323,-5378.2,-4.203,0.0000034697,2]
17
+ _enthalpy_constants = [2.3195E-7,1.9091,-5.0003,3.2641, 0] # Placeholder for enthalpy constants
@@ -17,4 +17,4 @@ class Methanol(Component):
17
17
  _viscosity_constants = [-25.317, 1789.2, 2.069, 0, 0]
18
18
  _thermal_conductivity_constants = [0.2837, -0.000281, 0,0,0]
19
19
  _vapor_pressure_constants = [82.718, -6904.5, -8.8622, 0.0000074664, 2]
20
- _enthalpy_constants = [5.0451e-7, 0.33594, 175.47, 4.3825, 0] # Placeholder for enthalpy constants
20
+ _enthalpy_constants = [5.0451e-7, 0.33594, 0, 0, 0] # Placeholder for enthalpy constants
@@ -16,3 +16,13 @@ class Oil(Component):
16
16
  _thermal_conductivity_constants = [0.18, -0.00025, 0, 0, 0]
17
17
  _vapor_pressure_constants = [75, -7000, -7.2, 0.000007, 2]
18
18
  _enthalpy_constants = [4.5E-7, 0.34, 200, 3.6, 0]
19
+
20
+ def httype(self):
21
+ P = self.pressure.to("Pa").value
22
+ Pvap = self.vapor_pressure().to("Pa").value
23
+ if P < Pvap:
24
+ httype = "heavyoil"
25
+ else:
26
+ httype = "lightoil"
27
+ return httype
28
+
@@ -5,6 +5,8 @@ class OrganicLiquid(Component):
5
5
  name = "Organic Liquid"
6
6
  formula = "R-CH"
7
7
  molecular_weight = 100.0
8
+ httype = "organicliquid"
9
+ p_h = "liquid"
8
10
  _critical_temperature = Temperature(550, "K")
9
11
  _critical_pressure = Pressure(4.5, "MPa")
10
12
  _critical_volume = Volume(0.25, "m3")
@@ -16,3 +18,12 @@ class OrganicLiquid(Component):
16
18
  _thermal_conductivity_constants = [0.2, -0.0003, 0, 0, 0]
17
19
  _vapor_pressure_constants = [70, -6000, -7.0, 0.000006, 2]
18
20
  _enthalpy_constants = [4.2E-7, 0.3, 180, 3.5, 0]
21
+
22
+ def httype(self):
23
+ P = self.pressure.to("Pa").value
24
+ Pvap = self.vapor_pressure().to("Pa").value
25
+ if P < Pvap:
26
+ httype = "organicvapour"
27
+ else:
28
+ httype = "organicliquid"
29
+ return httype
@@ -18,5 +18,14 @@ class Toluene(Component):
18
18
  _viscosity_constants = [-226.08, 6805.7,37.542,-0.060853, 1]
19
19
  _thermal_conductivity_constants = [0.20463, -0.00024252, 0,0,0]
20
20
  _vapor_pressure_constants = [76.945, -6729.8, -8.179, 0.0000053017, 2]
21
- _enthalpy_constants = [4.9507E-7, 0.37742, 178.18, 4.3246, 0] # Placeholder for enthalpy constants
22
-
21
+ _enthalpy_constants = [4.9507E-7, 0.37742, 0, 0, 0] # Placeholder for enthalpy constants
22
+
23
+ def httype(self):
24
+ P = self.pressure.to("Pa").value
25
+ Pvap = self.vapor_pressure().to("Pa").value
26
+ if P < Pvap:
27
+ httype = "organicvapour"
28
+ else:
29
+ httype = "organicliquid"
30
+ return httype
31
+
@@ -0,0 +1,2 @@
1
+ from .material import MaterialStream
2
+ from .energy import EnergyStream
@@ -0,0 +1,67 @@
1
+ from ..units import Power
2
+ from typing import Dict, Optional, Union
3
+
4
+
5
+ class EnergyStream:
6
+ """
7
+ Represents an energy stream (heat or work transfer).
8
+ Can bind to equipment and log Q/W duties automatically.
9
+
10
+ Notes
11
+ -----
12
+ * Positive duty = energy input (to equipment)
13
+ * Negative duty = energy output (from equipment)
14
+ * Duties are stored with units (`Power`) to ensure consistency.
15
+ """
16
+
17
+ def __init__(self, name: str):
18
+ self.name = name
19
+ self.log: list[Dict[str, Union[str, Power]]] = [] # {equipment, tag, duty}
20
+
21
+ def bind_equipment(self, equipment) -> None:
22
+ """Optionally keep reference to equipment if needed later."""
23
+ self.log.append({
24
+ "equipment": equipment.name,
25
+ "tag": "bind",
26
+ "duty": Power(0.0, "W")
27
+ })
28
+
29
+ def record(self, duty: Union[float, Power], tag: str, equipment: str) -> None:
30
+ """
31
+ Log an energy duty.
32
+
33
+ Parameters
34
+ ----------
35
+ duty : float | Power
36
+ Energy duty (W). If float is given, assumed in watts.
37
+ tag : str
38
+ Label such as 'Q_in', 'Q_out', 'W_in', 'W_out'.
39
+ equipment : str
40
+ Name of equipment associated with this duty.
41
+ """
42
+ if isinstance(duty, (int, float)):
43
+ duty = Power(duty, "W")
44
+
45
+ self.log.append({
46
+ "equipment": equipment,
47
+ "tag": tag,
48
+ "duty": duty
49
+ })
50
+
51
+ def total_duty(self, tag: Optional[str] = None) -> Power:
52
+ """
53
+ Return cumulative duty as a Power object.
54
+ Optionally filter by tag.
55
+ """
56
+ duties = (
57
+ [event["duty"] for event in self.log if tag is None or event["tag"] == tag]
58
+ )
59
+ total_value = sum(d.value for d in duties)
60
+ return Power(total_value, "W")
61
+
62
+ def __repr__(self) -> str:
63
+ return (
64
+ f"EnergyStream(name={self.name}, "
65
+ f"total={self.total_duty()}, "
66
+ f"entries={len(self.log)})"
67
+ )