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
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ input: one write atomically assigns exact zero/one availability to the complete
controlled edge set and preserves all per-edge hydraulic models. Do not emulate a
multi-edge line-up with independent availability actions; that permits partial
configurations during candidate application and restoration.
The same selector works for live `WellFlow` injection networks. Update a zone's
lifecycle boundary through `WellFlow.ReservoirLayer.setReservoirPressure(...)` so
the layer pressure and its reservoir stream remain synchronized; do not change the
public pressure field alone in a repeated coupled calculation.
The dedicated `TwoFluidPipeNetwork` already owns storage-node transients; do not
replace it. Live well/injection coupling must reuse WellSystem/WellFlow physics.

Expand Down
17 changes: 15 additions & 2 deletions docs/fielddevelopment/SURF_NETWORK_INTEGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,17 @@ dual-path configurations, exact restoration, positive flow in both redundant pat
network mass conservation. This is synthetic integration evidence, not an outage or
brownfield field qualification.

The same line-up contract is exercised for a live two-zone water injector. Primary,
brownfield and dual-path injection configurations feed one canonical live injection-well
node and preserve the existing `WellFlow` zone allocation contract.
`WellFlow.ReservoirLayer.setReservoirPressure(...)` updates the layer boundary and its
owning reservoir stream together for controlled repeated lifecycle solves. It does not
change injectivity, fracture pressure, composition, topology or hydraulic fidelity. The
synthetic acceptance checks selected-path flow, dual-path flow, zone allocation, fracture
limits, exact route restoration, mass conservation and the expected lower injectivity at
higher reservoir pressure; it is not injectivity, fracture, outage or brownfield
qualification.

The synthetic `FieldToFacilityOptimizationAcceptanceTest` connects two live wells and
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
Expand All @@ -498,8 +509,10 @@ physical limits to obtain a feasible optimizer result.
boundaries, qualified pump/compressor maps, shared host power/capacity constraints and
controlled reservoir-pressure updates without duplicating reservoir ownership.
The canonical multi-edge line-up contract now supports branch/loop outage and brownfield
actions through #4188/#3154 optimization; next exercise it in the larger production and
injection field cases with qualified facility maps and lifecycle evidence.
actions through #4188/#3154 optimization and is exercised on live production and
multi-zone injection wells with controlled reservoir-pressure updates. Next scale the
same contract to qualified pump/compressor maps, shared host constraints and multi-period
lifecycle evidence.
2. Qualify steady arrival temperature, DNV-ST-F101 and DNV-RP-F109 route cases against independent
field, laboratory or commercial-simulator evidence. The software contract for staged
OD/ID/wall/material/installation/insulation/stability alternatives now exists with synthetic
Expand Down
34 changes: 34 additions & 0 deletions src/main/java/neqsim/process/equipment/reservoir/WellFlow.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import neqsim.process.equipment.valve.ThrottlingValve;
import neqsim.process.mechanicaldesign.well.WellFlowMechanicalDesign;
import neqsim.thermo.system.SystemInterface;
import neqsim.util.unit.PressureUnit;

/**
* WellFlow class - Inflow Performance Relationship (IPR) model for reservoir-to-wellbore flow.
Expand Down Expand Up @@ -236,6 +237,39 @@ public ReservoirLayer(String name, StreamInterface stream, double reservoirPress
this.productivityIndex = pi;
}

/**
* Update the reservoir pressure owned by this layer.
*
* <p>
* The layer pressure and its reservoir stream are updated together so repeated coupled field-network calculations
* do not carry contradictory lifecycle state. This method changes only the reservoir boundary; it does not alter
* injectivity/productivity, fracture pressure, fluid composition, or network topology.
* </p>
*
* @param pressure new absolute reservoir pressure
* @param unit pressure unit accepted by {@link PressureUnit}
*/
public void setReservoirPressure(double pressure, String unit) {
double pressureBara = new PressureUnit(pressure, unit).getValue("bara");
if (!Double.isFinite(pressureBara) || pressureBara <= 0.0) {
throw new IllegalArgumentException("Reservoir pressure must be finite and positive");
}
this.reservoirPressure = pressureBara;
if (stream != null) {
stream.setPressure(pressureBara, "bara");
}
}

