design_flash_benchmark · git:20260912.c5ddb97 · 2026-09-12 · sha256 45752820a9a8d375
design_flash_benchmark git:20260912.c5ddb97A
Immutable. This exact content is served forever at /api/v1/blob/45752820a9a8d375.
---
name: "design_flash_benchmark"
description: "Create a structured test matrix for comparing flash algorithm performance"
last_verified: "2026-09-12"
---
# Skill: Design Flash Benchmark
## Purpose
Create a structured test matrix for comparing flash algorithm performance
across fluid types, thermodynamic conditions, and difficulty levels.
## When to Use
- Starting a new flash algorithm comparison study
- Extending an existing benchmark to new fluid families
- Designing stress tests for near-critical or multi-phase regions
## Benchmark Design Procedure
### Step 1: Select Fluid Families
Choose from these standard families:
| Family | Components | Mole Fractions | Characteristics |
|--------|-----------|----------------|-----------------|
| **Lean gas** | CH4(0.90), C2(0.05), C3(0.03), N2(0.01), CO2(0.01) | Fixed or ±10% | Easy, mostly single-phase |
| **Rich gas** | CH4(0.70), C2(0.10), C3(0.08), nC4(0.05), nC5(0.03), N2(0.02), CO2(0.02) | Fixed or ±15% | Moderate, clear two-phase |
| **Gas condensate** | CH4(0.65), C2(0.08), C3(0.06), nC4(0.04), nC5(0.03), nC6(0.02), nC7(0.02), nC10(0.05), N2(0.02), CO2(0.03) | ±20% | Near-critical behavior |
| **CO2-rich** | CO2(0.80), CH4(0.10), N2(0.05), H2S(0.03), C2(0.02) | ±15% | Strong non-ideality |
| **Wide-boiling** | CH4(0.50), nC4(0.15), nC10(0.15), nC16(0.10), nC20(0.10) | ±20% | Large volatility range |
| **Sour gas** | CH4(0.60), CO2(0.15), H2S(0.10), C2(0.08), C3(0.05), N2(0.02) | ±15% | Acid gas behavior |
### Step 2: Define PT Space
For each family, define the pressure-temperature sampling grid:
```python
import numpy as np
def generate_pt_grid(T_min_K, T_max_K, P_min_bara, P_max_bara, n_T=20, n_P=20):
"""Generate a regular PT grid."""
T_values = np.linspace(T_min_K, T_max_K, n_T)
P_values = np.logspace(np.log10(P_min_bara), np.log10(P_max_bara), n_P)
cases = []
for T in T_values:
for P in P_values:
cases.append({"T_K": float(T), "P_bara": float(P)})
return cases
```
Standard ranges by family:
| Family | T range (K) | P range (bara) | Focus region |
|--------|------------|----------------|--------------|
| Lean gas | 200–400 | 1–200 | Dew point region |
| Rich gas | 220–450 | 5–300 | Two-phase dome |
| Gas condensate | 250–500 | 10–500 | Near cricondenbar |
| CO2-rich | 250–400 | 10–200 | CO2 critical region |
| Wide-boiling | 300–600 | 1–100 | Large T range |
### Step 3: Add Stress Cases
Add cases specifically designed to challenge the algorithm:
1. **Near bubble point**: T slightly above Tbub at given P
2. **Near dew point**: T slightly below Tdew at given P
3. **Near critical**: T ≈ Tc ± 5K, P ≈ Pc ± 5 bar
4. **Very low vapor fraction**: β ≈ 0.001
5. **Very high vapor fraction**: β ≈ 0.999
6. **Single phase (verification)**: Known single-phase conditions
7. **Trace components**: One component at < 1e-6 mole fraction
### Step 4: Define Composition Perturbation
Use Dirichlet sampling to generate composition variants:
```python
from numpy.random import dirichlet
def perturb_composition(base_comp, n_variants=10, concentration=50):
"""Generate composition variants around a base composition.
Higher concentration = less perturbation.
"""
names = list(base_comp.keys())
alpha = np.array([base_comp[n] for n in names]) * concentration
variants = []
for _ in range(n_variants):
x = dirichlet(alpha)
variants.append(dict(zip(names, x.tolist())))
return variants
```
### Step 5: Define Metrics
Every benchmark case must record:
| Metric | Type | Unit | How to Measure |
|--------|------|------|----------------|
| `converged` | bool | — | Did the flash converge? |
| `iterations` | int | — | Total iterations (SS + NR) |
| `ss_iterations` | int | — | Successive substitution iterations only |
| `nr_iterations` | int | — | Newton-Raphson iterations only |
| `cpu_time_ms` | float | ms | Wall-clock time (median of 3 runs) |
| `residual_norm` | float | — | Final norm of equilibrium residuals |
| `stability_tested` | bool | — | Was stability analysis triggered? |
| `stability_iters` | int | — | TPD minimization iterations |
| `n_phases` | int | — | Number of phases at equilibrium |
| `beta_vapor` | float | — | Vapor phase fraction |
| `phase_id_correct` | bool | — | Correct phase identification? |
### Step 6: Estimate Total Cases
Target: **500–2000 cases per algorithm version**.
| Component | Cases |
|-----------|-------|
| 6 families × 20 PT points | 120 base cases |
| 10 composition variants each | 1200 cases |
| 50 stress cases | 50 cases |
| Total | ~1250 cases |
### Step 7: Generate Config File
Output `benchmark_config.json`:
```json
{
"benchmark_id": "tpflash_2026_01",
"created": "2026-03-31",
"algorithms": ["baseline", "candidate_eigenvalue_switch"],
"eos_models": ["SRK"],
"timing_repeats": 3,
"families": [
{
"name": "lean_gas",
"base_composition": {"methane": 0.90, "ethane": 0.05, "propane": 0.03, "nitrogen": 0.01, "CO2": 0.01},
"n_composition_variants": 10,
"dirichlet_concentration": 50,
"T_range_K": [200, 400],
"P_range_bara": [1, 200],
"n_T": 20,
"n_P": 20
}
],
"stress_cases": {
"near_critical": 20,
"near_bubble": 10,
"near_dew": 10,
"trace_component": 10
}
}
```
## Checklist
- [ ] All components available in NeqSim database
- [ ] PT ranges cover the two-phase region for each family
- [ ] Stress cases are well-defined
- [ ] Metrics list is complete
- [ ] Total case count is between 500 and 2000
- [ ] Random seed is fixed for reproducibility