Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/skills/neqsim-production-optimization/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,7 @@ Map<String, Double> breakdown = decom.getBreakdown();
| Interpolating a discrete operating choice or mutating a candidate without a restoration token | A solver can invent an invalid line-up, target the wrong area, hide conversion resolution behind a false rejection, or leave a rejected point in the model | Define a provenance-bearing `ProcessModelOperatingAction` with an area-qualified address. Enumerate `getAllowedValues()` for discrete actions, capture the baseline before mutation, and restore the identity-matched state after candidate validation. Keep the strict default read-back comparison unless the exact automation conversion or control tag has a known resolution; then declare it with `withReadBackTolerance(absolute, relative, provenance)` and retain the numeric residual/tolerance diagnostics. Capability or read-back success is not process feasibility or operating approval. |
| Screening a well or gathering action from a live model without rerunning and restoring the baseline | A rejected candidate can leak into the next evaluation, an exact hydraulic limit can be missing, or evidence can be used outside its validity range | Configure a zero-parameter `ProcessModelSimulationEvaluator`, wrap one action with `ProcessModelOperatingActionEvaluator`, and bind every required hydraulic constraint by exact area/equipment/constraint identity and provenance. Require candidate convergence, finite utilization/margin, no violation, in-range evidence when a range is declared, and successful baseline restoration and reconvergence. Use separate model instances for parallel candidates. |
| Applying coupled well-allocation actions independently or simulating a partial vector | A later rejected write can leave earlier wells changed, shared limits can be evaluated at an impossible mixed state, and rollback evidence is lost | Use one provenance-bearing `ProcessModelOperatingActionSetEvaluator` with unique action IDs and addresses. Require exact per-well and shared constraints. Inspect every `ActionCandidateEvidence`, reject any partial application, and require reverse-order restoration plus baseline reconvergence before the next candidate. Validate total/component balance and nearby monotonic constraint response; the transaction does not choose or approve the allocation. |
| Routing a well by splitting its solved outlet downstream or writing independent HP/LP availabilities | Separator pressure does not feed back into well deliverability, both routes can be open or closed, and rejected candidates can leave an invalid line-up | Register the existing alternative edges with `FieldWellNetworkProcessUnit.registerExclusiveRouteSelector(...)` and use one discrete `route.<selector>.selection` action. The selector applies an exact one-hot line-up atomically; rerun the coupled model, require finite convergence and per-route constraints, and verify exact selection restoration. Zero availability is an exact closed edge in the Newton solve; fractional values remain deratings. |
| Reducing fixed-total allocation to a score-only callback or continuing after rollback failure | Constraint provenance and rejected-point evidence disappear, a shared total can drift, or later trials can start from a corrupted process state | For continuous common-unit actions, compose the atomic action set with `ProcessModelAllocationOptimizer`. Declare the fixed total, seed, objective row, budget, transfer tolerance and objective tolerance provenance. Require `isModelRecovered()`, retain the full candidate trace, and inspect utilization-ranked constraints at the best feasible and best sampled objective points. Treat the sampled opportunity gap and transfer convergence as local search diagnostics, not production loss, global optimality, a shadow price, or approval. |
| Labelling normalized installed-capacity utilization or margin with RPM, kW, or a flow unit | External solvers and AI agents can mistake a fraction for physical headroom, resample a mutable supplier, or join relief to only a selected hydraulic subset | Register capacities with `addEquipmentCapacityConstraints()` and retain `EvaluationResult.getInstalledEquipmentCapacityEvidence()`. Treat `getNormalizedUnit()` as `"1"`; use `getPhysicalUnit()` only for current, applicable limit, physical margin, and required relief. Preserve qualified identity, origin, evidence status, and validity applicability. |
| Joining an improving rejected allocation to mutable constraint definitions or calling one sampled gap causal debottleneck value | Archived rows can be relabelled, normalized capacity margins can be mistaken for engineering-unit relief, and coupled or weak evidence can look isolated | Retain `CandidateSetEvaluationResult.getObjectiveEvidence()` and `getConstraintEvidence()`, then call `ProcessModelAllocationBottleneckAnalyzer.analyze(search)`. Require complete recovery, inspect every hard violation and its evidence class, and keep unlike relief units separate. Treat results as sampled non-causal associations, not capacity sizing, production loss, shadow price, economics, or approval. |
Expand Down
33 changes: 26 additions & 7 deletions docs/fielddevelopment/SURF_NETWORK_INTEGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,14 +253,33 @@ available under the process-unit name. A copied unit rebuilds the topology defin
copies the bound `WellSystem`/`WellFlow` objects and rebinds them before execution;
accepted runtime state is not shared with the source unit.

For a well that can be routed to mutually exclusive separator pressure levels, register
the existing alternative network edges with
`FieldWellNetworkProcessUnit.registerExclusiveRouteSelector(...)`. Automation then
exposes `route.<selector>.selection` as a zero-based discrete input. One verified write
atomically makes the selected edge fully available and every alternative unavailable;
invalid fractional selections are rejected without changing the line-up. A zero edge
availability is an exact closed route in the Newton network solve, while fractional
availability remains a hydraulic derating. The selected HP or LP fixed-pressure sink
therefore feeds back through the same live well/network coupling instead of acting as a
downstream flow split. Copies preserve selector definitions but keep independent edge
state.

