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.
- processpi-0.0.0/LICENSE +21 -0
- processpi-0.0.0/PKG-INFO +5 -0
- processpi-0.0.0/README.md +174 -0
- processpi-0.0.0/processpi/__init__.py +1 -0
- processpi-0.0.0/processpi/calculations/__init__.py +3 -0
- processpi-0.0.0/processpi/calculations/base.py +55 -0
- processpi-0.0.0/processpi/calculations/engine.py +69 -0
- processpi-0.0.0/processpi/calculations/fluids/__init__.py +13 -0
- processpi-0.0.0/processpi/calculations/fluids/cool_brook_white.py +49 -0
- processpi-0.0.0/processpi/calculations/fluids/flow_type.py +37 -0
- processpi-0.0.0/processpi/calculations/fluids/optimium_pipe_dia.py +45 -0
- processpi-0.0.0/processpi/calculations/fluids/pressure_drop_darcy.py +25 -0
- processpi-0.0.0/processpi/calculations/fluids/pressure_drop_fanning.py +25 -0
- processpi-0.0.0/processpi/calculations/fluids/pump_power.py +34 -0
- processpi-0.0.0/processpi/calculations/fluids/reynolds_number.py +24 -0
- processpi-0.0.0/processpi/calculations/fluids/velocity.py +36 -0
- processpi-0.0.0/processpi/calculations/heat_transfer/__init__.py +4 -0
- processpi-0.0.0/processpi/calculations/heat_transfer/conduction_heat_loss.py +26 -0
- processpi-0.0.0/processpi/calculations/heat_transfer/convection_heat_loss.py +23 -0
- processpi-0.0.0/processpi/calculations/heat_transfer/heat_exchanger_area.py +26 -0
- processpi-0.0.0/processpi/calculations/mass_transfer/__init__.py +3 -0
- processpi-0.0.0/processpi/calculations/mass_transfer/distillation_stage_count.py +346 -0
- processpi-0.0.0/processpi/calculations/mass_transfer/drying_rate.py +132 -0
- processpi-0.0.0/processpi/calculations/reaction_engineering/__init__.py +4 -0
- processpi-0.0.0/processpi/calculations/reaction_engineering/catalyst_activity.py +109 -0
- processpi-0.0.0/processpi/calculations/reaction_engineering/reaction_rate.py +143 -0
- processpi-0.0.0/processpi/calculations/reaction_engineering/residence_time.py +80 -0
- processpi-0.0.0/processpi/calculations/thermodynamics/__init__.py +4 -0
- processpi-0.0.0/processpi/calculations/thermodynamics/enthalpy_change.py +23 -0
- processpi-0.0.0/processpi/calculations/thermodynamics/entropy_change.py +25 -0
- processpi-0.0.0/processpi/calculations/thermodynamics/heat_of_vaporization.py +20 -0
- processpi-0.0.0/processpi/components/__init__.py +5 -0
- processpi-0.0.0/processpi/components/aceticacid.py +19 -0
- processpi-0.0.0/processpi/components/acetone.py +20 -0
- processpi-0.0.0/processpi/components/acrylicacid.py +19 -0
- processpi-0.0.0/processpi/components/air.py +20 -0
- processpi-0.0.0/processpi/components/ammonia.py +19 -0
- processpi-0.0.0/processpi/components/base.py +85 -0
- processpi-0.0.0/processpi/components/benzene.py +40 -0
- processpi-0.0.0/processpi/components/benzoicacid.py +19 -0
- processpi-0.0.0/processpi/components/bromine.py +19 -0
- processpi-0.0.0/processpi/components/butane.py +19 -0
- processpi-0.0.0/processpi/components/carbondioxide.py +19 -0
- processpi-0.0.0/processpi/components/carbonmonoxide.py +17 -0
- processpi-0.0.0/processpi/components/carbontetrachloride.py +17 -0
- processpi-0.0.0/processpi/components/chlorine.py +17 -0
- processpi-0.0.0/processpi/components/chlorobenzene.py +17 -0
- processpi-0.0.0/processpi/components/chloroform.py +17 -0
- processpi-0.0.0/processpi/components/chloromethane.py +17 -0
- processpi-0.0.0/processpi/components/cyanogen.py +17 -0
- processpi-0.0.0/processpi/components/cyclohexene.py +17 -0
- processpi-0.0.0/processpi/components/ethane.py +17 -0
- processpi-0.0.0/processpi/components/ethanol.py +20 -0
- processpi-0.0.0/processpi/components/ethylacetate.py +17 -0
- processpi-0.0.0/processpi/components/methanol.py +20 -0
- processpi-0.0.0/processpi/components/toluene.py +22 -0
- processpi-0.0.0/processpi/components/water.py +24 -0
- processpi-0.0.0/processpi/pipelines/__init__.py +0 -0
- processpi-0.0.0/processpi/pipelines/base.py +76 -0
- processpi-0.0.0/processpi/pipelines/design_rules.py +91 -0
- processpi-0.0.0/processpi/pipelines/engine.py +718 -0
- processpi-0.0.0/processpi/pipelines/equipment.py +32 -0
- processpi-0.0.0/processpi/pipelines/fittings.py +76 -0
- processpi-0.0.0/processpi/pipelines/insulation.py +0 -0
- processpi-0.0.0/processpi/pipelines/materials.py +102 -0
- processpi-0.0.0/processpi/pipelines/network.py +277 -0
- processpi-0.0.0/processpi/pipelines/pipelineresults.py +95 -0
- processpi-0.0.0/processpi/pipelines/pipes.py +84 -0
- processpi-0.0.0/processpi/pipelines/pumps.py +83 -0
- processpi-0.0.0/processpi/pipelines/selection.py +89 -0
- processpi-0.0.0/processpi/pipelines/standards.py +279 -0
- processpi-0.0.0/processpi/pipelines/vessel.py +34 -0
- processpi-0.0.0/processpi/units/__init__.py +38 -0
- processpi-0.0.0/processpi/units/area.py +51 -0
- processpi-0.0.0/processpi/units/base.py +57 -0
- processpi-0.0.0/processpi/units/density.py +47 -0
- processpi-0.0.0/processpi/units/diameter.py +58 -0
- processpi-0.0.0/processpi/units/dimensionless.py +42 -0
- processpi-0.0.0/processpi/units/heat_flux.py +46 -0
- processpi-0.0.0/processpi/units/heat_of_vaporization.py +47 -0
- processpi-0.0.0/processpi/units/heat_transfer_coefficient.py +45 -0
- processpi-0.0.0/processpi/units/length.py +47 -0
- processpi-0.0.0/processpi/units/mass.py +50 -0
- processpi-0.0.0/processpi/units/mass_flowrate.py +50 -0
- processpi-0.0.0/processpi/units/pressure.py +57 -0
- processpi-0.0.0/processpi/units/specific_heat.py +47 -0
- processpi-0.0.0/processpi/units/strings.py +31 -0
- processpi-0.0.0/processpi/units/temperature.py +51 -0
- processpi-0.0.0/processpi/units/thermal_conductivity.py +45 -0
- processpi-0.0.0/processpi/units/thermal_resistance.py +46 -0
- processpi-0.0.0/processpi/units/velocity.py +45 -0
- processpi-0.0.0/processpi/units/viscosity.py +78 -0
- processpi-0.0.0/processpi/units/volume.py +50 -0
- processpi-0.0.0/processpi/units/volumetric_flowrate.py +52 -0
- processpi-0.0.0/processpi.egg-info/PKG-INFO +5 -0
- processpi-0.0.0/processpi.egg-info/SOURCES.txt +102 -0
- processpi-0.0.0/processpi.egg-info/dependency_links.txt +1 -0
- processpi-0.0.0/processpi.egg-info/top_level.txt +1 -0
- processpi-0.0.0/pyproject.toml +1 -0
- processpi-0.0.0/setup.cfg +4 -0
- processpi-0.0.0/tests/test_engine_network.py +109 -0
- processpi-0.0.0/tests/test_network.py +77 -0
- processpi-0.0.0/tests/test_pipeline_network.py +85 -0
- processpi-0.0.0/tests/test_variables.py +93 -0
processpi-0.0.0/LICENSE
ADDED
|
@@ -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.
|
processpi-0.0.0/PKG-INFO
ADDED
|
@@ -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,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,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")
|