/**
* Get the reservoir pressure owned by this layer.
*
* @param unit requested pressure unit accepted by {@link PressureUnit}
* @return reservoir pressure in the requested unit
*/
public double getReservoirPressure(String unit) {
return new PressureUnit(reservoirPressure, "bara").getValue(unit);
}

/**
* Set the fracture pressure for this layer.
*
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/neqsim/mcp/engineering-coverage.json
Original file line number Diff line number Diff line change
Expand Up @@ -2869,7 +2869,7 @@
{"id":"neqsim.process.equipment.reservoir.SimpleReservoir","kind":"class","source":"src/main/java/neqsim/process/equipment/reservoir/SimpleReservoir.java","sourceDigest":"cf508908233aeec20d6ab1b76afe5674218cab06bdbaa885cc7cbd7b18498b16","domain":"process","skills":["neqsim-capability-map","neqsim-dynamic-equipment-implementation","neqsim-field-development","neqsim-integrated-production-and-lifecycle","neqsim-process-extraction","neqsim-production-optimization","neqsim-task-workflow","neqsim-troubleshooting"],"agents":["capability-scout","capacity-increase-screening","dynamic-equipment","engineering-deliverables","extract-process","field-development","gas-processing-refining","mechanical-design","neqsim-test","network-energy-systems","notebook-example","process-model","reaction-engineering","root-cause","rotating-equipment","solve-process","solve-task","thermo-fluid","unisim-reader"],"capabilities":[],"disposition":"review_required"},
{"id":"neqsim.process.equipment.reservoir.TubingPerformance","kind":"class","source":"src/main/java/neqsim/process/equipment/reservoir/TubingPerformance.java","sourceDigest":"f11cc0d0c041ec61600af65a7b8d91df74bb116af658f378c8812f195e5bc0d0","domain":"process","skills":[],"agents":[],"capabilities":[],"disposition":"review_required"},
{"id":"neqsim.process.equipment.reservoir.Well","kind":"class","source":"src/main/java/neqsim/process/equipment/reservoir/Well.java","sourceDigest":"cad09aced79bd293324292757c684a63590b112c61ed5c984de736b5e1c46719","domain":"process","skills":["neqsim-api-patterns","neqsim-capability-map","neqsim-ccs-hydrogen","neqsim-eos-regression","neqsim-field-development","neqsim-field-economics","neqsim-flow-assurance","neqsim-integrated-production-and-lifecycle","neqsim-pipeline-and-terminal-networks","neqsim-platform-modeling","neqsim-process-extraction","neqsim-production-optimization","neqsim-regression-baselines","neqsim-standards-lookup","neqsim-subsea-and-wells","neqsim-task-workflow","neqsim-technical-document-reading","neqsim-tight-reservoir-test-design","neqsim-troubleshooting"],"agents":["capability-scout","capacity-increase-screening","ccs-hydrogen","consequence-analysis","control-system","documentation","dynamic-equipment","emissions-environmental","engineering-deliverables","extract-process","field-development","flow-assurance","gas-processing-refining","gas-quality","literature-scout","lng-value-chain","mechanical-design","neqsim-test","network-energy-systems","notebook-example","optimize","optimize-processmodel","process-model","production-chemistry","pvt-simulation","reaction-engineering","root-cause","rotating-equipment","safety-depressuring","solve-process","solve-task","standards-review","technical-reader","thermo-fluid","unisim-reader","utility-design"],"capabilities":[],"disposition":"review_required"},
{"id":"neqsim.process.equipment.reservoir.WellFlow","kind":"class","source":"src/main/java/neqsim/process/equipment/reservoir/WellFlow.java","sourceDigest":"60c6f8c7ec6f1b86c74643094effa1447c8db3e861e9911acf6dada9cdae9994","domain":"process","skills":["neqsim-dynamic-equipment-implementation","neqsim-field-development","neqsim-pipeline-and-terminal-networks","neqsim-platform-modeling","neqsim-task-workflow","neqsim-troubleshooting"],"agents":["dynamic-equipment","field-development","flow-assurance","gas-processing-refining","neqsim-test","network-energy-systems","notebook-example","optimize-processmodel","process-model","reaction-engineering","root-cause","rotating-equipment","solve-process","solve-task","thermo-fluid","unisim-reader"],"capabilities":[],"disposition":"review_required"},
{"id":"neqsim.process.equipment.reservoir.WellFlow","kind":"class","source":"src/main/java/neqsim/process/equipment/reservoir/WellFlow.java","sourceDigest":"1f069cbbe2bef5c1aea7b1b632b77fa0244c3e6621ec66e552831c58dfedb040","domain":"process","skills":["neqsim-dynamic-equipment-implementation","neqsim-field-development","neqsim-pipeline-and-terminal-networks","neqsim-platform-modeling","neqsim-task-workflow","neqsim-troubleshooting"],"agents":["dynamic-equipment","field-development","flow-assurance","gas-processing-refining","neqsim-test","network-energy-systems","notebook-example","optimize-processmodel","process-model","reaction-engineering","root-cause","rotating-equipment","solve-process","solve-task","thermo-fluid","unisim-reader"],"capabilities":[],"disposition":"review_required"},
{"id":"neqsim.process.equipment.reservoir.WellSystem","kind":"class","source":"src/main/java/neqsim/process/equipment/reservoir/WellSystem.java","sourceDigest":"5a9c015750f7f0c8f8691db3523507db6b58f4dec5c6977e1e98202a17edcca4","domain":"process","skills":["neqsim-field-development","neqsim-integrated-production-and-lifecycle","neqsim-pipeline-and-terminal-networks","neqsim-production-optimization"],"agents":["capacity-increase-screening","field-development","flow-assurance","network-energy-systems"],"capabilities":[],"disposition":"review_required"},
{"id":"neqsim.process.equipment.reservoir.WellUnloadingScreening","kind":"class","source":"src/main/java/neqsim/process/equipment/reservoir/WellUnloadingScreening.java","sourceDigest":"55a5c3c86928d0c6436f7c2017196005640dc354cd799df96c76aa598018e896","domain":"process","skills":[],"agents":[],"capabilities":[],"disposition":"review_required"},
{"id":"neqsim.process.equipment.separator.CryogenicSeparator","kind":"class","source":"src/main/java/neqsim/process/equipment/separator/CryogenicSeparator.java","sourceDigest":"f5ebcf4f51f5d5889167ac94a2994b685920fd2c5af96d9a178d1727a3c51098","domain":"process","skills":["neqsim-produced-water-and-solids-separation"],"agents":["gas-processing-refining","production-chemistry"],"capabilities":[],"disposition":"review_required"},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,73 @@ void switchesCanonicalMultiEdgeRouteConfigurationsThroughAutomation() {
unit.getHydraulicNetwork().getPipe("primary flowline").setAvailability(1.0);
}

/** Live injection must switch complete line-ups and respond to controlled reservoir-pressure updates. */
@Test
void switchesLiveInjectionRouteConfigurationsAndReservoirState() {
WellFlow injectionWell = createConfiguredInjectionWell();
FieldWellNetworkProcessUnit unit = createConfiguredInjectionUnit(injectionWell);
ProcessSystem injection = new ProcessSystem("injection field");
injection.add(unit);
ProcessModel model = new ProcessModel();
model.add("Injection", injection);
model.run();

assertTrue(model.isModelConverged());
assertTrue(unit.solved(), unit.getLastCouplingResult().getMessage());
String address = "Injection::configured injection network.configuration.injector route.selection";
ProcessAutomation automation = model.getAutomation();
assertTrue(automation.isWritableAddress(address));
double primaryRate = unit.getLastCouplingResult().getWellResults().get(0).getAppliedRateKgS();
assertTrue(primaryRate > 0.0);
assertFalse(injectionWell.getZoneFractureRisk()[0]);
assertFalse(injectionWell.getZoneFractureRisk()[1]);

automation.setVariableValue(address, 1.0, "-");
model.run();
assertTrue(unit.solved(), couplingDiagnostic(unit));
assertTrue(model.isModelConverged(), couplingDiagnostic(unit));
double brownfieldRate = unit.getLastCouplingResult().getWellResults().get(0).getAppliedRateKgS();
assertTrue(brownfieldRate > 0.0);
assertEquals(0.0, unit.getHydraulicNetwork().getPipe("primary injection flowline").getFlowRate(), 1.0e-12);
assertTrue(unit.getHydraulicNetwork().getPipe("brownfield injection flowline").getFlowRate() > 0.0);

automation.setVariableValue(address, 2.0, "-");
model.run();
assertTrue(unit.solved(), couplingDiagnostic(unit));
assertTrue(model.isModelConverged(), couplingDiagnostic(unit));
double dualPathRate = unit.getLastCouplingResult().getWellResults().get(0).getAppliedRateKgS();
assertTrue(dualPathRate > 0.0);
assertTrue(unit.getHydraulicNetwork().getPipe("primary injection flowline").getFlowRate() > 0.0);
assertTrue(unit.getHydraulicNetwork().getPipe("brownfield injection flowline").getFlowRate() > 0.0);
assertEquals(0.0, unit.getLastCouplingResult().getMassBalanceResidualKgS(), 1.0e-8);
assertEquals(1.0, injectionWell.getZoneAllocationFractions()[0] + injectionWell.getZoneAllocationFractions()[1],
1.0e-12);

automation.setVariableValue(address, 0.0, "-");
model.run();
assertEquals(primaryRate, unit.getLastCouplingResult().getWellResults().get(0).getAppliedRateKgS(),
primaryRate * 1.0e-4);
injectionWell.getLayer(0).setReservoirPressure(115.0, "bara");
injectionWell.getLayer(1).setReservoirPressure(125.0, "bara");
assertEquals(115.0, injectionWell.getLayer(0).getReservoirPressure("bara"), 0.0);
assertEquals(115.0, injectionWell.getLayer(0).stream.getPressure("bara"), 0.0);
model.run();
double higherReservoirPressureRate = unit.getLastCouplingResult().getWellResults().get(0).getAppliedRateKgS();
assertTrue(higherReservoirPressureRate < primaryRate,
"Higher reservoir pressure must reduce injectivity at unchanged source pressure and route");
assertTrue(unit.solved(), unit.getLastCouplingResult().getMessage());
assertFalse(injectionWell.getZoneFractureRisk()[0]);
assertFalse(injectionWell.getZoneFractureRisk()[1]);
}

