|
10 | 10 | --- |
11 | 11 |
|
12 | 12 | ## 2026-07-08 — UniSim Reader: Orientation Detection (GasScrubber) |
| 13 | +<<<<<<< HEAD |
13 | 14 |
|
14 | 15 | ### Vertical Separator → GasScrubber Mapping |
15 | 16 |
|
@@ -167,55 +168,161 @@ raw = kij_obj.Values # tuple-of-tuples (n×n symmetric matrix) |
167 | 168 | --- |
168 | 169 |
|
169 | 170 | ## 2026-04-08 — IEC 81346 Reference Designation Support |
| 171 | +======= |
| 172 | +>>>>>>> 0a1cdaa75a2293eaa9447f3df1b0ec9460a74c1e |
170 | 173 |
|
171 | | -### New Java Package: `neqsim.process.equipment.iec81346` |
172 | | - |
173 | | -| Class | Description | |
174 | | -|-------|-------------| |
175 | | -| `IEC81346LetterCode` | Enum for IEC 81346-2 letter codes (A–X). Maps all `EquipmentEnum` values and provides `fromEquipment()` for instanceof-based classification. Now tries `EquipmentEnum.valueOf()` first for faster lookup. | |
176 | | -| `ReferenceDesignation` | Serializable data class holding three IEC 81346 aspects (function `=`, product `-`, location `+`) plus letter code and sequence number. `toReferenceDesignationString()` composes `"=A1-B1+P1"`. Static `parse(String)` factory for round-tripping. | |
177 | | -| `ReferenceDesignationGenerator` | Auto-assigns IEC 81346 designations to a `ProcessSystem` or multi-area `ProcessModel`. Supports hierarchical (`A1.A1`, `A1.A2`) and flat (`A1`, `A2`) function numbering. No-arg constructor + late binding via `generate(ProcessSystem)` / `generate(ProcessModel)`. | |
178 | | - |
179 | | -### Modified Interfaces & Classes |
180 | | - |
181 | | -| Class | Change | |
182 | | -|-------|--------| |
183 | | -| `ProcessEquipmentInterface` | Added 3 default methods: `getReferenceDesignation()`, `setReferenceDesignation(ReferenceDesignation)`, `getReferenceDesignationString()` | |
184 | | -| `ProcessEquipmentBaseClass` | Added `referenceDesignation` field and overriding getter/setter | |
185 | | -| `ProcessSystem` | Added `generateReferenceDesignations(funcPrefix, locPrefix)` convenience method and `getUnitByReferenceDesignation(String)` for lookup by ref des | |
186 | | -| `ProcessModel` | Added `generateReferenceDesignations(locPrefix)` (flat), `generateReferenceDesignations(funcPrefix, locPrefix)` (hierarchical), and `getUnitByReferenceDesignation(String)` (cross-area lookup) | |
187 | | -| `ProcessConnection` | Added `sourceReferenceDesignation` and `targetReferenceDesignation` fields with getters/setters | |
188 | | -| `ControllerDeviceBaseClass` | Added `referenceDesignation` field with getter/setter/`getReferenceDesignationString()` | |
189 | | -| `ProcessSystemState` | `EquipmentState.fromEquipment()` now captures 6 IEC 81346 properties (`iec81346_referenceDesignation`, `_functionDesignation`, `_productDesignation`, `_locationDesignation`, `_letterCode`, `_sequenceNumber`) | |
190 | | -| `DexpiXmlWriter` (dexpi package) | Writes 5 IEC 81346 `GenericAttribute` elements per equipment when reference designation is set | |
191 | | -| `ProcessAutomation` | `findUnit()` now resolves IEC 81346 reference designation addresses (strings starting with `=` or `-`) | |
192 | | -| `StudyClass` | Added `REFERENCE_DESIGNATION_SCHEDULE` to `DeliverableType` enum (included in CLASS_A and CLASS_B) | |
193 | | -| `EngineeringDeliverablesPackage` | Added `generateReferenceDesignationSchedule()` method and JSON output for ref des schedule | |
194 | | -| `InstrumentScheduleGenerator` | Added `getISAToIEC81346Map()` for ISA-5.1 to IEC 81346 cross-reference | |
195 | | - |
196 | | -### Usage Pattern |
| 174 | +### Vertical Separator → GasScrubber Mapping |
| 175 | + |
| 176 | +The UniSim reader (`devtools/unisim_reader.py`) now detects separator orientation. |
| 177 | +Vertical `flashtank` operations are mapped to `GasScrubber` instead of `Separator`. |
| 178 | + |
| 179 | +| UniSim flashtank | NeqSim Type | |
| 180 | +|---|---| |
| 181 | +| horizontal (default) | `Separator` | |
| 182 | +| vertical | `GasScrubber` | |
| 183 | +| has WaterProduct | `ThreePhaseSeparator` | |
| 184 | + |
| 185 | +`GasScrubber` extends `Separator` — it is a vertical vessel with K-value |
| 186 | +sizing constraints and 10% liquid level. The orientation is detected from |
| 187 | +UniSim COM attributes (`Orientation`, `VesselOrientation`, `SeparatorOrientation`). |
| 188 | + |
| 189 | +### Affected Files |
| 190 | +- `devtools/unisim_reader.py` — `resolve_neqsim_type()` method, orientation extraction |
| 191 | +- `.github/skills/neqsim-unisim-reader/SKILL.md` |
| 192 | +- `.github/agents/unisim.reader.agent.md` |
| 193 | +- `AGENTS.md` |
| 194 | + |
| 195 | +--- |
| 196 | + |
| 197 | +## 2026-07-07 — Full FPSO Model: Architecture Learnings |
| 198 | + |
| 199 | +### HP Separator Water Routing |
| 200 | + |
| 201 | +When replicating UniSim models in NeqSim, the HP separator at high pressure (90 bar) |
| 202 | +may not produce a separate aqueous phase in UniSim. To match this behavior, use |
| 203 | +`ThreePhaseSeparator` and then `Mixer` to recombine oil + water: |
197 | 204 |
|
198 | 205 | ```java |
199 | | -// Single system |
200 | | -process.generateReferenceDesignations("A1", "P1"); |
201 | | -ProcessEquipmentInterface sep = process.getUnitByReferenceDesignation("=A1-B1+P1"); |
| 206 | +ThreePhaseSeparator hpSep = new ThreePhaseSeparator("HP Sep", feedStream); |
| 207 | +Mixer hpLiqRecombine = new Mixer("HP Liquid Recombine"); |
| 208 | +hpLiqRecombine.addStream(hpSep.getOilOutStream()); |
| 209 | +hpLiqRecombine.addStream(hpSep.getWaterOutStream()); |
| 210 | +// hpLiqRecombine.getOutletStream() now matches UniSim HP oil (includes water) |
| 211 | +``` |
| 212 | + |
| 213 | +### Import Gas Compression Architecture |
| 214 | + |
| 215 | +Large FPSO models use staged import gas compression matching pressure levels: |
| 216 | +- VLP gas (~2 bar) → VRU compressor → ~5 bar → mix with LP gas |
| 217 | +- LP+VRU gas (~5 bar) → 1st import compressor → ~22 bar → mix with MP gas |
| 218 | +- MP+1st import gas (~22 bar) → 2nd import compressor → ~90 bar → mix with HP gas |
| 219 | + |
| 220 | +Each stage has cooler + flash drum before the compressor (removes condensate). |
| 221 | + |
| 222 | +### Pump API |
| 223 | + |
| 224 | +```java |
| 225 | +Pump pump = new Pump("P-100", liquidStream); |
| 226 | +pump.setOutletPressure(6.1); // bara |
| 227 | +pump.setIsentropicEfficiency(0.75); |
| 228 | +pump.getPower("kW"); // after run |
| 229 | +``` |
| 230 | + |
| 231 | +### ComponentSplitter for TEG Dehydration |
| 232 | + |
| 233 | +```java |
| 234 | +ComponentSplitter teg = new ComponentSplitter("TEG", wetGasStream); |
| 235 | +int nComp = wetGasStream.getFluid().getNumberOfComponents(); |
| 236 | +double[] sf = new double[nComp]; |
| 237 | +java.util.Arrays.fill(sf, 1.0); |
| 238 | +sf[nComp - 1] = 0.0; // water is last component |
| 239 | +teg.setSplitFactors(sf); |
| 240 | +// getSplitStream(0) = dry gas, getSplitStream(1) = removed water |
| 241 | +``` |
| 242 | + |
| 243 | +### Model Scale: 50+ Equipment Units in Single ProcessSystem |
| 244 | + |
| 245 | +The reference FPSO model demonstrates ~50 equipment units in a single `ProcessSystem` |
| 246 | +covering wellhead → HP/MP/LP/VLP separation → VRU + import gas compression → |
| 247 | +gas cooling + TEG → 2-stage export compression → seal gas JT → oil export. |
| 248 | +Single `ProcessSystem` converges in ~2 seconds without recycles. |
202 | 249 |
|
203 | | -// Multi-area |
204 | | -plant.generateReferenceDesignations("P1"); // flat |
205 | | -plant.generateReferenceDesignations("A1", "P1"); // hierarchical |
206 | | -ProcessEquipmentInterface comp = plant.getUnitByReferenceDesignation("=A2-K1+P1"); |
| 250 | +--- |
| 251 | + |
| 252 | +## 2026-07-06 — JT Expansion: Use ThrottlingValve, Not PHflash |
| 253 | + |
| 254 | +### Critical Agent Guidance |
207 | 255 |
|
208 | | -// ProcessAutomation addresses accept IEC 81346 strings |
209 | | -double temp = auto.getVariableValue("=A1-B1+P1.gasOutStream.temperature", "C"); |
| 256 | +When modeling isenthalpic (Joule-Thomson) expansion, **always use `ThrottlingValve` in a |
| 257 | +`ProcessSystem`**, never manual `PHflash()` on a cloned fluid. Tested on FPSO seal gas |
| 258 | +(90→48 bar): |
| 259 | + |
| 260 | +| Method | Temperature (°C) | UniSim Reference | Error | |
| 261 | +|--------|-----------------|------------------|-------| |
| 262 | +| `ThrottlingValve` in ProcessSystem | 16.44 | 18.17 | -1.73°C | |
| 263 | +| Manual `PHflash(H/n)` on clone | 33.05 | 18.17 | +14.88°C | |
| 264 | + |
| 265 | +The manual PHflash approach fails because `getEnthalpy('J')` returns total system enthalpy |
| 266 | +while `PHflash(double)` expects a specific enthalpy convention (per mole at the system's |
| 267 | +reference state). The ThrottlingValve handles the enthalpy bookkeeping internally. |
| 268 | + |
| 269 | +**Pattern:** |
| 270 | +```java |
| 271 | +// CORRECT: Use process-level valve |
| 272 | +ProcessSystem proc = new ProcessSystem(); |
| 273 | +Stream sg = new Stream("SG", fluid.clone()); |
| 274 | +proc.add(sg); |
| 275 | +ThrottlingValve jt = new ThrottlingValve("JT", sg); |
| 276 | +jt.setOutletPressure(48.0); |
| 277 | +proc.add(jt); |
| 278 | +proc.run(); |
| 279 | +double T_jt = jt.getOutletStream().getTemperature("C"); // Correct JT temperature |
| 280 | + |
| 281 | +// WRONG: Manual PHflash — gives incorrect JT temperature |
| 282 | +// SystemInterface clone = fluid.clone(); |
| 283 | +// clone.setPressure(48.0); |
| 284 | +// new ThermodynamicOperations(clone).PHflash(fluid.getEnthalpy("J") / fluid.getTotalNumberOfMoles()); |
210 | 285 | ``` |
211 | 286 |
|
212 | | -### Agent Impact |
213 | | -- `ProcessAutomation` addresses now accept IEC 81346 strings (`=A1-B1+P1`) |
214 | | -- DEXPI exports contain IEC 81346 attributes when designations are generated |
215 | | -- Lifecycle state snapshots preserve IEC 81346 designations for versioning |
216 | | -- Engineering deliverables include reference designation schedule (Class A/B) |
217 | | -- ISA-5.1 to IEC 81346 bridging via `InstrumentScheduleGenerator` |
218 | | -- New documentation: `docs/standards/iec81346-reference-designations.md` |
| 287 | +### FPSO Model Extension |
| 288 | + |
| 289 | +Extended the NeqSim FPSO replication to include: |
| 290 | +- LP/MP gas recompression + mixing with HP gas |
| 291 | +- Gas cooling (24HA101, 75°C→36°C) + flash drum (24VG101) |
| 292 | +- Seal gas takeoff (5.4% split) |
| 293 | +- 2-stage export compression (26KA101: 86→259 bar, 26KA102: 258→554 bar) |
| 294 | +- Seal gas JT expansion curve showing 1.35% max condensation at 30 bar |
| 295 | + |
| 296 | +Compressor discharge temperature comparison: |
| 297 | +- 26KA101: NeqSim 126.7°C vs UniSim 117.8°C (75% η_is assumed) |
| 298 | +- 26KA102: NeqSim 85.9°C vs UniSim 83.6°C |
| 299 | +- Suggests UniSim uses ~83-85% isentropic efficiency |
| 300 | + |
| 301 | +--- |
| 302 | + |
| 303 | +## 2026-07-05 — EclipseFluidReadWrite Null BIC Fix, UniSim BIP Extraction |
| 304 | + |
| 305 | +### Bug Fix |
| 306 | + |
| 307 | +| Class | Issue | Fix | |
| 308 | +|-------|-------|-----| |
| 309 | +| `EclipseFluidReadWrite` | `NullPointerException` when E300 file has no BIC section — `kij` array stays `null` | Both `read()` methods now initialize `kij` to zero matrix if BIC section is missing. E300 files without BIC load correctly (all BIPs default to 0.0). | |
| 310 | + |
| 311 | +### Impact on Agents |
| 312 | + |
| 313 | +- **E300 file loading**: Previously required a BIC section or the reader crashed. Now optional (defaults to zero BIPs). However, agents should always include BIC in generated E300 files for accurate results. |
| 314 | +- **UniSim → E300 workflow**: BIPs can now be extracted from UniSim via `pp.Kij.Values` (tuple-of-tuples). See `neqsim-unisim-reader` skill Section 1.1 for the COM access pattern. |
| 315 | + |
| 316 | +### Key Discovery |
| 317 | + |
| 318 | +UniSim COM BIP extraction pattern: |
| 319 | +```python |
| 320 | +kij_obj = pp.Kij # CDispatch (RealFlexVariable) |
| 321 | +raw = kij_obj.Values # tuple-of-tuples (n×n symmetric matrix) |
| 322 | +# Diagonal sentinel = -32767.0, replace with 0.0 |
| 323 | +``` |
| 324 | +- `pp.GetInteractionParameter(i,j)` returns 0.0 for PR-LK (correlation BIPs not accessible this way) |
| 325 | +- `kij_obj.GetValues()` fails — use `.Values` property instead |
219 | 326 |
|
220 | 327 | --- |
221 | 328 |
|
|
0 commit comments