processpi 0.0.0__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 (104) hide show
  1. processpi-0.0.0/LICENSE +21 -0
  2. processpi-0.0.0/PKG-INFO +5 -0
  3. processpi-0.0.0/README.md +174 -0
  4. processpi-0.0.0/processpi/__init__.py +1 -0
  5. processpi-0.0.0/processpi/calculations/__init__.py +3 -0
  6. processpi-0.0.0/processpi/calculations/base.py +55 -0
  7. processpi-0.0.0/processpi/calculations/engine.py +69 -0
  8. processpi-0.0.0/processpi/calculations/fluids/__init__.py +13 -0
  9. processpi-0.0.0/processpi/calculations/fluids/cool_brook_white.py +49 -0
  10. processpi-0.0.0/processpi/calculations/fluids/flow_type.py +37 -0
  11. processpi-0.0.0/processpi/calculations/fluids/optimium_pipe_dia.py +45 -0
  12. processpi-0.0.0/processpi/calculations/fluids/pressure_drop_darcy.py +25 -0
  13. processpi-0.0.0/processpi/calculations/fluids/pressure_drop_fanning.py +25 -0
  14. processpi-0.0.0/processpi/calculations/fluids/pump_power.py +34 -0
  15. processpi-0.0.0/processpi/calculations/fluids/reynolds_number.py +24 -0
  16. processpi-0.0.0/processpi/calculations/fluids/velocity.py +36 -0
  17. processpi-0.0.0/processpi/calculations/heat_transfer/__init__.py +4 -0
  18. processpi-0.0.0/processpi/calculations/heat_transfer/conduction_heat_loss.py +26 -0
  19. processpi-0.0.0/processpi/calculations/heat_transfer/convection_heat_loss.py +23 -0
  20. processpi-0.0.0/processpi/calculations/heat_transfer/heat_exchanger_area.py +26 -0
  21. processpi-0.0.0/processpi/calculations/mass_transfer/__init__.py +3 -0
  22. processpi-0.0.0/processpi/calculations/mass_transfer/distillation_stage_count.py +346 -0
  23. processpi-0.0.0/processpi/calculations/mass_transfer/drying_rate.py +132 -0
  24. processpi-0.0.0/processpi/calculations/reaction_engineering/__init__.py +4 -0
  25. processpi-0.0.0/processpi/calculations/reaction_engineering/catalyst_activity.py +109 -0
  26. processpi-0.0.0/processpi/calculations/reaction_engineering/reaction_rate.py +143 -0
  27. processpi-0.0.0/processpi/calculations/reaction_engineering/residence_time.py +80 -0
  28. processpi-0.0.0/processpi/calculations/thermodynamics/__init__.py +4 -0
  29. processpi-0.0.0/processpi/calculations/thermodynamics/enthalpy_change.py +23 -0
  30. processpi-0.0.0/processpi/calculations/thermodynamics/entropy_change.py +25 -0
  31. processpi-0.0.0/processpi/calculations/thermodynamics/heat_of_vaporization.py +20 -0
  32. processpi-0.0.0/processpi/components/__init__.py +5 -0
  33. processpi-0.0.0/processpi/components/aceticacid.py +19 -0
  34. processpi-0.0.0/processpi/components/acetone.py +20 -0
  35. processpi-0.0.0/processpi/components/acrylicacid.py +19 -0
  36. processpi-0.0.0/processpi/components/air.py +20 -0
  37. processpi-0.0.0/processpi/components/ammonia.py +19 -0
  38. processpi-0.0.0/processpi/components/base.py +85 -0
  39. processpi-0.0.0/processpi/components/benzene.py +40 -0
  40. processpi-0.0.0/processpi/components/benzoicacid.py +19 -0
  41. processpi-0.0.0/processpi/components/bromine.py +19 -0
  42. processpi-0.0.0/processpi/components/butane.py +19 -0
  43. processpi-0.0.0/processpi/components/carbondioxide.py +19 -0
  44. processpi-0.0.0/processpi/components/carbonmonoxide.py +17 -0
  45. processpi-0.0.0/processpi/components/carbontetrachloride.py +17 -0
  46. processpi-0.0.0/processpi/components/chlorine.py +17 -0
  47. processpi-0.0.0/processpi/components/chlorobenzene.py +17 -0
  48. processpi-0.0.0/processpi/components/chloroform.py +17 -0
  49. processpi-0.0.0/processpi/components/chloromethane.py +17 -0
  50. processpi-0.0.0/processpi/components/cyanogen.py +17 -0
  51. processpi-0.0.0/processpi/components/cyclohexene.py +17 -0
  52. processpi-0.0.0/processpi/components/ethane.py +17 -0
  53. processpi-0.0.0/processpi/components/ethanol.py +20 -0
  54. processpi-0.0.0/processpi/components/ethylacetate.py +17 -0
  55. processpi-0.0.0/processpi/components/methanol.py +20 -0
  56. processpi-0.0.0/processpi/components/toluene.py +22 -0
  57. processpi-0.0.0/processpi/components/water.py +24 -0
  58. processpi-0.0.0/processpi/pipelines/__init__.py +0 -0
  59. processpi-0.0.0/processpi/pipelines/base.py +76 -0
  60. processpi-0.0.0/processpi/pipelines/design_rules.py +91 -0
  61. processpi-0.0.0/processpi/pipelines/engine.py +718 -0
  62. processpi-0.0.0/processpi/pipelines/equipment.py +32 -0
  63. processpi-0.0.0/processpi/pipelines/fittings.py +76 -0
  64. processpi-0.0.0/processpi/pipelines/insulation.py +0 -0
  65. processpi-0.0.0/processpi/pipelines/materials.py +102 -0
  66. processpi-0.0.0/processpi/pipelines/network.py +277 -0
  67. processpi-0.0.0/processpi/pipelines/pipelineresults.py +95 -0
  68. processpi-0.0.0/processpi/pipelines/pipes.py +84 -0
  69. processpi-0.0.0/processpi/pipelines/pumps.py +83 -0
  70. processpi-0.0.0/processpi/pipelines/selection.py +89 -0
  71. processpi-0.0.0/processpi/pipelines/standards.py +279 -0
  72. processpi-0.0.0/processpi/pipelines/vessel.py +34 -0
  73. processpi-0.0.0/processpi/units/__init__.py +38 -0
  74. processpi-0.0.0/processpi/units/area.py +51 -0
  75. processpi-0.0.0/processpi/units/base.py +57 -0
  76. processpi-0.0.0/processpi/units/density.py +47 -0
  77. processpi-0.0.0/processpi/units/diameter.py +58 -0
  78. processpi-0.0.0/processpi/units/dimensionless.py +42 -0
  79. processpi-0.0.0/processpi/units/heat_flux.py +46 -0
  80. processpi-0.0.0/processpi/units/heat_of_vaporization.py +47 -0
  81. processpi-0.0.0/processpi/units/heat_transfer_coefficient.py +45 -0
  82. processpi-0.0.0/processpi/units/length.py +47 -0
  83. processpi-0.0.0/processpi/units/mass.py +50 -0
  84. processpi-0.0.0/processpi/units/mass_flowrate.py +50 -0
  85. processpi-0.0.0/processpi/units/pressure.py +57 -0
  86. processpi-0.0.0/processpi/units/specific_heat.py +47 -0
  87. processpi-0.0.0/processpi/units/strings.py +31 -0
  88. processpi-0.0.0/processpi/units/temperature.py +51 -0
  89. processpi-0.0.0/processpi/units/thermal_conductivity.py +45 -0
  90. processpi-0.0.0/processpi/units/thermal_resistance.py +46 -0
  91. processpi-0.0.0/processpi/units/velocity.py +45 -0
  92. processpi-0.0.0/processpi/units/viscosity.py +78 -0
  93. processpi-0.0.0/processpi/units/volume.py +50 -0
  94. processpi-0.0.0/processpi/units/volumetric_flowrate.py +52 -0
  95. processpi-0.0.0/processpi.egg-info/PKG-INFO +5 -0
  96. processpi-0.0.0/processpi.egg-info/SOURCES.txt +102 -0
  97. processpi-0.0.0/processpi.egg-info/dependency_links.txt +1 -0
  98. processpi-0.0.0/processpi.egg-info/top_level.txt +1 -0
  99. processpi-0.0.0/pyproject.toml +1 -0
  100. processpi-0.0.0/setup.cfg +4 -0
  101. processpi-0.0.0/tests/test_engine_network.py +109 -0
  102. processpi-0.0.0/tests/test_network.py +77 -0
  103. processpi-0.0.0/tests/test_pipeline_network.py +85 -0
  104. processpi-0.0.0/tests/test_variables.py +93 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Raviteja Varma Nadimpalli
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,5 @@
1
+ Metadata-Version: 2.4
2
+ Name: processpi
3
+ Version: 0.0.0
4
+ License-File: LICENSE
5
+ Dynamic: license-file
@@ -0,0 +1,174 @@
1
+
2
+ <p align="center">
3
+ <img src="https://img.shields.io/badge/Process_PI-🧪-blue?style=for-the-badge&logo=python" alt="Process PI Logo" height="40"/>
4
+ </p>
5
+
6
+ <h1 align="center">Process PI</h1>
7
+
8
+ <p align="center">
9
+ <strong>Python toolkit for chemical engineering simulations, equipment design, and unit conversions</strong>
10
+ </p>
11
+
12
+ <p align="center">
13
+ <img src="https://img.shields.io/badge/🛠_Unit_Conversions-blue?style=for-the-badge" alt="Unit Conversions"/>
14
+ <img src="https://img.shields.io/badge/💧_Components-blue?style=for-the-badge" alt="Components"/>
15
+ <img src="https://img.shields.io/badge/🏭_Pumps-Pipelines-blue?style=for-the-badge" alt="Pumps & Pipelines"/>
16
+ <img src="https://img.shields.io/badge/♨️_Heat_Exchangers-blue?style=for-the-badge" alt="Heat Exchangers"/>
17
+ <img src="https://img.shields.io/badge/⚗️_Chemical_Flasks-blue?style=for-the-badge" alt="Chemical Flasks"/>
18
+ </p>
19
+
20
+ <p align="center">
21
+ <a href="https://pypi.org/project/processpi/">
22
+ <img src="https://img.shields.io/pypi/v/processpi?style=for-the-badge&logo=pypi" alt="PyPI Version"/>
23
+ </a>
24
+ <a href="https://github.com/varma666/ProcessPi/stargazers">
25
+ <img src="https://img.shields.io/github/stars/varma666/ProcessPi?style=for-the-badge&logo=github" alt="GitHub Stars"/>
26
+ </a>
27
+ <a href="https://github.com/varma666/ProcessPi/network/members">
28
+ <img src="https://img.shields.io/github/forks/varma666/ProcessPi?style=for-the-badge&logo=github" alt="GitHub Forks"/>
29
+ </a>
30
+ <a href="https://github.com/varma666/ProcessPi/actions">
31
+ <img src="https://img.shields.io/github/actions/workflow/status/varma666/ProcessPi/python-package.yml?branch=main&style=for-the-badge" alt="Build Status"/>
32
+ </a>
33
+ </p>
34
+
35
+ <p align="center">
36
+ <em>Built with ❤️ for chemical engineers.</em>
37
+ </p>
38
+
39
+ ---
40
+
41
+ ## 📖 Overview
42
+
43
+ **Process PI** is a cross-platform **Python toolkit** for chemical engineering equipment design, simulations, and unit conversions.
44
+ It provides a **modular framework** for learning, experimenting, and simulating process systems.
45
+
46
+ ---
47
+
48
+ ## 📑 Table of Contents
49
+ - [Features](#-features)
50
+ - [Installation](#-installation)
51
+ - [Getting Started](#-getting-started)
52
+ - [Unit Conversions](#1-unit-conversions)
53
+ - [Components](#2-components)
54
+ - [Pipeline Network Analysis](#3-pipeline-network-analysis)
55
+ - [Roadmap](#-roadmap)
56
+ - [License](#-license)
57
+
58
+ ---
59
+
60
+ ## ⚡ Features
61
+
62
+ - ⚙️ **Unit Conversion System** – Seamless conversion across SI, CGS, and Imperial units.
63
+ - 🧪 **Components Database (Work in Progress)** – Educational thermodynamic properties of 25+ components (Water, Ethanol, CO₂, etc.), expanding regularly.
64
+ - 🏭 **Equipment Design Modules (WIP)** – Pumps, pipelines, heat exchangers, reactors, and more.
65
+ - 🌐 **Pipeline Networks** – Analyze single pipes and complex networked systems, calculate flow, pressure drops, velocity, and Reynolds numbers.
66
+ - 📊 **Simulation Tools (Upcoming)** – Steady-state/dynamic process analysis, flow optimization, and energy management.
67
+
68
+ ---
69
+
70
+ ## 📦 Installation
71
+
72
+ Install via **PyPI**:
73
+
74
+ ```bash
75
+ pip install processpi
76
+ ```
77
+
78
+ ---
79
+
80
+ ## 🚀 Getting Started
81
+
82
+ ### 1. Unit Conversions
83
+
84
+ Easily convert between SI, CGS, and Imperial units.
85
+
86
+ ```python
87
+ from processpi.units import Length, Pressure, Temperature
88
+
89
+ # Length
90
+ l1 = Length(1.5, "m")
91
+ print(l1.to("ft")) # Output in feet
92
+
93
+ # Pressure
94
+ p = Pressure(2, "bar")
95
+ print(p.to("Pa")) # Output in Pascals
96
+
97
+ # Temperature
98
+ t = Temperature(100, "C")
99
+ print(t.to("K")) # Output in Kelvin
100
+ ```
101
+
102
+ ---
103
+
104
+ ### 2. Components
105
+
106
+ Access educational thermodynamic properties of chemical components.
107
+
108
+ ```python
109
+ from processpi.components import Water, Ethanol
110
+
111
+ water = Water()
112
+ ethanol = Ethanol()
113
+
114
+ print(f"Water density: {water.density()} kg/m³")
115
+ print(f"Ethanol viscosity: {ethanol.viscosity()} Pa·s")
116
+ ```
117
+
118
+ ⚠️ **Note**: Component data is provided for **educational and demonstration purposes**. The database is continuously expanding.
119
+
120
+ ---
121
+
122
+ ### 3. Pipeline Network Analysis
123
+
124
+ Perform hydraulic calculations for pipelines and network systems.
125
+
126
+ ```python
127
+ from processpi.pipelines.engine import PipelineEngine
128
+ from processpi.pipelines.pipes import Pipe
129
+ from processpi.components import Water
130
+ from processpi.units import VolumetricFlowRate, Diameter, Length
131
+
132
+ pipe = Pipe(
133
+ internal_diameter=Diameter(50, "mm"),
134
+ length=Length(10, "m")
135
+ )
136
+
137
+ engine = PipelineEngine()
138
+ engine.fit(
139
+ flowrate=VolumetricFlowRate(0.001, "m^3/s"),
140
+ pipe=pipe,
141
+ fluid=Water()
142
+ )
143
+
144
+ results = engine.run()
145
+ engine.summary()
146
+ ```
147
+
148
+ #### Planned Enhancements
149
+
150
+ - Support for branching and parallel pipeline networks
151
+ - Dynamic flow simulations and transient analysis
152
+ - Cost and energy optimization tools for network layouts
153
+
154
+ ---
155
+
156
+ ## 🛣 Roadmap
157
+
158
+ - Expand the Components Database to 50+ chemicals and fluids
159
+ - Add steady-state and dynamic simulation modules
160
+ - Implement energy optimization and cost analysis tools
161
+ - Build a GUI/web interface for interactive simulations
162
+ - Enable integration with real process data and sensor-driven workflows
163
+
164
+ ---
165
+
166
+ ## 📜 License
167
+
168
+ This project is licensed under the **MIT License** – see the LICENSE file for details.
169
+
170
+ ---
171
+
172
+ <p align="center">
173
+ <em>Process PI – Built with ❤️ for chemical engineers.</em>
174
+ </p>
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
@@ -0,0 +1,3 @@
1
+ from .engine import CalculationEngine
2
+ from .base import CalculationBase
3
+ __all__ = ["CalculationEngine", "CalculationBase"]
@@ -0,0 +1,55 @@
1
+ # base.py
2
+ from abc import ABC, abstractmethod
3
+
4
+ class CalculationBase(ABC):
5
+ """
6
+ Abstract base for all calculation modules in processpi.
7
+ """
8
+
9
+ def __init__(self, **kwargs):
10
+ """
11
+ Store all input parameters as attributes.
12
+ """
13
+ self.inputs = kwargs
14
+ self.validate_inputs()
15
+
16
+ @abstractmethod
17
+ def validate_inputs(self):
18
+ """
19
+ Validate inputs and raise ValueError if invalid.
20
+ """
21
+ pass
22
+
23
+ @abstractmethod
24
+ def calculate(self):
25
+ """
26
+ Perform the calculation and return results as a dict.
27
+ """
28
+ pass
29
+
30
+ def get_inputs(self):
31
+ """
32
+ Return a dictionary of original inputs.
33
+ """
34
+ return self.inputs
35
+
36
+ def to_dict(self):
37
+ """
38
+ Return a dictionary with both inputs and results.
39
+ """
40
+ result = self.calculate()
41
+ return {
42
+ "inputs": self.inputs,
43
+ "results": result
44
+ }
45
+ @staticmethod
46
+ def _get_value(x, name):
47
+ """Return numeric value if x is numeric or x.value if it's a Variable-like object."""
48
+ if hasattr(x, "value"):
49
+ return x.value
50
+ try:
51
+ # accept numpy/scalar numbers
52
+ return float(x)
53
+ except Exception:
54
+ raise TypeError(f"Could not interpret {name} value: {x!r}")
55
+
@@ -0,0 +1,69 @@
1
+ """
2
+ engine.py
3
+ ---------
4
+ This module will act as the central engine for performing calculations in ProcessPI.
5
+
6
+ Responsibilities:
7
+ 1. Receive input parameters for a calculation.
8
+ 2. Select the correct calculation class/function based on the requested operation.
9
+ 3. Handle validation and error reporting.
10
+ 4. Return results in a standardized format.
11
+ """
12
+
13
+ from typing import Any, Dict, Type
14
+ from processpi.units import * # Import all unit classes
15
+ from processpi.calculations import * # Import all calculation classes
16
+
17
+ # Import calculation classes here
18
+ # Example: from processpi.calculations.heat_transfer import HeatTransferCalculator
19
+ # We'll add them as we implement each calculation
20
+ # For now, I'll just put placeholders for demonstration
21
+ # ----------------------------------------------------------------------
22
+
23
+ class CalculationEngine:
24
+ """
25
+ Central hub for all ProcessPI calculations.
26
+ """
27
+
28
+ def __init__(self):
29
+ """
30
+ Initializes the engine with a registry of available calculations.
31
+ The registry maps calculation names (strings) to their handler classes.
32
+ """
33
+ self.registry: Dict[str, Type] = {}
34
+
35
+ def register_calculation(self, name: str, calc_class: Type):
36
+ """
37
+ Register a new calculation class to the engine.
38
+
39
+ Args:
40
+ name (str): A unique string key for the calculation.
41
+ calc_class (Type): The class that implements the calculation logic.
42
+
43
+ Example:
44
+ engine.register_calculation("heat_transfer", HeatTransferCalculator)
45
+ """
46
+ self.registry[name] = calc_class
47
+
48
+ def calculate(self, name: str, **kwargs) -> Any:
49
+ """
50
+ Executes a calculation by name.
51
+
52
+ Args:
53
+ name (str): The registered name of the calculation.
54
+ **kwargs: All input parameters required for the calculation.
55
+
56
+ Returns:
57
+ Any: The result of the calculation.
58
+
59
+ Raises:
60
+ ValueError: If the calculation name is not registered.
61
+ """
62
+ if name not in self.registry:
63
+ raise ValueError(f"Calculation '{name}' not found in registry.")
64
+
65
+ calc_class = self.registry[name]
66
+ calc_instance = calc_class(**kwargs) # Pass all inputs to the class constructor
67
+ return calc_instance.calculate() # All calculation classes must have a `.calculate()` method
68
+
69
+
@@ -0,0 +1,13 @@
1
+ """ Init module for Process PI calculation engine."""
2
+
3
+ # Implementation will be added here.
4
+ from .pressure_drop_darcy import PressureDropDarcy
5
+ from .pressure_drop_fanning import PressureDropFanning
6
+ from .pump_power import PumpPower
7
+ from .reynolds_number import ReynoldsNumber
8
+ from .optimium_pipe_dia import OptimumPipeDiameter
9
+ from .velocity import FluidVelocity
10
+ from .cool_brook_white import ColebrookWhite
11
+ from .flow_type import TypeOfFlow
12
+
13
+ __all__ = ["PressureDropDarcy", "PressureDropFanning", "PumpPower", "ReynoldsNumber", "OptimumPipeDiameter", "FluidVelocity", "ColebrookWhite", "TypeOfFlow"]
@@ -0,0 +1,49 @@
1
+ from ..base import CalculationBase
2
+ from ...units import *
3
+
4
+ import math
5
+
6
+
7
+ class ColebrookWhite(CalculationBase):
8
+ """
9
+ Calculate Darcy friction factor using the Colebrook–White equation.
10
+
11
+ Formula (implicit):
12
+ 1/sqrt(f) = -2.0 * log10( (ε/D)/3.7 + 2.51/(Re*sqrt(f)) )
13
+
14
+ Notes:
15
+ - For laminar flow (Re < 2000), f = 64/Re.
16
+ - Solved iteratively for turbulent flow.
17
+ """
18
+
19
+ def validate_inputs(self):
20
+ required = ["reynolds_number", "diameter", "roughness"]
21
+ for key in required:
22
+ if key not in self.inputs:
23
+ raise ValueError(f"Missing required input: {key}")
24
+
25
+ def calculate(self):
26
+ Re = self._get_value(self.inputs["reynolds_number"], "reynolds_number")
27
+ D = self._get_value(self.inputs["diameter"], "diameter") / 1000 # m
28
+ eps = self._get_value(self.inputs["roughness"], "roughness") # m
29
+
30
+ # Laminar flow check
31
+ if Re < 2000:
32
+ f = 64.0 / Re
33
+ return Dimensionless(f)
34
+
35
+ # Initial guess
36
+ f = 0.02
37
+ tol = 1e-6
38
+ max_iter = 100
39
+
40
+ for _ in range(max_iter):
41
+ lhs = 1.0 / math.sqrt(f)
42
+ rhs = -2.0 * math.log10((eps / (3.7 * D)) + (2.51 / (Re * math.sqrt(f))))
43
+ new_f = 1.0 / (rhs**2)
44
+
45
+ if abs(new_f - f) < tol:
46
+ return Dimensionless(new_f)
47
+ f = new_f
48
+
49
+ return Dimensionless(f)
@@ -0,0 +1,37 @@
1
+ from ..base import CalculationBase
2
+ from ...units import *
3
+
4
+
5
+ class TypeOfFlow(CalculationBase):
6
+ """
7
+ Determine the type of flow based on Reynolds number.
8
+
9
+ Criteria:
10
+ - Laminar: Re < 2000
11
+ - Transitional: 2000 ≤ Re ≤ 4000
12
+ - Turbulent: Re > 4000
13
+
14
+ Inputs:
15
+ - reynolds_number (Re) [dimensionless]
16
+
17
+ Output:
18
+ - Flow type as a StringUnit
19
+ """
20
+
21
+ def validate_inputs(self):
22
+ required = ["reynolds_number"]
23
+ for key in required:
24
+ if key not in self.inputs:
25
+ raise ValueError(f"Missing required input: {key}")
26
+
27
+ def calculate(self):
28
+ Re = self._get_value(self.inputs["reynolds_number"], "reynolds_number")
29
+
30
+ if Re < 2000:
31
+ flow_type = "Laminar"
32
+ elif 2000 <= Re <= 4000:
33
+ flow_type = "Transitional"
34
+ else:
35
+ flow_type = "Turbulent"
36
+
37
+ return StringUnit(flow_type, "flow_type")
@@ -0,0 +1,45 @@
1
+ # processpi/calculations/fluids/optimum_pipe_diameter.py
2
+ from ..base import CalculationBase
3
+ from ...units import *
4
+ from ...pipelines.standards import get_nearest_diameter
5
+
6
+ class OptimumPipeDiameter(CalculationBase):
7
+ """
8
+ Calculate the optimum pipe diameter using empirical formula and map to nearest standard:
9
+
10
+ D_opt = 293 * (Q^0.53) * (ρ^-0.37)
11
+
12
+ Where:
13
+ - Q = Volumetric flow rate [m³/s]
14
+ - ρ = Fluid density [kg/m³]
15
+ - D_opt = Optimum diameter [mm]
16
+
17
+ Inputs:
18
+ - flow_rate (Q) [m³/s]
19
+ - density (ρ) [kg/m³]
20
+
21
+ Output:
22
+ - Nearest standard diameter [Diameter object, mm]
23
+ """
24
+
25
+ def validate_inputs(self):
26
+ required = ["flow_rate", "density"]
27
+ for key in required:
28
+ if key not in self.inputs:
29
+ raise ValueError(f"Missing required input: {key}")
30
+
31
+ def calculate(self):
32
+ # Get inputs
33
+ Q_volumetric = self._get_value(self.inputs["flow_rate"], "flow_rate") # m³/s
34
+ rho = self._get_value(self.inputs["density"], "density") # kg/m³
35
+
36
+ # Mass flow for formula
37
+ Q_mass = Q_volumetric * rho # kg/s
38
+
39
+ # Empirical formula for optimum diameter
40
+ D_opt = 293 * (Q_mass ** 0.53) * (rho ** -0.37) # mm
41
+ D_opt = Diameter(D_opt, "mm")
42
+
43
+ # Map to nearest standard diameter
44
+ nearest_std = get_nearest_diameter(D_opt)
45
+ return nearest_std
@@ -0,0 +1,25 @@
1
+ from ..base import CalculationBase
2
+ from ...units import *
3
+
4
+ class PressureDropDarcy(CalculationBase):
5
+ """
6
+ Calculate pressure drop using Darcy–Weisbach equation.
7
+ Formula:
8
+ ΔP = f * (L/D) * (ρ * v² / 2)
9
+ """
10
+
11
+ def validate_inputs(self):
12
+ required = ["friction_factor", "length", "diameter", "density", "velocity"]
13
+ for key in required:
14
+ if key not in self.inputs:
15
+ raise ValueError(f"Missing required input: {key}")
16
+
17
+ def calculate(self):
18
+ f = self._get_value(self.inputs["friction_factor"], "friction_factor")
19
+ L = self._get_value(self.inputs["length"], "length") # m
20
+ D = self._get_value(self.inputs["diameter"], "diameter") # m
21
+ rho = self._get_value(self.inputs["density"], "density") # kg/m³
22
+ v = self._get_value(self.inputs["velocity"], "velocity") # m/s
23
+
24
+ delta_P = f * (L / D) * (rho * v**2 / 2)
25
+ return Pressure(delta_P, "Pa") # Return result as Pressure object
@@ -0,0 +1,25 @@
1
+ from ..base import CalculationBase
2
+ from ...units import *
3
+
4
+ class PressureDropFanning(CalculationBase):
5
+ """
6
+ Calculate pressure drop using Fanning equation.
7
+ Formula:
8
+ ΔP = 4 * f * (L/D) * (ρ * v² / 2)
9
+ """
10
+
11
+ def validate_inputs(self):
12
+ required = ["friction_factor", "length", "diameter", "density", "velocity"]
13
+ for key in required:
14
+ if key not in self.inputs:
15
+ raise ValueError(f"Missing required input: {key}")
16
+
17
+ def calculate(self):
18
+ f = self._get_value(self.inputs["friction_factor"], "friction_factor")
19
+ L = self._get_value(self.inputs["length"], "length") # m
20
+ D = self._get_value(self.inputs["diameter"], "diameter") # m
21
+ rho = self._get_value(self.inputs["density"], "density") # kg/m³
22
+ v = self._get_value(self.inputs["velocity"], "velocity") # m/s
23
+
24
+ delta_P = 4 * f * (L / D) * (rho * v**2 / 2)
25
+ return Pressure(delta_P, "Pa") # Return result as Pressure object
@@ -0,0 +1,34 @@
1
+ from ..base import CalculationBase
2
+ from ...units import *
3
+
4
+ class PumpPower(CalculationBase):
5
+ """
6
+ Calculate pump power from flow rate, head, density, and efficiency.
7
+ """
8
+
9
+ def validate_inputs(self):
10
+ # Ensure required inputs are present and valid
11
+ required = ["flow_rate", "head", "density", "efficiency"]
12
+ for key in required:
13
+ if key not in self.inputs:
14
+ raise ValueError(f"Missing required input: {key}")
15
+ if not (0 < self.inputs["efficiency"] <= 1):
16
+ raise ValueError("Efficiency must be between 0 and 1")
17
+
18
+ def calculate(self):
19
+ g = 9.81 # m/s²
20
+ flow_rate = self._get_value(self.inputs["flow_rate"], "flow_rate") # m³/s
21
+ head = self._get_value(self.inputs["head"], "head") # m
22
+ density = self._get_value(self.inputs["density"], "density") # kg/m³
23
+ efficiency = self._get_value(self.inputs["efficiency"], "efficiency") # decimal
24
+
25
+ # Hydraulic Power (Watts)
26
+ hydraulic_power = density * g * flow_rate * head
27
+
28
+ # Shaft Power (Watts)
29
+ shaft_power = hydraulic_power / efficiency
30
+
31
+ return {
32
+ "hydraulic_power_W": hydraulic_power,
33
+ "shaft_power_W": shaft_power
34
+ }
@@ -0,0 +1,24 @@
1
+ from ..base import CalculationBase
2
+ from ...units import *
3
+
4
+ class ReynoldsNumber(CalculationBase):
5
+ """
6
+ Calculate Reynolds number for pipe flow.
7
+ Formula:
8
+ Re = (ρ * v * D) / μ
9
+ """
10
+
11
+ def validate_inputs(self):
12
+ required = ["density", "velocity", "diameter", "viscosity"]
13
+ for key in required:
14
+ if key not in self.inputs:
15
+ raise ValueError(f"Missing required input: {key}")
16
+
17
+ def calculate(self):
18
+ rho = self._get_value(self.inputs["density"], "density") # kg/m³
19
+ v = self._get_value(self.inputs["velocity"], "velocity") # m/s
20
+ D = self._get_value(self.inputs["diameter"], "diameter") # m
21
+ mu = self._get_value(self.inputs["viscosity"], "viscosity") # Pa·s
22
+
23
+ Re = rho * v * D / mu
24
+ return Dimensionless(Re)
@@ -0,0 +1,36 @@
1
+ from ..base import CalculationBase
2
+ from ...units import *
3
+
4
+ import math
5
+
6
+
7
+ class FluidVelocity(CalculationBase):
8
+ """
9
+ Calculate fluid velocity in a circular pipe.
10
+
11
+ Formula:
12
+ v = Q / A
13
+ = 4Q / (πD²)
14
+
15
+ Inputs:
16
+ - volumetric_flow_rate (Q) [m³/s]
17
+ - diameter (D) [m]
18
+
19
+ Output:
20
+ - velocity (v) [m/s]
21
+ """
22
+
23
+ def validate_inputs(self):
24
+ required = ["volumetric_flow_rate", "diameter"]
25
+ for key in required:
26
+ if key not in self.inputs:
27
+ raise ValueError(f"Missing required input: {key}")
28
+
29
+ def calculate(self):
30
+ Q = self._get_value(self.inputs["volumetric_flow_rate"], "volumetric_flow_rate") # m³/s
31
+ D = self._get_value(self.inputs["diameter"], "diameter") # m
32
+
33
+ A = math.pi * (D**2) / 4.0 # cross-sectional area [m²]
34
+ v = Q / A # velocity [m/s]
35
+
36
+ return Velocity(v, "m/s")
@@ -0,0 +1,4 @@
1
+ from .conduction_heat_loss import ConductionHeatLoss
2
+ from .convection_heat_loss import ConvectionHeatLoss
3
+ from .heat_exchanger_area import HeatExchangerArea
4
+ __all__ = ["ConductionHeatLoss", "ConvectionHeatLoss", "HeatExchangerArea"]
@@ -0,0 +1,26 @@
1
+ from ..base import CalculationBase
2
+ from ...units import *
3
+
4
+ class ConductionHeatLoss(CalculationBase):
5
+ """
6
+ Calculate steady-state conduction heat loss through a wall.
7
+ Formula:
8
+ Q = k * A * ΔT / L
9
+ """
10
+
11
+ def validate_inputs(self):
12
+ required = ["thermal_conductivity", "area", "temp_difference", "thickness"]
13
+ for key in required:
14
+ if key not in self.inputs:
15
+ raise ValueError(f"Missing required input: {key}")
16
+ if self.inputs["thickness"] <= 0:
17
+ raise ValueError("Wall thickness must be greater than 0")
18
+
19
+ def calculate(self):
20
+ k = self._get_value(self.inputs["thermal_conductivity"], "thermal_conductivity") # W/m·K
21
+ A = self._get_value(self.inputs["area"], "area") # m²
22
+ ΔT = self._get_value(self.inputs["temp_difference"], "temp_difference") # K
23
+ L = self._get_value(self.inputs["thickness"], "thickness") # m
24
+
25
+ Q = k * A * ΔT / L
26
+ return HeatFlux(Q, "W")