The synthetic `FieldToFacilityOptimizationAcceptanceTest` connects two live wells and
well chokes through individual gathering lines to a host, then routes the stable host
stream between two separators and mapped compressors on a common shaft. It exercises
configured well/line/separator operating limits, compressor surge/stonewall evidence,
driver and gearbox power, a shared plant-power budget, a transactional route action,
baseline restoration and an operating-envelope slice. A route rejected by compressor
or other hard evidence remains rejected; the test does not relax physical limits to
obtain a feasible optimizer result.
well chokes through exclusive per-well routes to distinct 45 bara HP and 30 bara LP
fixed-pressure sinks. Those stable sink streams feed separators, compressor-suction
scrubbers and mapped compressors on a common shaft before a pressure-controlled 100 bara
gas-export boundary. The workflow exercises configured
well/per-route-line/separator/scrubber operating limits, compressor surge/stonewall
evidence, driver and gearbox power, a shared plant-power budget, transactional discrete
route actions, qualified export-pressure evidence, exact action restoration and an
operating-envelope slice. A second deterministic acceptance case instantiates ten live
wells with twenty alternative route edges and verifies complete well, line, vessel,
shaft, shared-power and export-boundary evidence without changing the two-well physical
model or introducing a separate optimizer. The lower
pressure route must reduce well backpressure and increase deliverability. A route
rejected by compressor or other hard evidence remains rejected; the test does not relax
physical limits to obtain a feasible optimizer result.

## Dependency-ordered continuation

Expand Down
67 changes: 66 additions & 1 deletion src/main/java/neqsim/process/automation/ProcessAutomation.java
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,9 @@ public double getVariableValue(String address, String unitOfMeasure) {

ProcessEquipmentInterface unit = findUnit(areaName, unitName);

if (unit instanceof FieldWellNetworkProcessUnit && parts.length == 3 && "route".equals(parts[1])) {
return getFieldRouteProperty((FieldWellNetworkProcessUnit) unit, parts[2], unitOfMeasure);
}
LoopedPipeNetwork addressableNetwork = getAddressableNetwork(unit);
if (addressableNetwork != null && parts.length == 3) {
return getNetworkProperty(addressableNetwork, parts[1], parts[2], unitOfMeasure);
Expand Down Expand Up @@ -648,6 +651,11 @@ public void setVariableValue(String address, double value, String unitOfMeasure)

ProcessEquipmentInterface unit = findUnit(areaName, unitName);

if (unit instanceof FieldWellNetworkProcessUnit && parts.length == 3 && "route".equals(parts[1])) {
setFieldRouteProperty((FieldWellNetworkProcessUnit) unit, parts[2], value, unitOfMeasure);
this.dirty = true;
return;
}
LoopedPipeNetwork addressableNetwork = getAddressableNetwork(unit);
if (addressableNetwork != null && parts.length == 3) {
setNetworkProperty(addressableNetwork, parts[1], parts[2], value, unitOfMeasure);
Expand Down Expand Up @@ -875,7 +883,17 @@ private List<SimulationVariable> buildVariableList(String unitName, ProcessEquip

LoopedPipeNetwork addressableNetwork = getAddressableNetwork(unit);
if (addressableNetwork != null) {
return enrichVariableMetadata(buildNetworkVariableList(unitName, addressableNetwork));
List<SimulationVariable> networkVariables = buildNetworkVariableList(unitName, addressableNetwork);
if (unit instanceof FieldWellNetworkProcessUnit) {
FieldWellNetworkProcessUnit fieldUnit = (FieldWellNetworkProcessUnit) unit;
for (String selectorName : fieldUnit.getExclusiveRouteSelectorNames()) {
networkVariables.add(new SimulationVariable(unitName + ".route." + selectorName + ".selection",
"route." + selectorName + ".selection", VariableType.INPUT, "-",
"Exclusive route index; provenance: " + fieldUnit.getExclusiveRouteProvenance(selectorName)).withBounds(
Double.valueOf(0.0), Double.valueOf(fieldUnit.getExclusiveRouteEdges(selectorName).size() - 1.0)));
}
}
return enrichVariableMetadata(networkVariables);
}

// Universal equipment-level outputs
Expand Down Expand Up @@ -1197,6 +1215,53 @@ private LoopedPipeNetwork getAddressableNetwork(ProcessEquipmentInterface unit)
return null;
}

/**
* Read one field-unit exclusive route selection.
*
* @param unit field well/network process unit
* @param targetAndProperty selector name and selection property
* @param unitOfMeasure requested dimensionless unit
* @return zero-based selected route index
*/
private double getFieldRouteProperty(FieldWellNetworkProcessUnit unit, String targetAndProperty,
String unitOfMeasure) {
String[] targetProperty = splitNetworkTargetProperty(targetAndProperty);
requireDimensionlessUnit(unitOfMeasure);
if (!"selection".equals(targetProperty[1])) {
throw new IllegalArgumentException("Unknown field route property: " + targetProperty[1]);
}
return unit.getExclusiveRouteSelection(targetProperty[0]);
}

/**
* Write one field-unit exclusive route selection.
*
* @param unit field well/network process unit
* @param targetAndProperty selector name and selection property
* @param value zero-based selected route index
* @param unitOfMeasure requested dimensionless unit
*/
private void setFieldRouteProperty(FieldWellNetworkProcessUnit unit, String targetAndProperty, double value,
String unitOfMeasure) {
String[] targetProperty = splitNetworkTargetProperty(targetAndProperty);
requireDimensionlessUnit(unitOfMeasure);
if (!"selection".equals(targetProperty[1])) {
throw new IllegalArgumentException("Unknown field route property: " + targetProperty[1]);
}
unit.setExclusiveRouteSelection(targetProperty[0], value);
}

/**
* Require a dimensionless route-selector unit.
*
* @param unitOfMeasure requested unit
*/
private void requireDimensionlessUnit(String unitOfMeasure) {
if (unitOfMeasure != null && !unitOfMeasure.trim().isEmpty() && !"-".equals(unitOfMeasure)) {
throw new IllegalArgumentException("Exclusive route selection uses the dimensionless '-' unit");
}
}

/**
* Read an internal looped-network address.
*
Expand Down
Loading
Loading