Class LoopedPipeNetwork

All Implemented Interfaces:
Serializable, Runnable, ProcessEquipmentInterface, ProcessElementInterface, SimulationInterface, NamedInterface

public class LoopedPipeNetwork extends ProcessEquipmentBaseClass
Pipeline network supporting looped topologies with Hardy Cross solver.

This class extends the basic pipeline network concept to support looped configurations commonly found in water distribution systems, oil and gas gathering networks, and gas transmission systems. The Hardy Cross method is used to iteratively solve for flow distribution in networks with loops.

Hardy Cross Method

The Hardy Cross method is an iterative technique for solving networks with loops:

  1. Initial flow estimates are made for each pipe
  2. Independent loops are identified using DFS spanning tree algorithm
  3. For each loop, calculate head loss imbalance: ΔH = ∑(h·sign)
  4. Calculate flow correction: ΔQ = -ΔH / ∑(|dh/dQ|)
  5. Apply corrections to all pipes in each loop
  6. Repeat until convergence (ΔH < tolerance for all loops)

Network Topology

The network supports:

  • Multiple source nodes (wells, compressor stations)
  • Multiple sink nodes (customers, export terminals)
  • Junction nodes where pipes connect
  • Looped configurations for redundancy

Example Usage


// Create a simple ring main network
SystemInterface gas = new SystemSrkEos(298.15, 50.0);
gas.addComponent("methane", 0.9);
gas.addComponent("ethane", 0.1);
gas.setMixingRule("classic");

LoopedPipeNetwork network = new LoopedPipeNetwork("ring main");
network.setFluidTemplate(gas);

// Add nodes
network.addSourceNode("supply", 50.0, 1000.0); // 50 bar, 1000 kg/hr
network.addJunctionNode("A");
network.addJunctionNode("B");
network.addJunctionNode("C");
network.addSinkNode("customer1", 100.0); // 100 kg/hr demand
network.addSinkNode("customer2", 200.0);

// Connect with pipes (creates loops)
network.addPipe("supply", "A", "pipe1", 1000.0, 0.3);
network.addPipe("A", "B", "pipe2", 500.0, 0.2);
network.addPipe("B", "C", "pipe3", 500.0, 0.2);
network.addPipe("C", "A", "pipe4", 500.0, 0.2); // Creates a loop
network.addPipe("B", "customer1", "pipe5", 200.0, 0.15);
network.addPipe("C", "customer2", "pipe6", 200.0, 0.15);

// Solve using Hardy Cross
network.setSolverType(SolverType.HARDY_CROSS);
network.setTolerance(1e-6);
network.run();

// Get results
System.out.println("Converged in " + network.getIterationCount() + " iterations");
for (String pipeName : network.getPipeNames()) {
  System.out.println(pipeName + ": " + network.getPipeFlowRate(pipeName) + " kg/hr");
}

