Skip to content

Commit 9825ccf

Browse files
committed
Merge branch 'improve-unisim-reader' of https://github.com/equinor/neqsim into improve-unisim-reader
2 parents 8c7c29c + 0a1cdaa commit 9825ccf

File tree

1 file changed

+149
-42
lines changed

1 file changed

+149
-42
lines changed

CHANGELOG_AGENT_NOTES.md

Lines changed: 149 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
---
1111

1212
## 2026-07-08 — UniSim Reader: Orientation Detection (GasScrubber)
13+
<<<<<<< HEAD
1314

1415
### Vertical Separator → GasScrubber Mapping
1516

@@ -167,55 +168,161 @@ raw = kij_obj.Values # tuple-of-tuples (n×n symmetric matrix)
167168
---
168169

169170
## 2026-04-08 — IEC 81346 Reference Designation Support
171+
=======
172+
>>>>>>> 0a1cdaa75a2293eaa9447f3df1b0ec9460a74c1e
170173
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:
197204

198205
```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.
202249

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
207255

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());
210285
```
211286

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
219326

220327
---
221328

0 commit comments

Comments
 (0)