Skip to content

Commit cb822bb

Browse files
committed
feat(process): add ProcessContext and ProcessBuilder classes
Implemented all recommended improvements for process simulation: 1. ProcessContext class: - Context manager for explicit process management - Each context has its own ProcessSystem - Supports multiple independent processes - Methods: stream, separator, compressor, pump, valve, heater, cooler, etc. 2. ProcessBuilder class: - Fluent/chainable API for building processes - Equipment referenced by name (string) - Great for configuration-driven design - Methods: add_stream, add_separator, add_compressor, etc. 3. Updated wrapper functions with process= parameter: - stream, separator, separator3phase, valve - compressor, pump, expander - heater, cooler, heatExchanger - mixer, splitter 4. Added _add_to_process helper function 5. New example: processApproaches.py demonstrating all three approaches Backwards compatible - existing code using global process still works.
1 parent d20a13b commit cb822bb

File tree

2 files changed

+882
-128
lines changed

2 files changed

+882
-128
lines changed

examples/processApproaches.py

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
Process Simulation Approaches in NeqSim Python
4+
5+
This example demonstrates the three ways to build process simulations
6+
in NeqSim Python:
7+
8+
1. Python Wrappers with Global Process - Simple, good for notebooks
9+
2. ProcessContext - Explicit process management with context manager
10+
3. ProcessBuilder - Fluent/chainable API for building processes
11+
12+
Each approach has its advantages depending on your use case.
13+
14+
@author: NeqSim Team
15+
"""
16+
17+
from neqsim.thermo import fluid
18+
from neqsim.process import (
19+
# Wrapper functions (Approach 1)
20+
clearProcess,
21+
runProcess,
22+
stream,
23+
separator,
24+
compressor,
25+
cooler,
26+
valve,
27+
# New classes (Approaches 2 & 3)
28+
ProcessContext,
29+
ProcessBuilder,
30+
newProcess,
31+
)
32+
33+
print("=" * 70)
34+
print("NEQSIM PYTHON: THREE APPROACHES TO PROCESS SIMULATION")
35+
print("=" * 70)
36+
37+
38+
# =============================================================================
39+
# APPROACH 1: Python Wrappers with Global Process
40+
# =============================================================================
41+
# Best for: Quick prototyping, Jupyter notebooks, learning
42+
# Pros: Simple, concise, automatic process management
43+
# Cons: Global state, one process at a time
44+
45+
print("\n" + "-" * 70)
46+
print("APPROACH 1: Python Wrappers (Global Process)")
47+
print("-" * 70)
48+
49+
# Create fluid for the process
50+
feed_fluid = fluid("srk")
51+
feed_fluid.addComponent("nitrogen", 1.0)
52+
feed_fluid.addComponent("methane", 85.0)
53+
feed_fluid.addComponent("ethane", 8.0)
54+
feed_fluid.addComponent("propane", 4.0)
55+
feed_fluid.addComponent("n-butane", 2.0)
56+
feed_fluid.setMixingRule("classic")
57+
feed_fluid.setTemperature(30.0, "C")
58+
feed_fluid.setPressure(30.0, "bara")
59+
feed_fluid.setTotalFlowRate(5.0, "MSm3/day")
60+
61+
# Clear any previous process and build new one
62+
clearProcess()
63+
64+
# Equipment is automatically added to global process
65+
inlet = stream("inlet", feed_fluid)
66+
sep1 = separator("HP separator", inlet)
67+
comp1 = compressor("1st stage", sep1.getGasOutStream(), pres=60.0)
68+
cool1 = cooler("intercooler", comp1.getOutletStream())
69+
cool1.setOutTemperature(303.15) # 30°C
70+
comp2 = compressor("2nd stage", cool1.getOutletStream(), pres=120.0)
71+
72+
# Run the simulation
73+
runProcess()
74+
75+
print(f"\nResults (Approach 1):")
76+
print(f" Stage 1 power: {comp1.getPower()/1e6:.3f} MW")
77+
print(f" Stage 2 power: {comp2.getPower()/1e6:.3f} MW")
78+
print(f" Total power: {(comp1.getPower() + comp2.getPower())/1e6:.3f} MW")
79+
80+
81+
# =============================================================================
82+
# APPROACH 2: ProcessContext (Explicit Process Management)
83+
# =============================================================================
84+
# Best for: Production code, multiple processes, clean resource management
85+
# Pros: Explicit control, supports multiple processes, context manager
86+
# Cons: Slightly more verbose
87+
88+
print("\n" + "-" * 70)
89+
print("APPROACH 2: ProcessContext (Explicit Process)")
90+
print("-" * 70)
91+
92+
# Create fresh fluid
93+
feed_fluid2 = fluid("srk")
94+
feed_fluid2.addComponent("nitrogen", 1.0)
95+
feed_fluid2.addComponent("methane", 85.0)
96+
feed_fluid2.addComponent("ethane", 8.0)
97+
feed_fluid2.addComponent("propane", 4.0)
98+
feed_fluid2.addComponent("n-butane", 2.0)
99+
feed_fluid2.setMixingRule("classic")
100+
feed_fluid2.setTemperature(30.0, "C")
101+
feed_fluid2.setPressure(30.0, "bara")
102+
feed_fluid2.setTotalFlowRate(5.0, "MSm3/day")
103+
104+
# Use ProcessContext - each context has its own process
105+
with ProcessContext("Compression Train") as ctx:
106+
# Methods mirror the wrapper functions
107+
inlet = ctx.stream("inlet", feed_fluid2)
108+
sep = ctx.separator("HP separator", inlet)
109+
comp1 = ctx.compressor("1st stage", sep.getGasOutStream(), pres=60.0)
110+
cool = ctx.cooler("intercooler", comp1.getOutletStream(), temp=303.15)
111+
comp2 = ctx.compressor("2nd stage", cool.getOutletStream(), pres=120.0)
112+
113+
# Run this specific process
114+
ctx.run()
115+
116+
# Access equipment by name
117+
stage1 = ctx.get("1st stage")
118+
stage2 = ctx.get("2nd stage")
119+
120+
print(f"\nResults (Approach 2):")
121+
print(f" Stage 1 power: {stage1.getPower()/1e6:.3f} MW")
122+
print(f" Stage 2 power: {stage2.getPower()/1e6:.3f} MW")
123+
print(f" Total power: {(stage1.getPower() + stage2.getPower())/1e6:.3f} MW")
124+
125+
126+
# =============================================================================
127+
# APPROACH 3: ProcessBuilder (Fluent/Chainable API)
128+
# =============================================================================
129+
# Best for: Configuration-driven design, clean declarative style
130+
# Pros: Chainable, equipment referenced by name, very readable
131+
# Cons: Less direct access during construction
132+
133+
print("\n" + "-" * 70)
134+
print("APPROACH 3: ProcessBuilder (Fluent API)")
135+
print("-" * 70)
136+
137+
# Create fresh fluid
138+
feed_fluid3 = fluid("srk")
139+
feed_fluid3.addComponent("nitrogen", 1.0)
140+
feed_fluid3.addComponent("methane", 85.0)
141+
feed_fluid3.addComponent("ethane", 8.0)
142+
feed_fluid3.addComponent("propane", 4.0)
143+
feed_fluid3.addComponent("n-butane", 2.0)
144+
feed_fluid3.setMixingRule("classic")
145+
feed_fluid3.setTemperature(30.0, "C")
146+
feed_fluid3.setPressure(30.0, "bara")
147+
feed_fluid3.setTotalFlowRate(5.0, "MSm3/day")
148+
149+
# Build process with fluent API - all chained together
150+
process = (ProcessBuilder("Compression Train")
151+
.add_stream("inlet", feed_fluid3)
152+
.add_separator("HP separator", "inlet")
153+
.add_compressor("1st stage", "HP separator", pressure=60.0)
154+
.add_cooler("intercooler", "1st stage", temperature=303.15)
155+
.add_compressor("2nd stage", "intercooler", pressure=120.0)
156+
.run())
157+
158+
# Access results
159+
comp1 = process.get("1st stage")
160+
comp2 = process.get("2nd stage")
161+
162+
print(f"\nResults (Approach 3):")
163+
print(f" Stage 1 power: {comp1.getPower()/1e6:.3f} MW")
164+
print(f" Stage 2 power: {comp2.getPower()/1e6:.3f} MW")
165+
print(f" Total power: {(comp1.getPower() + comp2.getPower())/1e6:.3f} MW")
166+
167+
168+
# =============================================================================
169+
# HYBRID APPROACH: Wrappers with Explicit Process
170+
# =============================================================================
171+
# You can also use wrapper functions with an explicit process parameter
172+
173+
print("\n" + "-" * 70)
174+
print("HYBRID: Wrapper Functions with process= Parameter")
175+
print("-" * 70)
176+
177+
# Create fresh fluid
178+
feed_fluid4 = fluid("srk")
179+
feed_fluid4.addComponent("nitrogen", 1.0)
180+
feed_fluid4.addComponent("methane", 85.0)
181+
feed_fluid4.addComponent("ethane", 8.0)
182+
feed_fluid4.addComponent("propane", 4.0)
183+
feed_fluid4.addComponent("n-butane", 2.0)
184+
feed_fluid4.setMixingRule("classic")
185+
feed_fluid4.setTemperature(30.0, "C")
186+
feed_fluid4.setPressure(30.0, "bara")
187+
feed_fluid4.setTotalFlowRate(5.0, "MSm3/day")
188+
189+
# Create explicit process
190+
my_process = newProcess("Compression Train")
191+
192+
# Use wrapper functions but specify process explicitly
193+
inlet = stream("inlet", feed_fluid4, process=my_process)
194+
sep = separator("HP separator", inlet, process=my_process)
195+
comp1 = compressor("1st stage", sep.getGasOutStream(), pres=60.0, process=my_process)
196+
cool = cooler("intercooler", comp1.getOutletStream(), process=my_process)
197+
cool.setOutTemperature(303.15)
198+
comp2 = compressor("2nd stage", cool.getOutletStream(), pres=120.0, process=my_process)
199+
200+
# Run specific process
201+
my_process.run()
202+
203+
print(f"\nResults (Hybrid):")
204+
print(f" Stage 1 power: {comp1.getPower()/1e6:.3f} MW")
205+
print(f" Stage 2 power: {comp2.getPower()/1e6:.3f} MW")
206+
print(f" Total power: {(comp1.getPower() + comp2.getPower())/1e6:.3f} MW")
207+
208+
209+
# =============================================================================
210+
# COMPARISON SUMMARY
211+
# =============================================================================
212+
213+
print("\n" + "=" * 70)
214+
print("CHOOSING AN APPROACH")
215+
print("=" * 70)
216+
print("""
217+
| Use Case | Recommended Approach |
218+
|---------------------------------|-------------------------------|
219+
| Learning / tutorials | Wrappers (global process) |
220+
| Jupyter notebooks | Wrappers (global process) |
221+
| Quick prototyping | Wrappers (global process) |
222+
| Production applications | ProcessContext or ProcessBuilder |
223+
| Multiple parallel processes | ProcessContext |
224+
| Configuration-driven design | ProcessBuilder |
225+
| Clean declarative style | ProcessBuilder |
226+
| Mixing wrapper convenience | Hybrid (process= parameter) |
227+
with explicit control | |
228+
""")

0 commit comments

Comments
 (0)