Version:
1.0
Author:
Even Solbraa
See Also:
  • Field Details

    • serialVersionUID

      private static final long serialVersionUID
      See Also:
    • logger

      private static final org.apache.logging.log4j.Logger logger
      Logger for this class.
    • nodes

      private final transient Map<String, LoopedPipeNetwork.NetworkNode> nodes
    • pipes

      private final transient Map<String, LoopedPipeNetwork.NetworkPipe> pipes
    • pipeNames

      private final List<String> pipeNames
    • solverType

      private LoopedPipeNetwork.SolverType solverType
    • pipeModelType

      private LoopedPipeNetwork.PipeModelType pipeModelType
    • tolerance

      private double tolerance
    • maxIterations

      private int maxIterations
    • relaxationFactor

      private double relaxationFactor
    • loops

      private List<NetworkLoop> loops
    • iterationCount

      private int iterationCount
    • maxResidual

      private double maxResidual
    • converged

      private boolean converged
    • fluidTemplate

      private SystemInterface fluidTemplate
    • massBalanceError

      private double massBalanceError
    • feedStreams

      private final transient Map<String, StreamInterface> feedStreams
    • outletStreams

      private final transient Map<String, StreamInterface> outletStreams
    • nodeFluidMap

      private final transient Map<String, SystemInterface> nodeFluidMap
    • erosionalViolations

      private final transient List<String> erosionalViolations
    • nodeGasQuality

      private final transient Map<String,double[]> nodeGasQuality
    • nodeOilQuality

      private final transient Map<String,double[]> nodeOilQuality
    • nodePressureLimits

      private final transient Map<String,double[]> nodePressureLimits
    • elementFlowLimits

      private final transient Map<String,double[]> elementFlowLimits
    • constraintViolations

      private final transient List<String> constraintViolations
    • fuelGasHeatRate

      private double fuelGasHeatRate
    • totalFuelGasRate

      private double totalFuelGasRate
    • nodeWaterBalance

      private final transient Map<String,double[]> nodeWaterBalance
    • totalWaterProduction

      private double totalWaterProduction
    • totalWaterInjection

      private double totalWaterInjection
    • pipeSandResults

      private final transient Map<String,double[]> pipeSandResults
    • sandViolations

      private final transient List<String> sandViolations
    • maxAllowableSandRate

      private double maxAllowableSandRate
    • maxAllowableErosionRate

      private double maxAllowableErosionRate
    • pipeCorrosionResults

      private final transient Map<String,double[]> pipeCorrosionResults
    • corrosionViolations

      private final transient List<String> corrosionViolations
    • minAllowableWallLife

      private double minAllowableWallLife
    • emissionsResults

      private final transient Map<String,double[]> emissionsResults
    • co2EmissionFactor

      private double co2EmissionFactor
    • totalCO2Emissions

      private double totalCO2Emissions
    • methaneSlipFactor

      private double methaneSlipFactor
    • wellOilPrices

      private final transient Map<String,Double> wellOilPrices
      Per-well oil price for revenue-based optimization (USD/kg).
    • wellGasPrices

      private final transient Map<String,Double> wellGasPrices
      Per-well gas price for revenue-based optimization (USD/kg).
    • wellAllocationResults

      private final transient Map<String,double[]> wellAllocationResults
      Last optimization results per well.
    • topsideModel

      private transient ProcessSystem topsideModel
      The topside ProcessSystem coupled to the network outlet.
    • topsideSinkNodeName

      private String topsideSinkNodeName
      Name of the sink node used as the topside coupling point.
    • maxSeparatorUtilization

      private double maxSeparatorUtilization
      Maximum separator capacity utilization (0.0 to 1.0). Default 0.90.
    • maxCompressorPowerMW

      private double maxCompressorPowerMW
      Maximum compressor power in MW. Default 999 (unlimited).
    • couplingToleranceBar

      private double couplingToleranceBar
      Pressure tolerance for topside coupling convergence (bar). Default 0.5.
    • maxCouplingIterations

      private int maxCouplingIterations
      Maximum coupling iterations. Default 20.
    • attachedReservoirs

      private Map<String, LoopedPipeNetwork.ReservoirAttachment> attachedReservoirs
      Map from source node name to reservoir attachment.
  • Constructor Details

    • LoopedPipeNetwork

      public LoopedPipeNetwork(String name)
      Create a new looped pipe network.
      Parameters:
      name - network name
    • LoopedPipeNetwork

      public LoopedPipeNetwork(String name, StreamInterface feedStream)
      Create a new looped pipe network with a single feed stream.

      The feed stream's fluid is used as the network fluid template. The stream is associated with the first source node added to the network. Call setFeedStream(String, StreamInterface) to explicitly bind a stream to a named source node.

      Parameters:
      name - network name
      feedStream - inlet stream providing fluid composition, temperature and pressure
  • Method Details

    • setFeedStream

      public void setFeedStream(String sourceNodeName, StreamInterface stream)
      Associate a feed stream with a named source node.

      When run(UUID) is called, the stream's pressure and flow rate are read and applied to the named source node. If the fluid template has not been set, it is derived from the first feed stream.

      Parameters:
      sourceNodeName - name of an existing source node
      stream - the feed stream
    • getOutletStream

      public StreamInterface getOutletStream(String sinkNodeName)
      Get the outlet stream for a named sink node.

      The returned stream contains the solved pressure, temperature, flow rate, and fluid composition at the sink node. It is updated each time run(UUID) completes and can be connected to downstream process equipment.

      Parameters:
      sinkNodeName - name of a sink node
      Returns:
      outlet stream, or null if the node does not exist or has not been solved
    • getOutletStream

      public StreamInterface getOutletStream()
      Get the default outlet stream.

      Returns the outlet stream of the first sink node in the network. This provides a convenient single-outlet accessor for networks with one delivery point.

      Returns:
      outlet stream of the first sink node, or null if no sinks exist
    • getSourceNodeStream

      public StreamInterface getSourceNodeStream(String sourceNodeName)
      Get the outlet stream for a named source node (for IPR well sources where flow is computed).
      Parameters:
      sourceNodeName - name of a source node
      Returns:
      outlet stream at the source node, or null if not yet solved
    • getInletStreams

      public List<StreamInterface> getInletStreams()
      Returns all inlet streams connected to this equipment. Subclasses override to report their specific inlets. Used by graph builders, DEXPI export, and auto-instrumentation to discover topology without instanceof checks.
      Returns:
      unmodifiable list of inlet streams (empty by default)
    • getOutletStreams

      public List<StreamInterface> getOutletStreams()
      Returns all outlet streams produced by this equipment. Subclasses override to report their specific outlets. Used by graph builders, DEXPI export, and auto-instrumentation to discover topology without instanceof checks.
      Returns:
      unmodifiable list of outlet streams (empty by default)
    • setFluidTemplate

      public void setFluidTemplate(SystemInterface fluid)
      Set the fluid template for the network.
      Parameters:
      fluid - the fluid system to use as template
    • getFluidTemplate

      public SystemInterface getFluidTemplate()
      Get the fluid template.
      Returns:
      the fluid template
    • setPipeModelType

      public void setPipeModelType(LoopedPipeNetwork.PipeModelType type)
      Set the pipe flow model type.
      Parameters:
      type - pipe model type (DARCY_WEISBACH or BEGGS_BRILL)
    • getPipeModelType

      public LoopedPipeNetwork.PipeModelType getPipeModelType()
      Get the pipe flow model type.
      Returns:
      pipe model type
    • addSourceNode

      public void addSourceNode(String name, double pressureBar, double flowRateKgHr)
      Add a source node to the network.
      Parameters:
      name - node name
      pressureBar - fixed pressure in bara
      flowRateKgHr - supply flow rate in kg/hr (optional, for validation)
    • addSourceNode

      public void addSourceNode(String name, double pressureBar, double flowRateKgHr, double elevationM)
      Add a source node with specified elevation.
      Parameters:
      name - node name
      pressureBar - fixed pressure in bara
      flowRateKgHr - supply flow rate in kg/hr
      elevationM - node elevation in meters
    • addSinkNode

      public void addSinkNode(String name, double demandKgHr)
      Add a sink node (demand point) to the network.
      Parameters:
      name - node name
      demandKgHr - demand flow rate in kg/hr
    • addSinkNode

      public void addSinkNode(String name, double demandKgHr, double elevationM)
      Add a sink node with specified elevation.
      Parameters:
      name - node name
      demandKgHr - demand flow rate in kg/hr
      elevationM - node elevation in meters
    • addFixedPressureSinkNode

      public void addFixedPressureSinkNode(String name, double pressureBar)
      Add a fixed-pressure sink node (delivery point with specified pressure).

      In this mode the solver determines the flow rate that the network can deliver to this node given the upstream source pressures and network resistances. This is the "pressure-pressure" operating mode, analogous to PIPESIM's fixed-pressure deliverability calculation.

      Parameters:
      name - node name
      pressureBar - delivery pressure in bara
    • addFixedPressureSinkNode

      public void addFixedPressureSinkNode(String name, double pressureBar, double elevationM)
      Add a fixed-pressure sink node with specified elevation.
      Parameters:
      name - node name
      pressureBar - delivery pressure in bara
      elevationM - node elevation in meters
    • addJunctionNode

      public void addJunctionNode(String name)
      Add a junction node to the network.
      Parameters:
      name - node name
    • addJunctionNode

      public void addJunctionNode(String name, double elevationM)
      Add a junction node with specified elevation.
      Parameters:
      name - node name
      elevationM - node elevation in meters
    • addPipe

      public LoopedPipeNetwork.NetworkPipe addPipe(String fromNode, String toNode, String pipeName, double lengthM, double diameterM)
      Add a pipe connecting two nodes.
      Parameters:
      fromNode - source node name
      toNode - target node name
      pipeName - pipe name
      lengthM - pipe length in meters
      diameterM - pipe inner diameter in meters
      Returns:
      the created pipe
    • addPipe

      public LoopedPipeNetwork.NetworkPipe addPipe(String fromNode, String toNode, String pipeName, double lengthM, double diameterM, double roughnessM)
      Add a pipe with specified roughness.
      Parameters:
      fromNode - source node name
      toNode - target node name
      pipeName - pipe name
      lengthM - pipe length in meters
      diameterM - pipe inner diameter in meters
      roughnessM - pipe roughness in meters
      Returns:
      the created pipe
    • addWellIPR

      public LoopedPipeNetwork.NetworkPipe addWellIPR(String reservoirNode, String wellboreNode, String elementName, double productivityIndexSI, boolean isGas)
      Add a well IPR element using the productivity index model.

      Creates a network element from reservoir node to wellbore node representing the inflow performance relationship. For gas wells, uses P^2 drawdown: q = PI * (Pr^2 - Pwf^2). For oil wells, uses linear drawdown: q = PI * (Pr - Pwf).

      Parameters:
      reservoirNode - name of reservoir (source) node (must be fixed-pressure)
      wellboreNode - name of wellbore/bottomhole node
      elementName - element name
      productivityIndexSI - productivity index in kg/s/Pa (oil) or kg/s/Pa^2 (gas)
      isGas - true for gas wells (P^2 formulation)
      Returns:
      the created element
    • addWellIPRVogel

      public LoopedPipeNetwork.NetworkPipe addWellIPRVogel(String reservoirNode, String wellboreNode, String elementName, double qmaxKgS)
      Add a well IPR element using the Vogel model for solution-gas-drive oil wells.
      Parameters:
      reservoirNode - name of reservoir (source) node
      wellboreNode - name of wellbore node
      elementName - element name
      qmaxKgS - absolute open flow in kg/s
      Returns:
      the created element
    • addWellIPRFetkovich

      public LoopedPipeNetwork.NetworkPipe addWellIPRFetkovich(String reservoirNode, String wellboreNode, String elementName, double cCoeff, double nExp)
      Add a well IPR element using the Fetkovich model for gas wells.
      Parameters:
      reservoirNode - name of reservoir (source) node
      wellboreNode - name of wellbore node
      elementName - element name
      cCoeff - Fetkovich coefficient C in kg/s/Pa^(2n)
      nExp - Fetkovich exponent n (0.5-1.0)
      Returns:
      the created element
    • addChoke

      public LoopedPipeNetwork.NetworkPipe addChoke(String fromNode, String toNode, String elementName, double kv, double openingPercent)
      Add a production choke element between two nodes.

      The choke uses a simplified valve equation: Q = Kv * (opening/100) * sqrt(dP * rho). For critical (choked) flow, the pressure drop is limited by the critical pressure ratio.

      Parameters:
      fromNode - upstream node name
      toNode - downstream node name
      elementName - element name
      kv - valve flow coefficient in m3/hr per sqrt(bar)
      openingPercent - valve opening (0-100%)
      Returns:
      the created element
    • addTubing

      public LoopedPipeNetwork.NetworkPipe addTubing(String bottomNode, String topNode, String elementName, double lengthM, double diameterM, double inclinationDeg)
      Add wellbore tubing element (vertical lift performance).

      Models multiphase flow through wellbore tubing. Pressure drop includes hydrostatic head, wall friction, and flow acceleration. Uses a simplified Beggs-Brill-like approach with gravity and friction components.

      Parameters:
      bottomNode - bottomhole node name (higher pressure)
      topNode - wellhead node name (lower pressure)
      elementName - element name
      lengthM - tubing measured depth in meters
      diameterM - tubing inner diameter in meters
      inclinationDeg - inclination from horizontal (90 = vertical)
      Returns:
      the created element
    • addMultiphasePipe

      public LoopedPipeNetwork.NetworkPipe addMultiphasePipe(String fromNode, String toNode, String elementName, double lengthM, double diameterM)
      Add a multiphase pipeline element using Beggs-Brill correlation.

      Uses NeqSim's PipeBeggsAndBrills internally for accurate multiphase pressure-drop calculation. This element type is suitable for subsea flowlines and production pipelines carrying gas-oil or gas-oil-water mixtures.

      Parameters:
      fromNode - upstream node name
      toNode - downstream node name
      elementName - element name
      lengthM - pipe length in meters
      diameterM - pipe inner diameter in meters
      Returns:
      the created element
    • addCompressor

      public LoopedPipeNetwork.NetworkPipe addCompressor(String fromNode, String toNode, String elementName, double polytropicEfficiency)
      Add a compressor or booster station element between two nodes.

      The compressor adds energy to the flow (negative head loss). Head rise is calculated from polytropic efficiency and compression ratio, or from a performance chart if provided. Wraps NeqSim's Compressor class internally.

      Parameters:
      fromNode - suction (upstream) node name
      toNode - discharge (downstream) node name
      elementName - element name
      polytropicEfficiency - polytropic efficiency (0-1, typically 0.70-0.85)
      Returns:
      the created element
    • addCompressorWithChart

      public LoopedPipeNetwork.NetworkPipe addCompressorWithChart(String fromNode, String toNode, String elementName, Compressor compressor)
      Add a compressor with a performance chart (speed curves).

      When a chart is provided, the head-flow relationship follows the compressor curve at the specified speed. The solver iterates to find the operating point on the curve.

      Parameters:
      fromNode - suction node name
      toNode - discharge node name
      elementName - element name
      compressor - pre-configured NeqSim Compressor with chart
      Returns:
      the created element
    • addRegulator

      public LoopedPipeNetwork.NetworkPipe addRegulator(String fromNode, String toNode, String elementName, double setPointBar)
      Add a pressure regulator (PRV) element between two nodes.

      The regulator maintains a fixed downstream pressure set-point. If upstream pressure is above the set-point, the regulator throttles to deliver exactly the set-point downstream. If upstream pressure falls below the set-point, the regulator is fully open (acts as a pipe).

      Parameters:
      fromNode - upstream node name
      toNode - downstream node name
      elementName - element name
      setPointBar - downstream pressure set-point in bara
      Returns:
      the created element
    • setGasLift

      public void setGasLift(String elementName, double gasLiftRateKgHr)
      Configure gas lift on an existing well tubing or IPR element.

      Gas lift reduces the hydrostatic head in the tubing by injecting gas, effectively reducing the pressure drop from bottomhole to wellhead. The model applies a head-loss reduction factor based on the injection rate.

      Parameters:
      elementName - name of the well element (IPR or tubing)
      gasLiftRateKgHr - gas lift injection rate in kg/hr
    • setESP

      public void setESP(String elementName, double ratedPowerKW, double efficiency)
      Configure an Electrical Submersible Pump (ESP) on a well element.

      The ESP adds a negative head-loss (pressure boost) to the tubing element, calculated as: dP = espPower * efficiency / volumetricFlowRate. This enables production from wells that cannot flow naturally.

      Parameters:
      elementName - name of the well element (IPR or tubing)
      ratedPowerKW - ESP rated power in kW
      efficiency - pump efficiency (0-1, typically 0.3-0.7)
    • setJetPump

      public void setJetPump(String elementName, double equivalentPowerKW, double efficiency)
      Configure a jet pump on a well element.

      The jet pump uses high-pressure power fluid to create a pressure boost via a venturi effect. Modeled similarly to ESP with an equivalent power and efficiency.

      Parameters:
      elementName - name of the well element
      equivalentPowerKW - equivalent hydraulic power in kW
      efficiency - nozzle-throat efficiency (typically 0.2-0.4)
    • setRodPump

      public void setRodPump(String elementName, double equivalentPowerKW, double efficiency)
      Configure a rod pump (beam pump) on a well element.

      The rod pump provides artificial lift for low-rate wells. Modeled as an equivalent pressure boost based on pump displacement and stroke rate.

      Parameters:
      elementName - name of the well element
      equivalentPowerKW - equivalent mechanical power in kW
      efficiency - pump efficiency (typically 0.4-0.6)
    • getTotalGasLiftRate

      public double getTotalGasLiftRate()
      Get the total gas lift injection rate across all wells in kg/hr.
      Returns:
      total gas lift rate in kg/hr
    • getTotalESPPower

      public double getTotalESPPower()
      Get the total ESP power consumption across all wells in kW.
      Returns:
      total ESP power in kW
    • setWaterCut

      public void setWaterCut(String elementName, double waterCut)
      Set the water cut for a well or pipe element.

      Water cut is the volume fraction of water in the total liquid production. This enables produced water tracking through the network for water treatment sizing and disposal planning.

      Parameters:
      elementName - name of the element
      waterCut - water cut fraction (0-1)
    • addWaterInjection

      public LoopedPipeNetwork.NetworkPipe addWaterInjection(String sourceNode, String reservoirNode, String elementName, double injectionRateKgHr)
      Add a water injection well element between two nodes.

      Water injection wells provide pressure support to reservoirs. The injection rate creates a negative flow (into the reservoir) and increases reservoir pressure.

      Parameters:
      sourceNode - water supply node
      reservoirNode - reservoir node
      elementName - element name
      injectionRateKgHr - injection rate in kg/hr
      Returns:
      the created element
    • calculateWaterBalance

      public Map<String,double[]> calculateWaterBalance()
      Calculate water balance for the entire network.

      Tracks water production at each node based on water cuts and flow rates. Also tracks water injection rates. Returns per-node water balance: [0] = water production (kg/hr), [1] = water injection (kg/hr), [2] = net water (kg/hr).

      Returns:
      map of node name to water balance array
    • getTotalWaterProduction

      public double getTotalWaterProduction()
      Get total water production rate in kg/hr.
      Returns:
      total water production
    • getTotalWaterInjection

      public double getTotalWaterInjection()
      Get total water injection rate in kg/hr.
      Returns:
      total water injection
    • setWaterBreakthrough

      public void setWaterBreakthrough(String elementName, double breakthroughWC, double finalWC, double currentWC)
      Set the water breakthrough profile for a well (time-dependent water cut).

      Models water breakthrough as a function of cumulative production. When the cumulative production from a well exceeds the breakthrough threshold, the water cut ramps up linearly to the final water cut.

      Parameters:
      elementName - well element name
      breakthroughWC - water cut at breakthrough (typically 0.05-0.10)
      finalWC - ultimate water cut (typically 0.80-0.95)
      currentWC - current water cut
    • setSandRate

      public void setSandRate(String elementName, double sandRateKgHr)
      Set the sand production rate for a well element.
      Parameters:
      elementName - well element name
      sandRateKgHr - sand production rate in kg/hr
    • setMaxAllowableSandRate

      public void setMaxAllowableSandRate(double maxRateKgHr)
      Set the maximum allowable sand production rate.
      Parameters:
      maxRateKgHr - maximum sand rate in kg/hr (0 = no limit)
    • setMaxAllowableErosionRate

      public void setMaxAllowableErosionRate(double maxRateMmYr)
      Set the maximum allowable erosion rate for integrity management.
      Parameters:
      maxRateMmYr - maximum erosion rate in mm/yr (default 5.0)
    • calculateSandTransport

      public Map<String,double[]> calculateSandTransport()
      Calculate sand transport and erosion rates for all pipe elements.

      Sand erosion rate is calculated using the DNV RP O501 simplified model: E = K * F(alpha) * v^n * d_p / (rho_t * A) where K is material constant, F(alpha) is impact angle function, v is velocity, d_p is particle size, rho_t is target material density, A is pipe cross-section area. For the simplified API approach: E_rate = C_sand * rho_sand * V^2 / (rho_pipe * t_wall)

      Returns:
      map of pipe name to sand results: [0]=sandConc_kgm3, [1]=erosionRate_mmyr, [2]=depositionRate_kgmyr, [3]=sandVelocity_ms
    • getSandViolations

      public List<String> getSandViolations()
      Get sand violation messages.
      Returns:
      list of sand/erosion violation messages
    • setCorrosiveGas

      public void setCorrosiveGas(String elementName, double co2MoleFrac, double h2sMoleFrac)
      Set CO2 and H2S mole fractions for a pipe element for corrosion calculation.
      Parameters:
      elementName - pipe element name
      co2MoleFrac - CO2 mole fraction in gas phase (0-1)
      h2sMoleFrac - H2S mole fraction in gas phase (0-1)
    • setCorrosionModel

      public void setCorrosionModel(String elementName, String model)
      Set the corrosion model for a pipe element.
      Parameters:
      elementName - pipe element name
      model - "deWaard" for de Waard-Milliams (1975/1991) or "norsokM506" for NORSOK M-506
    • setMinAllowableWallLife

      public void setMinAllowableWallLife(double years)
      Set the minimum allowable remaining wall life.
      Parameters:
      years - minimum years before wall thickness falls below minimum
    • calculateCorrosion

      public Map<String,double[]> calculateCorrosion()
      Calculate CO2/H2S corrosion rates for all pipe elements.

      Supports two corrosion models:

      • de Waard-Milliams (1975/1991): log10(Vcorr) = 5.8 - 1710/T + 0.67*log10(pCO2) where T is temperature in K and pCO2 is CO2 partial pressure in bar.
      • NORSOK M-506 (2005): Vcorr = K_t * f(T) * (fCO2)^0.62 * (S/19)^0.146 + f(Vwall) which includes temperature, CO2 fugacity, shear stress, and pH effects.
      Returns:
      map of pipe name to corrosion results: [0]=corrosionRate_mmyr, [1]=co2PartialPressure_bar, [2]=h2sPartialPressure_bar, [3]=remainingLife_yr
    • getCorrosionViolations

      public List<String> getCorrosionViolations()
      Get corrosion violation messages.
      Returns:
      list of corrosion/integrity violation messages
    • setCO2EmissionFactor

      public void setCO2EmissionFactor(double factor)
      Set the CO2 emission factor for fuel gas combustion.
      Parameters:
      factor - emission factor in kgCO2 per kg fuel gas (default 2.75 for natural gas)
    • getCO2EmissionFactor

      public double getCO2EmissionFactor()
      Get the CO2 emission factor.
      Returns:
      kgCO2 per kg fuel gas
    • setMethaneSlipFactor

      public void setMethaneSlipFactor(double slipFraction)
      Set the methane slip factor for gas turbine/engine drivers.
      Parameters:
      slipFraction - fraction of unburned methane (0-1, default 0.02 = 2%)
    • calculateEmissions

      public Map<String,double[]> calculateEmissions()
      Calculate greenhouse gas emissions for all compressor stations in the network.

      Emissions are calculated from fuel gas consumption (from compressor power and heat rate). Results include: CO2 from combustion, methane slip (unburned CH4), and CO2-equivalent (using GWP=28 for CH4 per IPCC AR5). Per-compressor breakdown is returned.

      Returns:
      map of compressor element name to emissions array: [0]=CO2_kghr, [1]=CH4_slip_kghr, [2]=CO2eq_kghr, [3]=power_kW, [4]=fuelGas_kghr
    • getTotalCO2Emissions

      public double getTotalCO2Emissions()
      Get total CO2-equivalent emissions from all compressors in kg/hr.
      Returns:
      total CO2eq emissions in kg/hr
    • getAnnualCO2EmissionsTonnes

      public double getAnnualCO2EmissionsTonnes()
      Get annual CO2-equivalent emissions in tonnes/year.
      Returns:
      annual CO2eq in tonnes/yr
    • getEmissionsIntensity

      public double getEmissionsIntensity()
      Get emissions intensity in kgCO2eq per tonne of production.
      Returns:
      emissions intensity (kgCO2eq/tonne product)
    • setNodeFluid

      public void setNodeFluid(String sourceNodeName, SystemInterface fluid)
      Set the fluid composition for a specific source node.

      Enables compositional tracking where different wells/sources have different fluid compositions. At junction nodes, fluids are mixed weighted by mass flow from each incoming pipe. This provides accurate gas quality (heating value, Wobbe index) and phase behavior at each node.

      Parameters:
      sourceNodeName - name of the source node
      fluid - the fluid composition at this source
    • getNodeFluid

      public SystemInterface getNodeFluid(String nodeName)
      Get the fluid composition at a specific node (after solving with compositional tracking).
      Parameters:
      nodeName - node name
      Returns:
      fluid at this node, or the template fluid if compositional tracking is not used
    • getErosionalVelocityViolations

      public List<String> getErosionalVelocityViolations()
      Get erosional velocity violations after solving.

      Returns a list of pipe names where the actual velocity exceeds the API RP 14E erosional velocity limit: V_erosional = C / sqrt(rho_mixture) where C is typically 100-150.

      Returns:
      list of violation descriptions (empty if no violations)
    • setPipeEfficiency

      public void setPipeEfficiency(String pipeName, double efficiency)
      Set pipe efficiency factor for a named pipe.

      The efficiency factor accounts for pipe aging, internal fouling, wax deposition, or scale build-up. A factor of 1.0 means new pipe; 0.85 means 15% degradation. The friction loss is multiplied by 1/efficiency.

      Parameters:
      pipeName - pipe name
      efficiency - efficiency factor (0.5-1.0, typically 0.85-1.0)
    • getNode

      public LoopedPipeNetwork.NetworkNode getNode(String name)
      Get a node by name.
      Parameters:
      name - node name
      Returns:
      the node
    • getPipe

      public LoopedPipeNetwork.NetworkPipe getPipe(String name)
      Get a pipe by name.
      Parameters:
      name - pipe name
      Returns:
      the pipe
    • getMassBalanceError

      public double getMassBalanceError()
      Get mass balance error from last solve in kg/s.
      Returns:
      mass balance error
    • setSolverType

      public void setSolverType(LoopedPipeNetwork.SolverType type)
      Set the solver type.
      Parameters:
      type - solver type
    • getSolverType

      public LoopedPipeNetwork.SolverType getSolverType()
      Get the solver type.
      Returns:
      solver type
    • setTolerance

      public void setTolerance(double tol)
      Set convergence tolerance for head loss balance (Pa).
      Parameters:
      tol - tolerance in Pa
    • getTolerance

      public double getTolerance()
      Get convergence tolerance.
      Returns:
      tolerance in Pa
    • setMaxIterations

      public void setMaxIterations(int max)
      Set maximum number of iterations.
      Parameters:
      max - maximum iterations
    • getMaxIterations

      public int getMaxIterations()
      Get maximum iterations.
      Returns:
      max iterations
    • setRelaxationFactor

      public void setRelaxationFactor(double factor)
      Set relaxation factor for Hardy Cross (0 < factor <= 1).
      Parameters:
      factor - relaxation factor
    • getRelaxationFactor

      public double getRelaxationFactor()
      Get relaxation factor.
      Returns:
      relaxation factor
    • getIterationCount

      public int getIterationCount()
      Get number of iterations in last solve.
      Returns:
      iteration count
    • getMaxResidual

      public double getMaxResidual()
      Get maximum residual from last iteration.
      Returns:
      max residual in Pa
    • isConverged

      public boolean isConverged()
      Check if solution converged.
      Returns:
      true if converged
    • getLoops

      public List<NetworkLoop> getLoops()
      Get detected loops in the network.
      Returns:
      list of loops
    • getNumberOfLoops

      public int getNumberOfLoops()
      Get number of loops in the network.
      Returns:
      number of loops
    • getPipeNames

      public List<String> getPipeNames()
      Get all pipe names.
      Returns:
      list of pipe names
    • getPipeFlowRate

      public double getPipeFlowRate(String pipeName)
      Get flow rate for a specific pipe in kg/hr.
      Parameters:
      pipeName - pipe name
      Returns:
      flow rate in kg/hr
    • getNodePressure

      public double getNodePressure(String nodeName)
      Get pressure at a node in bara.
      Parameters:
      nodeName - node name
      Returns:
      pressure in bara
    • getNodeFlowRate

      public double getNodeFlowRate(String nodeName)
      Get the net delivered flow rate at a node in kg/hr.

      Computes the net inflow to the node: sum of incoming pipe flows minus outgoing. For a source node this is negative (supply). For a sink node this is the delivered flow rate. Useful in pressure-pressure mode where the delivered flow is not specified but computed by the solver.

      Parameters:
      nodeName - node name
      Returns:
      net delivered flow in kg/hr (positive = net inflow to node)
    • initializeFlowEstimates

      private void initializeFlowEstimates()
      Initialize pipe flow estimates using BFS spanning tree to satisfy mass balance.

      Builds a spanning tree from source nodes using BFS. Tree-edge flows are set to satisfy mass balance at every node (required for Hardy Cross). Non-tree edges (loop closers) are initialized to zero — the loop solver will correct them.

    • calculateHeadLoss

      private double calculateHeadLoss(LoopedPipeNetwork.NetworkPipe pipe, SystemInterface fluid)
      Calculate head loss for a network element based on its type.

      Dispatches to the appropriate head loss model depending on the element type: Darcy-Weisbach pipe, well IPR, production choke, tubing VLP, or multiphase pipe.

      Parameters:
      pipe - the network element
      fluid - fluid properties
      Returns:
      head loss in Pa (positive = pressure drop from-to direction)
    • calculateHeadLossDarcyWeisbach

      private double calculateHeadLossDarcyWeisbach(LoopedPipeNetwork.NetworkPipe pipe, SystemInterface fluid)
      Calculate head loss for a standard pipe using Darcy-Weisbach equation with elevation.
      Parameters:
      pipe - the pipe
      fluid - fluid properties
      Returns:
      head loss in Pa
    • calculateHeadLossIPR

      private double calculateHeadLossIPR(LoopedPipeNetwork.NetworkPipe pipe, SystemInterface fluid)
      Calculate head loss for a well IPR element.

      Models the reservoir-to-wellbore pressure relationship. For a given flow rate Q:

      • PI model (oil): dP = Q / PI
      • PI model (gas): Pr^2 - Pwf^2 = Q / PI, so dP = Pr - sqrt(Pr^2 - Q/PI)
      • Vogel: q/qmax = 1 - 0.2*(Pwf/Pr) - 0.8*(Pwf/Pr)^2
      • Fetkovich: q = C * (Pr^2 - Pwf^2)^n
      Parameters:
      pipe - the IPR element
      fluid - fluid properties (not used directly, IPR is empirical)
      Returns:
      head loss in Pa
    • calculateHeadLossChoke

      private double calculateHeadLossChoke(LoopedPipeNetwork.NetworkPipe pipe, SystemInterface fluid)
      Calculate head loss for a production choke element.

      Uses a simplified valve equation: Q = Kv * (opening/100) * sqrt(dP * rho / SG_ref). Inverting: dP = (Q / (Kv_eff))^2 / rho where Kv_eff = Kv * (opening/100) converted to SI.

      For critical (choked) flow, the effective dP is limited by the critical pressure ratio.

      Parameters:
      pipe - the choke element
      fluid - fluid properties
      Returns:
      head loss in Pa
    • calculateHeadLossTubing

      private double calculateHeadLossTubing(LoopedPipeNetwork.NetworkPipe pipe, SystemInterface fluid)
      Calculate head loss for wellbore tubing (VLP) element.

      Simplified multiphase vertical lift model with gravity and friction components: dP = dP_gravity + dP_friction = rho * g * L * sin(theta) + f * (L/D) * (rho * v^2 / 2)

      Parameters:
      pipe - the tubing element
      fluid - fluid properties
      Returns:
      head loss in Pa (positive = pressure drop bottom-to-top)
    • calculateHeadLossMultiphase

      private double calculateHeadLossMultiphase(LoopedPipeNetwork.NetworkPipe pipe, SystemInterface fluid)
      Calculate head loss for a multiphase pipe using simplified Beggs-Brill approach.

      When the fluid template has multiple phases, this uses a mixture density and viscosity for the pressure drop calculation. For single-phase conditions, it degenerates to Darcy-Weisbach. Elevation is incorporated via sin(angle) where angle is calculated from pipe-level elevation difference.

      Parameters:
      pipe - the multiphase pipe element
      fluid - fluid properties
      Returns:
      head loss in Pa
    • calculateHeadLossCompressor

      private double calculateHeadLossCompressor(LoopedPipeNetwork.NetworkPipe pipe, SystemInterface fluid)
      Calculate head loss for a compressor element.

      Compressors add energy to the flow, so they produce a negative head loss (pressure rise). The pressure rise is computed from the polytropic efficiency and compression ratio, or from a performance chart if available.

      Parameters:
      pipe - the compressor element
      fluid - fluid properties
      Returns:
      head loss in Pa (negative = pressure rise)
    • calculateHeadLossRegulator

      private double calculateHeadLossRegulator(LoopedPipeNetwork.NetworkPipe pipe, SystemInterface fluid)
      Calculate head loss for a pressure regulator element.

      The regulator maintains a fixed downstream pressure set-point. When upstream pressure exceeds the set-point, the regulator throttles: ΔP = P_upstream - P_setpoint. When upstream is below the set-point, the regulator is fully open (minimal resistance).

      Parameters:
      pipe - the regulator element
      fluid - fluid properties (used for velocity calculation only)
      Returns:
      head loss in Pa
    • calculateHeadLossDerivative

      private double calculateHeadLossDerivative(LoopedPipeNetwork.NetworkPipe pipe, SystemInterface fluid)
      Calculate derivative of head loss with respect to flow rate for any element type.
      Parameters:
      pipe - the network element
      fluid - fluid properties
      Returns:
      dh/dQ in Pa/(kg/s)
    • calculateHeadLossDerivativeIPR

      private double calculateHeadLossDerivativeIPR(LoopedPipeNetwork.NetworkPipe pipe)
      Calculate dh/dQ for IPR element.
      Parameters:
      pipe - the IPR element
      Returns:
      dh/dQ in Pa/(kg/s)
    • calculateHeadLossDerivativeChoke

      private double calculateHeadLossDerivativeChoke(LoopedPipeNetwork.NetworkPipe pipe, SystemInterface fluid)
      Calculate dh/dQ for choke element.
      Parameters:
      pipe - the choke element
      fluid - fluid properties
      Returns:
      dh/dQ in Pa/(kg/s)
    • calculateHeadLossDerivativeCompressor

      private double calculateHeadLossDerivativeCompressor(LoopedPipeNetwork.NetworkPipe pipe, SystemInterface fluid)
      Calculate dh/dQ for compressor element.

      For a compressor (which produces a pressure rise), the head loss is negative. The derivative reflects how the pressure rise changes with flow. At high flow, head decreases per the compressor curve. A first-order approximation uses 2|h|/Q.

      Parameters:
      pipe - the compressor element
      fluid - fluid properties
      Returns:
      dh/dQ in Pa/(kg/s) — typically negative (head rise decreases with more flow)
    • calculateHeadLossDerivativeRegulator

      private double calculateHeadLossDerivativeRegulator(LoopedPipeNetwork.NetworkPipe pipe, SystemInterface fluid)
      Calculate dh/dQ for regulator (pressure reducing valve) element.

      A regulator maintains a set-point downstream pressure. When it is active (upstream pressure above set-point), the pressure drop is nearly independent of flow, so the derivative is small.

      Parameters:
      pipe - the regulator element
      fluid - fluid properties
      Returns:
      dh/dQ in Pa/(kg/s)
    • detectLoops

      private void detectLoops()
      Detect loops in the network using DFS spanning tree algorithm.
    • runHardyCross

      private void runHardyCross(UUID id)
      Run Hardy Cross iterative solver.
      Parameters:
      id - calculation identifier
    • runSequential

      private void runSequential(UUID id)
      Run sequential solver for tree networks.
      Parameters:
      id - calculation identifier
    • initializePipeModels

      private void initializePipeModels()
      Initialize AdiabaticPipe models for each pipe.
    • updateNodePressures

      private void updateNodePressures(SystemInterface fluid)
      Update node pressures based on calculated head losses.
      Parameters:
      fluid - fluid for calculations
    • propagatePressure

      private void propagatePressure(String nodeName, SystemInterface fluid)
      Propagate pressure from a node to connected nodes.
      Parameters:
      nodeName - starting node name
      fluid - fluid for calculations
    • run

      public void run(UUID id)
      Description copied from interface: SimulationInterface

      In this method all thermodynamic and unit operations will be calculated in a steady state calculation.

      Parameters:
      id - UUID
    • updatePipeHydraulicProperties

      private void updatePipeHydraulicProperties()
      Update hydraulic properties (velocity, Reynolds, friction factor) for all pipes.
    • updateCompositionalMixing

      public void updateCompositionalMixing()
      Update compositional mixing at junction nodes.

      When different source nodes have different fluid compositions (set via setNodeFluid(String, SystemInterface)), this method traces flows through the network and calculates mass-weighted mixed compositions at junction nodes. The resulting mixed fluid at each node is stored in the nodeFluidMap.

      Call this after the network has converged to determine gas quality (heating value, Wobbe index) at each delivery point.

    • checkErosionalVelocity

      public List<String> checkErosionalVelocity()
      Check erosional velocity limits per API RP 14E for all pipe elements.

      The erosional velocity is calculated as:

      V_e = C / sqrt(rho_mixture)
      

      where C is the empirical constant (default 125 for continuous service, 100-150 typical range) and rho_mixture is the mixture density in kg/m3. Pipes where the actual velocity exceeds the erosional velocity are flagged as violations.

      Returns:
      list of violation descriptions (empty if all pipes are within limits)
    • calculateGasQuality

      public Map<String,double[]> calculateGasQuality()
      Calculate gas quality properties at every node that has an assigned fluid composition.

      Uses NeqSim's Standard_ISO6976 implementation to compute ISO 6976 properties at each node: superior (gross) calorific value, inferior (net) calorific value, Wobbe index, and relative density. Call updateCompositionalMixing() first to propagate compositions through the network.

      Results are stored per node and accessible via getNodeGasQuality(String). The returned array contains: [0] = Superior Wobbe Index (MJ/Sm3), [1] = Superior Calorific Value (kJ/Sm3), [2] = Inferior Calorific Value (kJ/Sm3), [3] = Relative Density.

      Returns:
      map of node name to gas quality array [Wobbe, HHV, LHV, relDens]
    • getNodeGasQuality

      public double[] getNodeGasQuality(String nodeName)
      Get gas quality properties at a specific node.

      Returns null if gas quality has not been calculated or the node has no assigned fluid. Call calculateGasQuality() first.

      Parameters:
      nodeName - name of the node
      Returns:
      array of [Wobbe (MJ/Sm3), HHV (kJ/Sm3), LHV (kJ/Sm3), RelDens], or null
    • checkGasQualityLimits

      public List<String> checkGasQualityLimits(double wobbeMin, double wobbeMax)
      Check if gas quality at all sink (delivery) nodes meets the specified Wobbe index bounds.

      Typical H-gas Wobbe bounds: 46.44-52.81 MJ/Sm3 (EN 16726). Call calculateGasQuality() first.

      Parameters:
      wobbeMin - minimum acceptable Wobbe index in MJ/Sm3
      wobbeMax - maximum acceptable Wobbe index in MJ/Sm3
      Returns:
      list of violation descriptions (empty if all sinks are within spec)
    • calculateOilQuality

      public Map<String,double[]> calculateOilQuality()
      Calculate oil quality properties (TVP and RVP) at every node that has an assigned fluid composition.

      True Vapor Pressure (TVP) is the bubble-point pressure at the node's actual flowing temperature. Reid Vapor Pressure (RVP) is calculated per ASTM D6377 at the standard reference temperature of 37.8 deg C (100 deg F). Both are key oil quality metrics for pipeline transport, storage tank design, and emissions control.

      Results are stored per node and accessible via getNodeOilQuality(String). The returned array contains: [0] = TVP (bara), [1] = RVP (bara), [2] = VPCR4 (bara).

      Returns:
      map of node name to oil quality array [TVP_bara, RVP_bara, VPCR4_bara]
    • estimateNodeTemperature

      private double estimateNodeTemperature(String nodeName)
      Estimate the node temperature in Kelvin.

      For source nodes, uses the inlet fluid temperature. For other nodes, uses 15 deg C as a default. This can be overridden with per-node temperature data if available.

      Parameters:
      nodeName - the node name
      Returns:
      temperature in Kelvin
    • getNodeOilQuality

      public double[] getNodeOilQuality(String nodeName)
      Get oil quality properties at a specific node.

      Returns null if oil quality has not been calculated or the node has no assigned fluid. Call calculateOilQuality() first.

      Parameters:
      nodeName - name of the node
      Returns:
      array of [TVP (bara), RVP (bara), VPCR4 (bara)], or null
    • checkOilQualityLimits

      public List<String> checkOilQualityLimits(double tvpMaxBara, double rvpMaxBara)
      Check if oil quality at all sink (delivery) nodes meets the specified TVP and RVP limits.

      TVP limits are typically driven by storage tank design pressure and emission regulations. RVP limits depend on crude classification and regional regulations (e.g., EPA, ASTM D4953). Typical pipeline crude specifications: TVP max 1.0 bara (atmospheric storage), RVP max 0.69 bara (10 psi) for light crude. Call calculateOilQuality() first.

      Parameters:
      tvpMaxBara - maximum acceptable TVP in bara at delivery
      rvpMaxBara - maximum acceptable RVP in bara at delivery
      Returns:
      list of violation descriptions (empty if all sinks are within spec)
    • nodalAnalysis

      public Map<String,double[]> nodalAnalysis(String iprElementName, String outflowElementName, int numPoints)
      Perform nodal analysis for a well defined by an IPR element and an outflow (tubing/pipe/choke) element downstream of the well node.

      Sweeps bottom-hole pressure from near-zero to reservoir pressure, computing the IPR flow rate and the VLP (vertical lift performance) flow rate at each point. The operating point is the intersection where IPR rate equals VLP rate. This is standard well deliverability analysis per Beggs (Production Optimization) and Brown & Lea.

      The returned map contains:

      • "bhp" - array of bottom-hole pressures in bara (sweep points)
      • "iprRate" - array of IPR flow rates in kg/hr at each BHP
      • "vlpRate" - array of VLP flow rates in kg/hr at each BHP (backpressure-derived)
      • "operatingBHP" - single value, operating BHP in bara
      • "operatingRate" - single value, operating rate in kg/hr
      Parameters:
      iprElementName - name of the WELL_IPR element
      outflowElementName - name of the downstream element (pipe, choke, or tubing)
      numPoints - number of sweep points (default 20 if <= 0)
      Returns:
      map with nodal analysis arrays, or empty map if elements not found
    • computeIPRFlowRate

      private double computeIPRFlowRate(LoopedPipeNetwork.NetworkPipe iprPipe, double pResPa, double bhpPa)
      Compute IPR flow rate at a given bottom-hole pressure.
      Parameters:
      iprPipe - the IPR element
      pResPa - reservoir pressure in Pa
      bhpPa - bottom-hole pressure in Pa
      Returns:
      flow rate in kg/s (positive)
    • estimateVLPRate

      private double estimateVLPRate(LoopedPipeNetwork.NetworkPipe outPipe, double bhpPa)
      Estimate VLP (outflow) rate at a given bottom-hole pressure.

      For a pipe/choke downstream of the well, this estimates the flow rate that the outflow system can deliver given the available driving pressure (BHP minus downstream network pressure).

      Parameters:
      outPipe - the outflow element
      bhpPa - available bottom-hole pressure in Pa
      Returns:
      flow rate in kg/s (positive)
    • setNodePressureLimits

      public void setNodePressureLimits(String nodeName, double minPressureBar, double maxPressureBar)
      Set pressure limits for a node.

      After solving the network, call checkConstraints() to verify pressures at all constrained nodes are within bounds. This enables safety envelope checking for minimum arrival pressure, maximum allowable working pressure, hydrate formation limits, etc.

      Parameters:
      nodeName - name of the node
      minPressureBar - minimum acceptable pressure in bara
      maxPressureBar - maximum acceptable pressure in bara
    • setElementFlowLimits

      public void setElementFlowLimits(String elementName, double minFlowKgHr, double maxFlowKgHr)
      Set flow rate limits for a network element (pipe, choke, compressor, etc.).

      Limits are checked after the network is solved. This enables enforcement of choke rate limits, compressor minimum/maximum flow, pipeline capacity limits, etc.

      Parameters:
      elementName - name of the element
      minFlowKgHr - minimum acceptable flow rate in kg/hr (use 0 for no minimum)
      maxFlowKgHr - maximum acceptable flow rate in kg/hr
    • checkConstraints

      public List<String> checkConstraints()
      Check all defined constraints and return any violations.

      Verifies:

      Returns:
      list of constraint violation descriptions (empty if all constraints satisfied)
    • getConstraintViolations

      public List<String> getConstraintViolations()
      Get the list of constraint violations from the last checkConstraints() call.
      Returns:
      list of violation descriptions
    • setFuelGasHeatRate

      public void setFuelGasHeatRate(double heatRateKJPerKWh)
      Set the fuel gas heat rate for gas-turbine driven compressors.

      The heat rate defines how much fuel energy is consumed per unit of mechanical work. Typical values: 9,000-12,000 kJ/kWh for industrial gas turbines, 3,600 kJ/kWh for electric drives (ideal).

      Parameters:
      heatRateKJPerKWh - fuel gas heat rate in kJ/kWh
    • getFuelGasHeatRate

      public double getFuelGasHeatRate()
      Get the fuel gas heat rate in kJ/kWh.
      Returns:
      heat rate
    • calculateFuelGasConsumption

      public Map<String,Double> calculateFuelGasConsumption()
      Calculate fuel gas consumption for all compressor elements in the network.

      For each compressor element, computes fuel gas rate from shaft power and driver heat rate:

      fuelRate_kgs = (power_kW * heatRate_kJperkWh) / (LHV_kJperkg * 3600)
      

      where LHV is the lower heating value of the gas at the compressor suction node. If gas quality has been calculated via calculateGasQuality(), the LHV from ISO 6976 is used. Otherwise a default of 47,000 kJ/kg (typical natural gas) is assumed.

      The total fuel gas rate is the sum across all compressors and can be accessed via getTotalFuelGasRate().

      Returns:
      map of compressor element name to fuel gas rate in kg/hr
    • estimateCompressorPower

      private double estimateCompressorPower(LoopedPipeNetwork.NetworkPipe compPipe)
      Estimate compressor shaft power from polytropic head calculation.
      Parameters:
      compPipe - the compressor element
      Returns:
      estimated power in kW
    • getTotalFuelGasRate

      public double getTotalFuelGasRate()
      Get the total fuel gas consumption rate for all compressors in the network.

      Call calculateFuelGasConsumption() first to populate this value.

      Returns:
      total fuel gas rate in kg/hr
    • getFuelGasPercentage

      public double getFuelGasPercentage()
      Get the fuel gas consumption as a percentage of total network throughput.

      This is the "self-consumption" ratio: how much of the transported gas is consumed as compressor fuel. Typical values: 1-3% for gas transmission, 0% for electric drives.

      Returns:
      fuel gas percentage of total sink flow (0-100)
    • optimizeChokeOpenings

      public double optimizeChokeOpenings(int maxIterations, double tolerance)
      Optimize choke openings to maximize total production from all wells.

      Uses a gradient-based optimization approach: for each choke element, perturb the opening slightly, re-solve the network, and compute the sensitivity of total production to the choke opening. Then adjust openings in the direction of increasing total production.

      The optimization respects constraints: choke openings between 0-100%, and downstream pressure/erosional limits.

      Parameters:
      maxIterations - maximum number of optimization iterations
      tolerance - convergence tolerance for relative change in total production
      Returns:
      total optimized production in kg/s
    • getTotalSinkFlow

      public double getTotalSinkFlow()
      Get total flow rate into all sink nodes (total network production).
      Returns:
      total sink inflow in kg/s (positive value)
    • exportVFPTables

      public void exportVFPTables(String filePath, double[] flowRates, double[] thps, double[] waterCuts, double[] gors)
      Export VFP (Vertical Flow Performance) tables for well elements in the network.

      Generates Eclipse E300-compatible VFPPROD/VFPINJ tables using the EclipseVFPExporter. For each well (IPR + tubing) element pair, this method creates a VFP table covering the specified flow rate, THP, and parameter ranges.

      Parameters:
      filePath - the file path to write VFP tables to
      flowRates - array of flow rates (Sm3/d) for VFP table
      thps - array of tubing head pressures (bara) for VFP table
      waterCuts - array of water cuts (fraction) for VFP table
      gors - array of gas-oil ratios (Sm3/Sm3) for VFP table
    • generateBHPTable

      private double[][][][][] generateBHPTable(LoopedPipeNetwork.NetworkPipe pipe, double[] flowRates, double[] thps, double[] waterCuts, double[] gors)
      Generate BHP table for a well element across parameter ranges.
      Parameters:
      pipe - the well element (IPR or tubing)
      flowRates - flow rates to evaluate (Sm3/d)
      thps - tubing head pressures (bara)
      waterCuts - water cut values (fraction)
      gors - GOR values (Sm3/Sm3)
      Returns:
      5D BHP table: [flow][thp][wc][gor][alq]
    • setOilPrice

      public void setOilPrice(double pricePerKg)
      Set oil price for revenue-based optimization.
      Parameters:
      pricePerKg - oil price in USD/kg (e.g., 0.50 for ~$80/bbl crude)
    • setGasPrice

      public void setGasPrice(double pricePerKg)
      Set gas price for revenue-based optimization.
      Parameters:
      pricePerKg - gas price in USD/kg
    • setWellPrice

      public void setWellPrice(String wellOrChokeName, double pricePerKg)
      Set per-well product price for revenue allocation.
      Parameters:
      wellOrChokeName - name of the well IPR or choke element
      pricePerKg - product price in USD/kg
    • optimizeProduction

      public double optimizeProduction(int maxIterations, double tolerance)
      Optimize choke openings to maximize revenue (price-weighted production).

      Uses adaptive gradient ascent with Armijo backtracking line search. Supports revenue-based objective (price x rate) and respects constraint limits set via setNodePressureLimits(String, double, double) and setElementFlowLimits(String, double, double).

      Parameters:
      maxIterations - maximum number of optimization iterations
      tolerance - convergence tolerance for relative revenue change
      Returns:
      optimized total revenue in USD/hr (or total flow in kg/hr if no prices set)
    • computeObjective

      private double computeObjective()
      Compute the optimization objective (revenue or total flow).
      Returns:
      objective value
    • computeConstrainedObjective

      private double computeConstrainedObjective()
      Compute objective with penalty for constraint violations.
      Returns:
      penalized objective
    • buildWellAllocationReport

      private void buildWellAllocationReport()
      Build per-well allocation report after optimization.
    • getWellAllocationResults

      public Map<String,double[]> getWellAllocationResults()
      Get the well allocation results from the last optimization.

      Returns a map from well/choke name to double[3]: [0]=rate_kg_hr, [1]=revenue_usd_hr, [2]=choke_opening_pct.

      Returns:
      well allocation results map
    • sensitivityAnalysis

      public Map<String,double[]> sensitivityAnalysis(String elementName, String parameterType, double[] values)
      Run sensitivity analysis by sweeping a parameter across a range.

      Supported parameter types: "reservoir_pressure", "well_pi", "choke_opening", "sink_pressure", "pipe_diameter".

      Parameters:
      elementName - name of the element to sweep
      parameterType - type of parameter to vary
      values - array of parameter values to evaluate
      Returns:
      map with keys "paramValues", "totalFlow_kghr", "sinkPressures_bara" containing results
    • applyParameterValue

      private void applyParameterValue(String elementName, String parameterType, double value)
      Apply a parameter value to a network element.
      Parameters:
      elementName - element name
      parameterType - parameter type
      value - value to set
    • productionForecast

      public Map<String,double[]> productionForecast(double[] reservoirPressures, double[] timestepYears)
      Run a production forecast with declining reservoir pressure over time.

      At each timestep, reservoir pressures are updated for all well IPR elements and the network is re-solved. Returns time-series of production rates, pressures, and cumulative production.

      Parameters:
      reservoirPressures - array of reservoir pressures (bara) at each timestep
      timestepYears - array of time values (years) corresponding to each pressure
      Returns:
      map with keys "time_years", "rate_kghr", "cumulative_kg", "avg_pressure_bara"
    • generateCoupledVFPTables

      public Map<String, double[][]> generateCoupledVFPTables(double[] flowRates_kghr, double[] thps)
      Generate coupled VFP tables for wells in the network.

      Unlike the basic exportVFPTables(String, double[], double[], double[], double[]), this method runs the full well system (IPR + tubing/flowline) at each (Q, THP) point. The THP is applied as the boundary condition at the wellhead node and the coupled system is solved to determine BHP. This produces accurate VFP tables suitable for reservoir simulation coupling.

      Parameters:
      flowRates_kghr - array of flow rates (kg/hr) for VFP table
      thps - array of tubing head pressures (bara) for VFP table
      Returns:
      map from well name to 2D BHP table [flowRate][THP] in bara
    • computeBHPFromIPR

      private double computeBHPFromIPR(LoopedPipeNetwork.NetworkPipe iprPipe, double pResPa, double qKgs)
      Compute BHP from IPR model given a flow rate.
      Parameters:
      iprPipe - the IPR element
      pResPa - reservoir pressure in Pa
      qKgs - flow rate in kg/s
      Returns:
      bottom-hole pressure in Pa
    • estimatePressureDropAtRate

      private double estimatePressureDropAtRate(LoopedPipeNetwork.NetworkPipe pipe, double qKgs)
      Estimate pressure drop through a network element at a given flow rate.
      Parameters:
      pipe - the network element
      qKgs - flow rate in kg/s
      Returns:
      pressure drop in Pa (positive)
    • exportCoupledVFPTables

      public void exportCoupledVFPTables(String filePath, double[] flowRates_kghr, double[] thps)
      Export coupled VFP tables to Eclipse-format files.

      Generates accurate VFPPROD tables by running the full well system at each operating point. Each well gets its own VFP table file.

      Parameters:
      filePath - base file path for VFP files
      flowRates_kghr - flow rates in kg/hr
      thps - tubing head pressures in bara
    • generateNetworkBackpressureCurve

      public Map<String,double[]> generateNetworkBackpressureCurve(String exportNodeName, double[] flowRates_kghr)
      Generate network back-pressure VFP table (VFPEXP).

      For each delivery rate at the network export boundary, this method determines the required inlet pressure. This enables reservoir-to-facility coupled simulation by providing the facility back-pressure curve for the reservoir simulator.

      Parameters:
      exportNodeName - name of the export/delivery sink node
      flowRates_kghr - array of flow rates (kg/hr) to evaluate
      Returns:
      map with "flowRate_kghr" and "requiredPressure_bara" arrays
    • validateVFPPoint

      public Map<String,Double> validateVFPPoint(double[][] vfpTable, double[] flowRates_kghr, double[] thps, double actualRate, double actualTHP, double actualBHP)
      Validate VFP table against network solution at current operating point.

      Runs the network and compares the actual BHP/rate/THP against the VFP table interpolated values. Reports discrepancy as a percentage error.

      Parameters:
      vfpTable - 2D BHP table [flowRate][THP] from generateCoupledVFPTables
      flowRates_kghr - flow rate axis values in kg/hr
      thps - THP axis values in bara
      actualRate - actual rate in kg/hr
      actualTHP - actual THP in bara
      actualBHP - actual BHP in bara
      Returns:
      map with "vfpBHP_bara", "actualBHP_bara", "error_pct"
    • interpolateVFP

      private double interpolateVFP(double[][] table, double[] xAxis, double[] yAxis, double x, double y)
      Bilinear interpolation in a 2D VFP table.
      Parameters:
      table - BHP table [flowRate][THP]
      xAxis - flow rate axis
      yAxis - THP axis
      x - query flow rate
      y - query THP
      Returns:
      interpolated BHP
    • getNetworkReport

      public String getNetworkReport()
      Get a summary report of the network solution including gas quality at each node.

      This method provides a comprehensive report with node pressures, pipe flows, element types, erosional velocity checks, and compositional data at each node. Useful for verifying network convergence and identifying potential issues.

      Returns:
      formatted report string
    • initializeFreeNodePressures

      private void initializeFreeNodePressures(List<String> freeNodeList)
      Initialize pressures for free (non-fixed) nodes using BFS propagation from fixed-pressure nodes. This produces a reasonable pressure gradient across the network, especially important for production networks where junctions sit between high-pressure sources and low-pressure sinks.
      Parameters:
      freeNodeList - names of free nodes needing pressure initialization
    • bfsDistances

      private void bfsDistances(Queue<String> queue, Map<String,Integer> dist)
      BFS distance computation from seed nodes through pipe connections.
      Parameters:
      queue - initial queue of seed nodes
      dist - map to populate with distances
    • runNewtonRaphson

      private void runNewtonRaphson(UUID id)
      Run Newton-Raphson Global Gradient Algorithm (Todini-Pilati, 1988).

      Solves the system of nodal mass balance equations + pipe head loss equations simultaneously using a Newton-Raphson scheme. This is the algorithm used by EPANET and PIPESIM Network.

      The method solves: [A11 A12] [dQ] = [f1] where A11 = diag(dh/dQ), A12 = incidence matrix, [A21 0 ] [dH] = [f2] A21 = A12^T, f1 = head residuals, f2 = flow residuals

      Parameters:
      id - calculation identifier
    • solveLinearSystem

      private double[] solveLinearSystem(double[][] matA, double[] vecB, int n)
      Solve a linear system Ax = b using the best available solver.

      Delegates to NetworkLinearSolver.solve(double[][], double[], int) which automatically selects sparse CSC LU (for n > 30), dense EJML LU, or Gaussian elimination with partial pivoting as a fallback.

      Parameters:
      matA - coefficient matrix (n x n)
      vecB - right-hand side vector (n)
      n - system size
      Returns:
      solution vector x
    • calculateMassBalanceError

      private void calculateMassBalanceError()
      Calculate overall mass balance error for the network.
    • applyFeedStreams

      private void applyFeedStreams()
      Apply conditions from connected feed streams to their corresponding source nodes.

      For each registered feed stream, updates the source node's pressure and supply flow rate from the stream's current state. Also updates the fluid template from the first feed stream if not already set.

    • updateOutletStreams

      private void updateOutletStreams()
      Create or update outlet streams at sink nodes with the solved network state.

      For each sink node, creates a Stream containing the fluid at the solved pressure, temperature, and flow rate. These streams can be connected to downstream process equipment in a ProcessSystem.

    • validate

      public List<String> validate()
      Validate the network topology. Checks connectivity, mass balance feasibility, and required fluid template.
      Returns:
      list of validation messages (empty if valid)
    • getPipeVelocity

      public double getPipeVelocity(String pipeName)
      Get pipe velocity in m/s.
      Parameters:
      pipeName - pipe name
      Returns:
      velocity in m/s (after solving)
    • getPipeHeadLoss

      public double getPipeHeadLoss(String pipeName)
      Get head loss for a specific pipe in bara.
      Parameters:
      pipeName - pipe name
      Returns:
      head loss in bara
    • getPipeReynoldsNumber

      public double getPipeReynoldsNumber(String pipeName)
      Get Reynolds number for a specific pipe.
      Parameters:
      pipeName - pipe name
      Returns:
      Reynolds number (dimensionless, after solving)
    • getPipeFrictionFactor

      public double getPipeFrictionFactor(String pipeName)
      Get Darcy friction factor for a specific pipe.
      Parameters:
      pipeName - pipe name
      Returns:
      friction factor (dimensionless, after solving)
    • getPipeFlowRegime

      public String getPipeFlowRegime(String pipeName)
      Get flow regime description for a specific pipe.
      Parameters:
      pipeName - pipe name
      Returns:
      flow regime string (e.g. "Turbulent", "Laminar")
    • getSolutionSummary

      public Map<String,Object> getSolutionSummary()
      Get a summary of the network solution.
      Returns:
      summary map
    • toJson

      public String toJson()
      Description copied from class: ProcessEquipmentBaseClass

      Serializes the Process Equipment along with its state to a JSON string.

      Specified by:
      toJson in interface ProcessEquipmentInterface
      Overrides:
      toJson in class ProcessEquipmentBaseClass
      Returns:
      json string.
    • setNodePressure

      public void setNodePressure(String nodeName, double pressureBar)
      Set the pressure of an existing node.

      Convenience method to update a node's pressure without recreating it. For source nodes this changes the reservoir pressure. For sink nodes this changes the delivery pressure (back- pressure). Throws IllegalArgumentException if the node does not exist.

      Parameters:
      nodeName - name of the existing node
      pressureBar - new pressure in bara
    • setReservoirPressure

      public void setReservoirPressure(String sourceNodeName, double pressureBar)
      Set the reservoir pressure for all IPR wells connected to a given source node.

      Updates both the source node pressure and the reservoir-pressure parameter on each connected IPR element. This is the correct API when modelling reservoir depletion.

      Parameters:
      sourceNodeName - name of the reservoir source node
      pressureBar - new reservoir pressure in bara
    • setTopsideModel

      public void setTopsideModel(ProcessSystem topside, String sinkNodeName)
      Set the topside process model to couple with the network.

      When a topside model is set, calling runCoupled() will iterate between the network solver and the topside separator/compressor model until the arrival pressure converges. The topside model should be a ProcessSystem containing at minimum an inlet separator.

      Parameters:
      topside - the topside ProcessSystem
      sinkNodeName - the network sink node to couple (the arrival point)
    • getTopsideModel

      public ProcessSystem getTopsideModel()
      Get the coupled topside model.
      Returns:
      the topside ProcessSystem, or null if not set
    • setMaxSeparatorUtilization

      public void setMaxSeparatorUtilization(double maxUtil)
      Set the maximum separator utilization for coupled optimisation.
      Parameters:
      maxUtil - maximum utilization (0.0 to 1.0), e.g. 0.90 for 90%
    • getMaxSeparatorUtilization

      public double getMaxSeparatorUtilization()
      Get the maximum separator utilization limit.
      Returns:
      max utilization (0.0 to 1.0)
    • setMaxCompressorPowerMW

      public void setMaxCompressorPowerMW(double maxPowerMW)
      Set the maximum compressor power limit for topside coupling.
      Parameters:
      maxPowerMW - power limit in MW
    • getMaxCompressorPowerMW

      public double getMaxCompressorPowerMW()
      Get the maximum compressor power limit.
      Returns:
      power limit in MW
    • setCouplingToleranceBar

      public void setCouplingToleranceBar(double toleranceBar)
      Set the coupling convergence tolerance.
      Parameters:
      toleranceBar - pressure tolerance in bar
    • setMaxCouplingIterations

      public void setMaxCouplingIterations(int maxIter)
      Set the maximum coupling iterations.
      Parameters:
      maxIter - maximum iterations
    • runCoupled

      public Map<String,Double> runCoupled()
      Run the network coupled with the topside model.

      Solves the integrated network + topside system by iterating: (1) solve the network to get outlet flow, (2) feed the outlet stream to the topside model, (3) run the topside model, (4) evaluate topside constraints (separator utilisation, compressor power), and (5) iterate. The convergence criterion is that the network arrives at a feasible operating point where all topside constraints are satisfied.

      If no topside model is set, this method simply calls SimulationInterface.run().

      Returns:
      a map with coupling results: arrivalPressure_bara, totalFlow_kghr, separatorUtilization, compressorPower_MW, converged, iterations
    • isTopsideFeasible

      public boolean isTopsideFeasible()
      Check whether the current network state is feasible given topside constraints.
      Returns:
      true if separator utilization and compressor power are within limits
    • productionForecastWithOptimization

      public Map<String,double[]> productionForecastWithOptimization(Map<String,double[]> reservoirPressureProfiles, double[] timestepYears, int optimMaxIter, double optimTolerance)
      Run production forecast with choke re-optimisation at each timestep.

      This extends productionForecast(double[], double[]) by calling optimizeProduction(int, double) at each timestep. This models how operators manage declining fields: as reservoir pressure drops, chokes are re-adjusted to maximise production or revenue. If a topside model is set, topside constraints are enforced at each timestep.

      Parameters:
      reservoirPressureProfiles - map from source-node name to pressure profiles (bara). Each array must have the same length as timestepYears.
      timestepYears - array of timestep years (e.g., 0, 1, 2, ... 20)
      optimMaxIter - max iterations for choke optimisation at each timestep
      optimTolerance - convergence tolerance for optimisation
      Returns:
      map with time-series results: time_years, rate_kghr, cumulative_kg, revenue_usd_hr, separator_util_pct, compressor_power_MW, plus per-reservoir pressures
    • productionForecastWithOptimization

      public Map<String,double[]> productionForecastWithOptimization(String sourceNodeName, double[] reservoirPressures, double[] timestepYears, int optimMaxIter, double optimTolerance)
      Simplified production forecast with re-optimisation using a single reservoir.

      Convenience overload for networks with a single reservoir source node. Delegates to productionForecastWithOptimization(Map, double[], int, double).

      Parameters:
      sourceNodeName - name of the reservoir source node
      reservoirPressures - pressure profile (bara) at each timestep
      timestepYears - array of timestep years
      optimMaxIter - max iterations for optimisation
      optimTolerance - convergence tolerance
      Returns:
      time-series results map
    • loadFluidFromE300

      public SystemInterface loadFluidFromE300(String e300FilePath)
      Load a fluid template from an Eclipse E300-format file.

      Reads the E300 file using EclipseFluidReadWrite.read(String, String) and sets it as the network's fluid template. The EOS type (SRK, PR, PR-LK) is determined automatically from the file. After loading, specific reservoir compositions can be set using setReservoirCompositionFromE300(String, String).

      Parameters:
      e300FilePath - path to the E300 file
      Returns:
      the loaded SystemInterface for further inspection
    • setReservoirCompositionFromE300

      public void setReservoirCompositionFromE300(String sourceNodeName, String e300FilePath)
      Set a reservoir node's fluid composition from a separate E300 file.

      Uses the same component characterisation (EOS, Tc, Pc, omega, BIPs) as the template fluid but applies a different molar composition read from the ZI section of the specified E300 file. This enables multi-reservoir networks where different reservoirs have different compositions but share the same fluid characterisation.

      Parameters:
      sourceNodeName - name of the reservoir source node
      e300FilePath - path to the E300 file with ZI section for this reservoir
    • setReservoirComposition

      public void setReservoirComposition(String sourceNodeName, double[] molarComposition)
      Set the molar composition for a specific reservoir source node.

      The composition array must have the same length and order as the fluid template's components. The template's EOS, BIPs, and component properties are preserved. Only the mole fractions change.

      Parameters:
      sourceNodeName - name of the reservoir source node
      molarComposition - mole fractions (same component order as template)
    • optimizeFullField

      public Map<String,Object> optimizeFullField(int optimMaxIter, double optimTolerance)
      Run full-field optimisation from reservoir to export.

      Combines network flow optimisation with topside equipment constraints to find the maximum revenue operating point. This is the highest-level optimisation call: it optimises choke settings, evaluates topside feasibility, and optionally sweeps arrival pressure to find the best overall operating point.

      Parameters:
      optimMaxIter - max iterations for choke optimisation
      optimTolerance - tolerance for optimisation convergence
      Returns:
      map with results: arrivalPressure_bara, totalFlow_kghr, revenue_usd_hr, separatorUtilization, compressorPower_MW, chokeSettings (per-well)
    • fullFieldForecast

      public Map<String,double[]> fullFieldForecast(Map<String,double[]> reservoirPressureProfiles, double[] timestepYears)
      Run a full-field lifecycle forecast from plateau through decline.

      Performs a complete field lifecycle simulation: at each year, updates reservoir pressures, re-optimises choke settings, runs the topside model, and records all KPIs. This is the highest-level forecast for field development planning.

      Parameters:
      reservoirPressureProfiles - map from source-node name to pressure arrays (bara)
      timestepYears - array of years
      Returns:
      comprehensive forecast results per timestep
    • attachReservoir

      public void attachReservoir(String sourceNodeName, SimpleReservoir reservoir, String wellType, int wellIndex)
      Attach a SimpleReservoir to a source node in the network.

      When a reservoir is attached, the network can run coupled transient simulations where production drains the reservoir (via TV-flash material balance) and the reservoir pressure drops naturally. This replaces the need for manually-supplied pressure profiles.

      The reservoir must already have the appropriate producer well added (via addGasProducer or addOilProducer).

      Parameters:
      sourceNodeName - name of the reservoir source node in the network
      reservoir - the SimpleReservoir instance
      wellType - type of producer well: "gas" or "oil"
      wellIndex - index of the well in the reservoir's producer list (0-based)
    • attachReservoir

      public void attachReservoir(String sourceNodeName, SimpleReservoir reservoir, String wellType)
      Attach a SimpleReservoir to a source node (convenience: assumes well index 0).
      Parameters:
      sourceNodeName - name of the reservoir source node
      reservoir - the SimpleReservoir instance
      wellType - "gas" or "oil"
    • getAttachedReservoir

      public SimpleReservoir getAttachedReservoir(String sourceNodeName)
      Get the attached reservoir for a source node, or null if none.
      Parameters:
      sourceNodeName - source node name
      Returns:
      the SimpleReservoir, or null
    • hasAttachedReservoirs

      public boolean hasAttachedReservoirs()
      Check whether any reservoirs are attached to this network.
      Returns:
      true if at least one reservoir is attached
    • runTransientCoupled

      public Map<String,Object> runTransientCoupled(double dtSeconds, int optimMaxIter, double optimTolerance)
      Run a single coupled timestep: solve network, drain reservoirs, update pressures.

      This is the core coupled simulation step:

      1. Optimise choke settings to maximise production at current reservoir pressures
      2. For each attached reservoir, set the well stream flow rate to match the network flow
      3. Call reservoir.runTransient(dt) to deplete moles and TV-flash at fixed volume
      4. Read the new reservoir pressure and update the network source node + IPR elements
      Parameters:
      dtSeconds - timestep duration in seconds
      optimMaxIter - max iterations for choke optimisation (0 to skip optimisation)
      optimTolerance - convergence tolerance for optimisation
      Returns:
      map with post-step state: reservoir pressures, total flow, GIP/OIP
    • productionForecastCoupled

      public Map<String,double[]> productionForecastCoupled(double[] timestepYears, int optimMaxIter, double optimTolerance)
      Run a full lifecycle forecast with coupled SimpleReservoir material balance.

      Unlike productionForecastWithOptimization(Map, double[], int, double) which requires externally-supplied pressure profiles, this method computes reservoir pressure decline naturally through the thermodynamic TV-flash material balance in SimpleReservoir. Each timestep:

      1. Network solves flow rates at current reservoir pressure
      2. Production flow rates update the reservoir well streams
      3. Reservoir runs TV-flash at fixed volume, computing new pressure and composition
      4. New pressure feeds back to network for next timestep

      Reservoirs must be attached via attachReservoir(String, SimpleReservoir, String, int) before calling this method.

      Parameters:
      timestepYears - array of timestep years (e.g., 0, 1, 2, ... 20)
      optimMaxIter - max iterations for choke optimisation at each timestep
      optimTolerance - convergence tolerance for optimisation
      Returns:
      map with time-series: time_years, rate_kghr, cumulative_kg, revenue_usd_hr, separator_util_pct, compressor_power_MW, plus per-reservoir pressures and GIP/OIP
    • productionForecastCoupled

      public Map<String,double[]> productionForecastCoupled(double[] timestepYears)
      Run a coupled lifecycle forecast with default optimisation parameters.

      Convenience overload using 30 iterations and 0.01 tolerance.

      Parameters:
      timestepYears - array of timestep years
      Returns:
      time-series results
    • createOptimizer

      public NetworkOptimizer createOptimizer()
      Create a NetworkOptimizer for this network.

      The returned optimizer provides access to BOBYQA and CMA-ES derivative-free optimization algorithms and multi-objective Pareto front exploration. Use it for formal production optimization as an alternative to the simpler gradient-based optimizeChokeOpenings(int, double) and optimizeProduction(int, double).

      Returns:
      a new optimizer bound to this network
    • optimizeProductionNLP

      public NetworkOptimizer.OptimizationResult optimizeProductionNLP()
      Optimize production using a formal NLP solver (BOBYQA by default).

      Convenience method that creates a NetworkOptimizer, runs BOBYQA to maximize total production, and returns the optimized result. For more control (algorithm selection, objective type, max evaluations), use createOptimizer() directly.

      Returns:
      optimization result
    • optimizeMultiObjective

      public List<NetworkOptimizer.OptimizationResult> optimizeMultiObjective(int numPoints)
      Run multi-objective optimization exploring the production vs compression power trade-off.

      Sweeps Pareto front weights and returns all non-dominated solutions. Each point represents a different balance between maximizing production and minimizing compressor fuel/power.

      Parameters:
      numPoints - number of Pareto front points to evaluate (5–20 typical)
      Returns:
      list of Pareto-optimal results
    • runValidationBenchmarks

      public static List<NetworkValidationBenchmarks.BenchmarkResult> runValidationBenchmarks()
      Run all validation benchmarks and return results.

      Provides quantitative verification against analytical solutions (Darcy-Weisbach, parallel pipe flow split), mass balance checks, solver cross-verification (Hardy Cross vs Newton-Raphson), pressure monotonicity, and sparse vs dense solver agreement.

      Returns:
      list of benchmark results
      See Also: