|
| 1 | +# -*- coding: utf-8 -*- |
| 2 | +""" |
| 3 | +Oil Viscosity Models and Tuning Tutorial |
| 4 | +========================================== |
| 5 | +
|
| 6 | +This example demonstrates advanced viscosity modeling for oil systems in NeqSim, |
| 7 | +including the LBC (Lohrenz-Bray-Clark) and Friction Theory models, and how to |
| 8 | +tune their parameters to match laboratory data. |
| 9 | +
|
| 10 | +Topics Covered: |
| 11 | + 1. Available viscosity models |
| 12 | + 2. LBC model and its parameters |
| 13 | + 3. Tuning LBC dense-fluid parameters |
| 14 | + 4. Friction Theory model |
| 15 | + 5. Tuning Friction Theory with TBP correction factor |
| 16 | + 6. Model comparison and selection guidelines |
| 17 | +
|
| 18 | +@author: NeqSim Team |
| 19 | +""" |
| 20 | + |
| 21 | +from neqsim.thermo import fluid, TPflash |
| 22 | +import neqsim.jneqsim as jneqsim |
| 23 | + |
| 24 | +print("=" * 70) |
| 25 | +print("OIL VISCOSITY MODELS AND TUNING TUTORIAL") |
| 26 | +print("=" * 70) |
| 27 | + |
| 28 | +# ============================================================================= |
| 29 | +# 1. AVAILABLE VISCOSITY MODELS |
| 30 | +# ============================================================================= |
| 31 | +print("\n1. AVAILABLE VISCOSITY MODELS") |
| 32 | +print("-" * 40) |
| 33 | +print(""" |
| 34 | +Model | Keyword | Best For |
| 35 | +-------------------|--------------------|--------------------------------- |
| 36 | +LBC | "LBC" | General oil, reservoir fluids |
| 37 | +Friction Theory | "friction theory" | Wide range, EoS-consistent |
| 38 | +PFCT | "PFCT" | Petroleum fractions |
| 39 | +PFCT Heavy Oil | "PFCT-Heavy-Oil" | Heavy oils, bitumen |
| 40 | +
|
| 41 | +The LBC model is based on corresponding states principle using critical |
| 42 | +properties. Friction Theory links viscosity to EoS pressure terms, |
| 43 | +providing thermodynamic consistency. |
| 44 | +""") |
| 45 | + |
| 46 | +# ============================================================================= |
| 47 | +# 2. BASIC VISCOSITY CALCULATION |
| 48 | +# ============================================================================= |
| 49 | +print("\n2. BASIC VISCOSITY CALCULATION") |
| 50 | +print("-" * 40) |
| 51 | + |
| 52 | +# Create a medium oil |
| 53 | +oil = fluid("srk") |
| 54 | +oil.addComponent("methane", 10.0, "mol%") |
| 55 | +oil.addComponent("n-pentane", 15.0, "mol%") |
| 56 | +oil.addComponent("n-heptane", 25.0, "mol%") |
| 57 | +oil.addComponent("n-decane", 30.0, "mol%") |
| 58 | +oil.addComponent("n-C16", 20.0, "mol%") |
| 59 | +oil.setMixingRule("classic") |
| 60 | +oil.setMultiPhaseCheck(True) |
| 61 | + |
| 62 | +# Set conditions |
| 63 | +oil.setTemperature(50.0, "C") |
| 64 | +oil.setPressure(100.0, "bara") |
| 65 | +TPflash(oil) |
| 66 | + |
| 67 | +print(f"Oil composition: C1: 10%, C5: 15%, C7: 25%, C10: 30%, C16: 20%") |
| 68 | +print(f"Conditions: T = 50°C, P = 100 bara") |
| 69 | + |
| 70 | +# Compare different viscosity models |
| 71 | +print("\nViscosity with different models:") |
| 72 | +print("\nModel | Viscosity [cP]") |
| 73 | +print("-------------------|----------------") |
| 74 | + |
| 75 | +for model in ["LBC", "friction theory", "PFCT"]: |
| 76 | + try: |
| 77 | + if oil.hasPhaseType("oil"): |
| 78 | + oil.getPhase("oil").getPhysicalProperties().setViscosityModel(model) |
| 79 | + oil.initPhysicalProperties() |
| 80 | + visc = oil.getPhase("oil").getViscosity("cP") |
| 81 | + print(f"{model:18} | {visc:.4f}") |
| 82 | + except Exception as e: |
| 83 | + print(f"{model:18} | Error: {e}") |
| 84 | + |
| 85 | +# ============================================================================= |
| 86 | +# 3. LBC MODEL EXPLANATION |
| 87 | +# ============================================================================= |
| 88 | +print("\n3. LBC MODEL (LOHRENZ-BRAY-CLARK)") |
| 89 | +print("-" * 40) |
| 90 | +print(""" |
| 91 | +The LBC model calculates viscosity as: |
| 92 | + |
| 93 | + η = η* + η_dense / ξ_m |
| 94 | + |
| 95 | +Where: |
| 96 | + η* = Low-pressure gas viscosity contribution |
| 97 | + η_dense = Dense-fluid contribution (function of reduced density) |
| 98 | + ξ_m = Mixture viscosity parameter |
| 99 | +
|
| 100 | +Dense-fluid contribution uses a polynomial: |
| 101 | +
|
| 102 | + (η_dense * ξ_m + 10^-4)^0.25 = a0 + a1*ρr + a2*ρr² + a3*ρr³ + a4*ρr⁴ |
| 103 | + |
| 104 | +Default LBC parameters (a0 to a4): |
| 105 | + a0 = 0.10230 |
| 106 | + a1 = 0.023364 |
| 107 | + a2 = 0.058533 |
| 108 | + a3 = -0.040758 |
| 109 | + a4 = 0.0093324 |
| 110 | +
|
| 111 | +These parameters can be tuned to match laboratory viscosity data. |
| 112 | +""") |
| 113 | + |
| 114 | +# ============================================================================= |
| 115 | +# 4. TUNING LBC MODEL PARAMETERS |
| 116 | +# ============================================================================= |
| 117 | +print("\n4. TUNING LBC MODEL PARAMETERS") |
| 118 | +print("-" * 40) |
| 119 | +print("Adjusting dense-fluid polynomial coefficients to match lab data") |
| 120 | + |
| 121 | +# Create oil system |
| 122 | +tuning_oil = fluid("srk") |
| 123 | +tuning_oil.addComponent("n-heptane", 30.0, "mol%") |
| 124 | +tuning_oil.addComponent("n-decane", 40.0, "mol%") |
| 125 | +tuning_oil.addTBPfraction("C16", 30.0, 0.22, 830.0) |
| 126 | +tuning_oil.setMixingRule("classic") |
| 127 | + |
| 128 | +tuning_oil.setTemperature(50.0, "C") |
| 129 | +tuning_oil.setPressure(150.0, "bara") |
| 130 | +TPflash(tuning_oil) |
| 131 | +tuning_oil.initThermoProperties() |
| 132 | + |
| 133 | +# Set LBC model and get default viscosity |
| 134 | +if tuning_oil.hasPhaseType("oil"): |
| 135 | + tuning_oil.getPhase("oil").getPhysicalProperties().setViscosityModel("LBC") |
| 136 | + tuning_oil.initPhysicalProperties() |
| 137 | + default_visc = tuning_oil.getPhase("oil").getViscosity("cP") |
| 138 | + |
| 139 | + print(f"\nDefault LBC viscosity: {default_visc:.4f} cP") |
| 140 | + |
| 141 | + # Suppose lab measurement is 2.5 cP - we need to tune |
| 142 | + lab_viscosity = 2.5 |
| 143 | + print(f"Laboratory measurement: {lab_viscosity:.4f} cP") |
| 144 | + |
| 145 | + # Method 1: Set all five parameters at once |
| 146 | + # Increasing coefficients generally increases viscosity |
| 147 | + # Default: [0.10230, 0.023364, 0.058533, -0.040758, 0.0093324] |
| 148 | + tuned_params = [0.15, 0.04, 0.08, -0.03, 0.015] # Increased values |
| 149 | + |
| 150 | + tuning_oil.getPhase("oil").getPhysicalProperties().setLbcParameters(tuned_params) |
| 151 | + tuning_oil.initPhysicalProperties() |
| 152 | + tuned_visc_1 = tuning_oil.getPhase("oil").getViscosity("cP") |
| 153 | + |
| 154 | + print(f"\nTuned viscosity (all params): {tuned_visc_1:.4f} cP") |
| 155 | + |
| 156 | + # Method 2: Adjust individual parameter |
| 157 | + # Parameter indices: a0=0, a1=1, a2=2, a3=3, a4=4 |
| 158 | + # a0 (index 0) has most influence at low density |
| 159 | + # a4 (index 4) has most influence at high density |
| 160 | + |
| 161 | + tuning_oil.getPhase("oil").getPhysicalProperties().setLbcParameter(2, 0.10) |
| 162 | + tuning_oil.initPhysicalProperties() |
| 163 | + tuned_visc_2 = tuning_oil.getPhase("oil").getViscosity("cP") |
| 164 | + |
| 165 | + print(f"Further adjusted (a2=0.10): {tuned_visc_2:.4f} cP") |
| 166 | + |
| 167 | +print(""" |
| 168 | +LBC Tuning Guidelines: |
| 169 | +---------------------- |
| 170 | +• a0 (index 0): Baseline offset - increase for higher overall viscosity |
| 171 | +• a1 (index 1): Linear density term - affects moderate densities |
| 172 | +• a2 (index 2): Quadratic term - significant for liquid viscosity |
| 173 | +• a3 (index 3): Cubic term - fine-tuning at high density |
| 174 | +• a4 (index 4): Quartic term - extreme density behavior |
| 175 | +""") |
| 176 | + |
| 177 | +# ============================================================================= |
| 178 | +# 5. FRICTION THEORY MODEL |
| 179 | +# ============================================================================= |
| 180 | +print("\n5. FRICTION THEORY MODEL") |
| 181 | +print("-" * 40) |
| 182 | +print(""" |
| 183 | +Friction Theory (f-theory) links viscosity to EoS pressure terms: |
| 184 | +
|
| 185 | + η = η0 + ηf |
| 186 | + |
| 187 | +Where: |
| 188 | + η0 = Dilute gas viscosity (Chung correlation) |
| 189 | + ηf = Friction contribution from EoS |
| 190 | +
|
| 191 | +The friction term is proportional to attractive and repulsive pressure |
| 192 | +terms from the equation of state, providing thermodynamic consistency. |
| 193 | +
|
| 194 | +Advantages: |
| 195 | + ✓ Consistent with phase equilibrium calculations |
| 196 | + ✓ Better extrapolation behavior |
| 197 | + ✓ Works well for wide T/P ranges |
| 198 | +""") |
| 199 | + |
| 200 | +# ============================================================================= |
| 201 | +# 6. TUNING FRICTION THEORY - TBP CORRECTION FACTOR |
| 202 | +# ============================================================================= |
| 203 | +print("\n6. TUNING FRICTION THEORY - TBP CORRECTION FACTOR") |
| 204 | +print("-" * 40) |
| 205 | +print("For TBP (True Boiling Point) fractions, a correction factor can be applied") |
| 206 | + |
| 207 | +# Create oil with TBP fractions |
| 208 | +ft_oil = fluid("srk") |
| 209 | +ft_oil.addComponent("methane", 5.0, "mol%") |
| 210 | +ft_oil.addComponent("n-heptane", 20.0, "mol%") |
| 211 | +ft_oil.addTBPfraction("C12", 25.0, 0.17, 780.0) |
| 212 | +ft_oil.addTBPfraction("C18", 30.0, 0.25, 820.0) |
| 213 | +ft_oil.addTBPfraction("C25", 20.0, 0.35, 860.0) |
| 214 | +ft_oil.setMixingRule("classic") |
| 215 | + |
| 216 | +ft_oil.setTemperature(60.0, "C") |
| 217 | +ft_oil.setPressure(100.0, "bara") |
| 218 | +TPflash(ft_oil) |
| 219 | +ft_oil.initThermoProperties() |
| 220 | + |
| 221 | +if ft_oil.hasPhaseType("oil"): |
| 222 | + # Set friction theory model |
| 223 | + ft_oil.getPhase("oil").getPhysicalProperties().setViscosityModel("friction theory") |
| 224 | + ft_oil.initPhysicalProperties() |
| 225 | + |
| 226 | + # Get default viscosity |
| 227 | + default_ft_visc = ft_oil.getPhase("oil").getViscosity("cP") |
| 228 | + print(f"\nDefault Friction Theory viscosity: {default_ft_visc:.4f} cP") |
| 229 | + |
| 230 | + # Apply TBP viscosity correction factor |
| 231 | + # Factor > 1.0 increases viscosity |
| 232 | + # Factor < 1.0 decreases viscosity |
| 233 | + visc_model = ft_oil.getPhase("oil").getPhysicalProperties().getViscosityModel() |
| 234 | + |
| 235 | + # Access the FrictionTheoryViscosityMethod directly |
| 236 | + print("\nApplying TBP correction factors:") |
| 237 | + print("\nCorrection Factor | Viscosity [cP]") |
| 238 | + print("------------------|----------------") |
| 239 | + |
| 240 | + for correction in [0.8, 1.0, 1.2, 1.5, 2.0]: |
| 241 | + try: |
| 242 | + # Set the TBP correction factor |
| 243 | + visc_model.setTBPviscosityCorrection(correction) |
| 244 | + ft_oil.initPhysicalProperties() |
| 245 | + |
| 246 | + corrected_visc = ft_oil.getPhase("oil").getViscosity("cP") |
| 247 | + print(f"{correction:17.1f} | {corrected_visc:.4f}") |
| 248 | + except Exception as e: |
| 249 | + print(f"{correction:17.1f} | Error: {e}") |
| 250 | + |
| 251 | + # Reset to default |
| 252 | + visc_model.setTBPviscosityCorrection(1.0) |
| 253 | + |
| 254 | +print(""" |
| 255 | +Friction Theory Tuning Guidelines: |
| 256 | +---------------------------------- |
| 257 | +• TBP Correction Factor: |
| 258 | + - Default = 1.0 (no correction) |
| 259 | + - > 1.0: Increases viscosity for heavy fractions |
| 260 | + - < 1.0: Decreases viscosity |
| 261 | + |
| 262 | +• Use when TBP fractions give incorrect viscosity predictions |
| 263 | +• Tune to match laboratory viscosity at one T/P condition |
| 264 | +• Model will extrapolate to other conditions |
| 265 | +""") |
| 266 | + |
| 267 | +# ============================================================================= |
| 268 | +# 7. ADVANCED: TUNING WITH EXPERIMENTAL DATA |
| 269 | +# ============================================================================= |
| 270 | +print("\n7. ADVANCED: TUNING WITH EXPERIMENTAL DATA") |
| 271 | +print("-" * 40) |
| 272 | +print("Workflow for matching lab measurements") |
| 273 | + |
| 274 | +# Example experimental data |
| 275 | +exp_data = [ |
| 276 | + {"T_C": 25, "P_bara": 1, "visc_cP": 3.5}, |
| 277 | + {"T_C": 50, "P_bara": 50, "visc_cP": 1.8}, |
| 278 | + {"T_C": 80, "P_bara": 100, "visc_cP": 0.9}, |
| 279 | +] |
| 280 | + |
| 281 | +print("\nExample experimental viscosity data:") |
| 282 | +print("T [°C] | P [bara] | Viscosity [cP]") |
| 283 | +print("-------|----------|---------------") |
| 284 | +for pt in exp_data: |
| 285 | + print(f"{pt['T_C']:6} | {pt['P_bara']:8} | {pt['visc_cP']:.2f}") |
| 286 | + |
| 287 | +print(""" |
| 288 | +Tuning Workflow: |
| 289 | +---------------- |
| 290 | +1. Create fluid with accurate composition |
| 291 | +2. Select viscosity model (LBC or Friction Theory) |
| 292 | +3. Calculate viscosity at each experimental condition |
| 293 | +4. Compare with experimental data |
| 294 | +5. Adjust parameters: |
| 295 | + - LBC: Tune a0-a4 dense-fluid parameters |
| 296 | + - Friction Theory: Tune TBP correction factor |
| 297 | +6. Iterate until deviation is acceptable |
| 298 | +7. Validate at conditions not used for tuning |
| 299 | +
|
| 300 | +For automatic optimization, use NeqSim's parameter fitting capabilities |
| 301 | +with the ViscosityFunction class in PVT simulation. |
| 302 | +""") |
| 303 | + |
| 304 | +# ============================================================================= |
| 305 | +# 8. MODEL COMPARISON AT DIFFERENT CONDITIONS |
| 306 | +# ============================================================================= |
| 307 | +print("\n8. MODEL COMPARISON AT DIFFERENT CONDITIONS") |
| 308 | +print("-" * 40) |
| 309 | + |
| 310 | +comp_oil = fluid("srk") |
| 311 | +comp_oil.addComponent("n-heptane", 30.0, "mol%") |
| 312 | +comp_oil.addComponent("n-decane", 40.0, "mol%") |
| 313 | +comp_oil.addComponent("n-C16", 30.0, "mol%") |
| 314 | +comp_oil.setMixingRule("classic") |
| 315 | +comp_oil.setMultiPhaseCheck(True) |
| 316 | + |
| 317 | +print("Viscosity comparison: LBC vs Friction Theory") |
| 318 | +print("Oil: C7 30%, C10 40%, C16 30%") |
| 319 | +print("\nT [°C] | P [bara] | LBC [cP] | F-Theory [cP] | Ratio") |
| 320 | +print("-------|----------|-----------|---------------|-------") |
| 321 | + |
| 322 | +conditions = [ |
| 323 | + (25, 1), |
| 324 | + (50, 50), |
| 325 | + (80, 100), |
| 326 | + (100, 200), |
| 327 | + (120, 300), |
| 328 | +] |
| 329 | + |
| 330 | +for t_c, p_bara in conditions: |
| 331 | + comp_oil.setTemperature(t_c, "C") |
| 332 | + comp_oil.setPressure(p_bara, "bara") |
| 333 | + TPflash(comp_oil) |
| 334 | + comp_oil.initThermoProperties() |
| 335 | + |
| 336 | + if comp_oil.hasPhaseType("oil"): |
| 337 | + # LBC |
| 338 | + comp_oil.getPhase("oil").getPhysicalProperties().setViscosityModel("LBC") |
| 339 | + comp_oil.initPhysicalProperties() |
| 340 | + lbc_visc = comp_oil.getPhase("oil").getViscosity("cP") |
| 341 | + |
| 342 | + # Friction Theory |
| 343 | + comp_oil.getPhase("oil").getPhysicalProperties().setViscosityModel("friction theory") |
| 344 | + comp_oil.initPhysicalProperties() |
| 345 | + ft_visc = comp_oil.getPhase("oil").getViscosity("cP") |
| 346 | + |
| 347 | + ratio = lbc_visc / ft_visc if ft_visc > 0 else 0 |
| 348 | + print(f"{t_c:6} | {p_bara:8} | {lbc_visc:9.4f} | {ft_visc:13.4f} | {ratio:.3f}") |
| 349 | + |
| 350 | +# ============================================================================= |
| 351 | +# 9. HEAVY OIL CONSIDERATIONS |
| 352 | +# ============================================================================= |
| 353 | +print("\n9. HEAVY OIL CONSIDERATIONS") |
| 354 | +print("-" * 40) |
| 355 | +print(""" |
| 356 | +For heavy oils (API < 20°, viscosity > 100 cP), consider: |
| 357 | +
|
| 358 | +1. Use PFCT-Heavy-Oil model: |
| 359 | + oil.getPhase("oil").getPhysicalProperties().setViscosityModel("PFCT-Heavy-Oil") |
| 360 | +
|
| 361 | +2. LBC may underpredict - needs significant parameter tuning |
| 362 | +
|
| 363 | +3. Friction Theory typically needs TBP correction > 1.0 for heavy ends |
| 364 | +
|
| 365 | +4. Temperature sensitivity is critical - ensure accurate measurements |
| 366 | +
|
| 367 | +5. Consider using Pedersen corresponding states for very heavy systems |
| 368 | +""") |
| 369 | + |
| 370 | +# ============================================================================= |
| 371 | +# 10. SUMMARY |
| 372 | +# ============================================================================= |
| 373 | +print("\n10. SUMMARY: MODEL SELECTION GUIDELINES") |
| 374 | +print("-" * 40) |
| 375 | +print(""" |
| 376 | +┌────────────────────┬──────────────────────────────────────────┐ |
| 377 | +│ Oil Type │ Recommended Model & Notes │ |
| 378 | +├────────────────────┼──────────────────────────────────────────┤ |
| 379 | +│ Light oil │ LBC or Friction Theory │ |
| 380 | +│ (API > 35°) │ Default parameters often sufficient │ |
| 381 | +├────────────────────┼──────────────────────────────────────────┤ |
| 382 | +│ Medium oil │ LBC with tuned parameters │ |
| 383 | +│ (20° < API < 35°) │ or Friction Theory with TBP correction │ |
| 384 | +├────────────────────┼──────────────────────────────────────────┤ |
| 385 | +│ Heavy oil │ PFCT-Heavy-Oil or Friction Theory │ |
| 386 | +│ (10° < API < 20°) │ TBP correction factor 1.5-2.0 │ |
| 387 | +├────────────────────┼──────────────────────────────────────────┤ |
| 388 | +│ Extra-heavy │ Specialized correlations │ |
| 389 | +│ (API < 10°) │ May require custom viscosity data │ |
| 390 | +└────────────────────┴──────────────────────────────────────────┘ |
| 391 | +""") |
| 392 | + |
| 393 | +print("=" * 70) |
0 commit comments