/** Build a compact failure diagnostic for coupled acceptance assertions. */
private String couplingDiagnostic(FieldWellNetworkProcessUnit unit) {
return unit.getLastCouplingResult().getMessage() + "; iterations=" + unit.getLastCouplingResult().getIterations()
+ "; rate residual=" + unit.getLastCouplingResult().getMaximumRateResidualKgS() + "; pressure residual="
+ unit.getLastCouplingResult().getMaximumPressureResidualBar() + "; network residual="
+ unit.getLastCouplingResult().getNetworkResidualPa();
}

/** The process unit must remain unsolved when the complete coupled convergence contract is not met. */
@Test
void incompleteCouplingFailsClosed() {
Expand Down Expand Up @@ -335,6 +402,61 @@ private FieldWellNetworkProcessUnit createConfiguredUnit() {
"synthetic direct, brownfield and redundant host line-ups");
}

/** Create a live two-zone injector for route and lifecycle acceptance. */
private WellFlow createConfiguredInjectionWell() {
WellFlow well = new WellFlow("configured injection well");
well.setInletStream(new Stream("injection water", water(180.0)));
well.addInjectionZone("target", new Stream("target reservoir zone", water(100.0)), 100.0, 7.0e-8, 205.0);
well.addInjectionZone("secondary", new Stream("secondary reservoir zone", water(110.0)), 110.0, 3.0e-8, 200.0);
well.setTargetZone("target");
return well;
}

