|
| 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