/** Create primary, brownfield and dual-path injection line-ups on one canonical graph. */
private FieldWellNetworkProcessUnit createConfiguredInjectionUnit(WellFlow well) {
FieldNetworkTopology topology = new FieldNetworkTopology("configured injection network hydraulics");
LoopedPipeNetwork network = topology.getHydraulicNetwork();
network.setFluidTemplate(water(180.0));
network.setSolverType(LoopedPipeNetwork.SolverType.NEWTON_RAPHSON);
network.setTolerance(3000.0);
topology.addPressureSource("injection source", "P-401-D", NodeRole.INJECTION_SOURCE, Service.INJECTION, 180.0, 0.0,
0.0);
topology.addJunction("primary injection PLEM", "PLEM-I401", NodeRole.PLEM, Service.INJECTION, 0.0);
topology.addJunction("brownfield injection tie-in", "TI-I401", NodeRole.BROWNFIELD_TIE_IN, Service.INJECTION, 0.0);
topology.addLiveWellNode("injector", "XT-I401", NodeRole.INJECTION_WELL, Service.INJECTION, 145.0, 0.0);
topology.addPipe("primary injection flowline", "WI-401", EdgeRole.FLOWLINE, Service.INJECTION,
FlowDirection.FROM_TO, "injection source", "primary discharge", "primary injection PLEM", "inlet", 900.0, 0.22,
LoopedPipeNetwork.PipeModelType.DARCY_WEISBACH);
topology.addPipe("primary injection jumper", "JU-I401", EdgeRole.JUMPER, Service.INJECTION, FlowDirection.FROM_TO,
"primary injection PLEM", "outlet", "injector", "primary injection", 700.0, 0.22,
LoopedPipeNetwork.PipeModelType.DARCY_WEISBACH);
topology.addPipe("brownfield injection flowline", "WI-402", EdgeRole.TIE_IN, Service.INJECTION,
FlowDirection.FROM_TO, "injection source", "brownfield discharge", "brownfield injection tie-in", "inlet",
2800.0, 0.16, LoopedPipeNetwork.PipeModelType.DARCY_WEISBACH);
topology.addPipe("brownfield injection jumper", "JU-I402", EdgeRole.JUMPER, Service.INJECTION,
FlowDirection.FROM_TO, "brownfield injection tie-in", "outlet", "injector", "brownfield injection", 900.0, 0.16,
LoopedPipeNetwork.PipeModelType.DARCY_WEISBACH);
network.getPipe("brownfield injection flowline").setAvailability(0.0);
network.getPipe("brownfield injection jumper").setAvailability(0.0);

FieldWellNetworkCoupler coupler = new FieldWellNetworkCoupler(topology);
coupler.setMaximumIterations(200);
coupler.setTolerances(2.0e-7, 3.0e-5);
coupler.setRelaxationFactor(0.6);
coupler.bindInjectionWell("injector", well);
return new FieldWellNetworkProcessUnit("configured injection network", coupler)
.registerRouteConfigurationSelector("injector route",
Arrays.asList(
new FieldWellNetworkProcessUnit.RouteConfiguration("primary",
Arrays.asList("primary injection flowline", "primary injection jumper")),
new FieldWellNetworkProcessUnit.RouteConfiguration("brownfield",
Arrays.asList("brownfield injection flowline", "brownfield injection jumper")),
new FieldWellNetworkProcessUnit.RouteConfiguration("dual path",
Arrays.asList("primary injection flowline", "primary injection jumper",
"brownfield injection flowline", "brownfield injection jumper"))),
"synthetic primary, brownfield and redundant injection line-ups");
}

/** Assert exact availability on the four controlled configuration edges. */
private void assertConfiguration(FieldWellNetworkProcessUnit unit, double primaryFlowline, double primaryRiser,
double brownfieldFlowline, double brownfieldRiser) {
Expand All @@ -359,4 +481,18 @@ private SystemInterface gas(double pressureBara) {
fluid.init(0);
return fluid;
}

/**
* Create a single-component water fluid.
*
* @param pressureBara pressure in bara
* @return initialized water fluid
*/
private SystemInterface water(double pressureBara) {
SystemInterface fluid = new SystemSrkEos(298.15, pressureBara);
fluid.addComponent("water", 1.0);
fluid.setMixingRule("classic");
fluid.init(0);
return fluid;
}
}
Loading
Loading