Class ProcessGraph

java.lang.Object
neqsim.process.processmodel.graph.ProcessGraph
All Implemented Interfaces:
Serializable

public class ProcessGraph extends Object implements Serializable
Represents a process flowsheet as an explicit directed graph (DAG with potential cycles).

This class provides comprehensive graph analysis capabilities:

  • Explicit DAG of equipment (nodes) and streams (edges)
  • Topological sorting with cycle detection
  • Strongly connected component (SCC) detection for recycle handling
  • Calculation order derivation (not assumed from insertion order)
  • Partitioning for parallel execution
  • Graph neural network compatible representation
  • AI agent compatibility for flowsheet reasoning

Why this matters: Without explicit graph representation:

  • Execution order = insertion order (fragile)
  • Rearranging units or adding recycles late causes wrong results
  • Parallel execution risks silent convergence failures
With a graph:
  • Execution order is derived, not assumed
  • Recycles and feedback loops are explicit objects
  • AI agents can reason about flowsheet structure
Version:
1.0
Author:
NeqSim
See Also:
  • Field Details

    • serialVersionUID

      private static final long serialVersionUID
      See Also:
    • logger

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

      private final List<ProcessNode> nodes
      All nodes in the graph, indexed by their index.
    • edges

      private final List<ProcessEdge> edges
      All edges in the graph.
    • equipmentToNode

      private final Map<Object, ProcessNode> equipmentToNode
      Map from equipment to its node.
    • nameToNode

      private final Map<String, ProcessNode> nameToNode
      Map from equipment name to node.
    • equipmentTypeMapping

      private final Map<String,Integer> equipmentTypeMapping
      Equipment type to index mapping for feature vectors.
    • cachedTopologicalOrder

      private transient List<ProcessNode> cachedTopologicalOrder
      Cached topological order (null if not computed or invalidated).
    • cachedCycleAnalysis

      private transient ProcessGraph.CycleAnalysisResult cachedCycleAnalysis
      Cached cycle analysis (null if not computed or invalidated).
    • cachedSCCResult

      private transient ProcessGraph.SCCResult cachedSCCResult
      Cached SCC result (null if not computed or invalidated).
    • structureChanged

      private transient boolean structureChanged
      Whether the graph structure has changed since last analysis.
  • Constructor Details

    • ProcessGraph

      public ProcessGraph()
      Creates an empty process graph.
  • Method Details

    • addNode

      public ProcessNode addNode(ProcessEquipmentInterface equipment)
      Adds a node to the graph.
      Parameters:
      equipment - the equipment to add
      Returns:
      the created node
    • addEdge

      public ProcessEdge addEdge(ProcessNode source, ProcessNode target, StreamInterface stream)
      Adds an edge between two nodes.
      Parameters:
      source - source node
      target - target node
      stream - the stream connecting them (may be null)
      Returns:
      the created edge
    • addEdge

      public ProcessEdge addEdge(ProcessEquipmentInterface sourceEquipment, ProcessEquipmentInterface targetEquipment, StreamInterface stream)
      Adds an edge between two equipment units.
      Parameters:
      sourceEquipment - source equipment
      targetEquipment - target equipment
      stream - the stream connecting them (may be null)
      Returns:
      the created edge, or null if either equipment is not in the graph
    • getNode

      public ProcessNode getNode(ProcessEquipmentInterface equipment)
      Gets a node by equipment.
      Parameters:
      equipment - the equipment
      Returns:
      the node, or null if not found
    • getNode

      public ProcessNode getNode(String name)
      Gets a node by name.
      Parameters:
      name - the equipment name
      Returns:
      the node, or null if not found
    • getNode

      public ProcessNode getNode(int index)
      Gets a node by index.
      Parameters:
      index - the node index
      Returns:
      the node
      Throws:
      IndexOutOfBoundsException - if index is out of range
    • getNodes

      public List<ProcessNode> getNodes()
      Returns:
      unmodifiable list of all nodes
    • getEdges

      public List<ProcessEdge> getEdges()
      Returns:
      unmodifiable list of all edges
    • getNodeCount

      public int getNodeCount()
      Returns:
      number of nodes
    • getEdgeCount

      public int getEdgeCount()
      Returns:
      number of edges
    • invalidateCache

      private void invalidateCache()
      Invalidates cached analysis results.
    • resetTraversalState

      private void resetTraversalState()
      Resets all nodes' traversal state.
    • analyzeCycles

      public ProcessGraph.CycleAnalysisResult analyzeCycles()
      Analyzes the graph for cycles using DFS.
      Returns:
      cycle analysis result
    • detectCyclesDFS

      private void detectCyclesDFS(ProcessNode node, Deque<ProcessNode> currentPath, List<List<ProcessNode>> cycles, List<ProcessEdge> backEdges)
    • hasCycles

      public boolean hasCycles()
      Checks if the graph has cycles.
      Returns:
      true if there are cycles
    • getTopologicalOrder

      public List<ProcessNode> getTopologicalOrder()
      Computes topological order of nodes.

      If the graph has cycles, back edges are ignored to produce a valid ordering for the acyclic portion. This is essential for determining correct calculation order.

      Returns:
      list of nodes in topological order
    • topologicalSortDFS

      private void topologicalSortDFS(ProcessNode node, Deque<ProcessNode> stack)
    • getCalculationOrder

      public List<ProcessEquipmentInterface> getCalculationOrder()
      Gets calculation order - the order in which units should be executed.

      This is the primary method for deriving execution order from topology rather than insertion order.

      Returns:
      list of equipment in calculation order
    • findStronglyConnectedComponents

      public ProcessGraph.SCCResult findStronglyConnectedComponents()
      Computes strongly connected components using Tarjan's algorithm.

      SCCs are used to identify recycle loops that need iterative solving.

      Returns:
      SCC analysis result
    • tarjanDFS

      private void tarjanDFS(ProcessNode node, int[] ids, int[] low, boolean[] onStack, Deque<ProcessNode> stack, List<List<ProcessNode>> components, int[] idCounter)
    • partitionForParallelExecution

      public ProcessGraph.ParallelPartition partitionForParallelExecution()
      Partitions nodes into levels for parallel execution.

      Nodes at the same level have no dependencies on each other and can be executed in parallel. This uses the longest path algorithm on the DAG (ignoring back edges).

      Returns:
      parallel partition result
    • getNodeFeatureMatrix

      public double[][] getNodeFeatureMatrix()
      Gets node feature matrix for GNN.
      Returns:
      2D array [numNodes][numFeatures]
    • getEdgeIndexTensor

      public int[][] getEdgeIndexTensor()
      Gets edge index tensor in COO format for GNN.
      Returns:
      2D array [[sources], [targets]]
    • getEdgeFeatureMatrix

      public double[][] getEdgeFeatureMatrix()
      Gets edge feature matrix for GNN.
      Returns:
      2D array [numEdges][numFeatures]
    • getAdjacencyList

      public Map<Integer, List<Integer>> getAdjacencyList()
      Gets adjacency list representation.
      Returns:
      map from node index to list of successor indices
    • getAdjacencyMatrix

      public boolean[][] getAdjacencyMatrix()
      Gets adjacency matrix (sparse representation).
      Returns:
      adjacency matrix as boolean 2D array
    • getSourceNodes

      public List<ProcessNode> getSourceNodes()
      Gets all source nodes (no incoming edges).
      Returns:
      list of source nodes
    • getSinkNodes

      public List<ProcessNode> getSinkNodes()
      Gets all sink nodes (no outgoing edges).
      Returns:
      list of sink nodes
    • getRecycleEdges

      public List<ProcessEdge> getRecycleEdges()
      Gets all recycle edges.
      Returns:
      list of edges marked as recycles or back edges
    • getNodesInRecycleLoops

      public Set<ProcessNode> getNodesInRecycleLoops()
      Gets nodes that are part of recycle loops.
      Returns:
      set of nodes in recycle loops
    • validate

      public List<String> validate()
      Validates the graph structure.
      Returns:
      list of validation issues (empty if valid)
    • selectTearStreams

      public ProcessGraph.TearStreamResult selectTearStreams()
      Selects optimal tear streams to break all cycles in the flowsheet.

      This method implements a heuristic approach to the Minimum Feedback Arc Set (MFAS) problem, which is NP-hard in general. The algorithm:

      1. Finds all strongly connected components (SCCs)
      2. For each non-trivial SCC (size > 1), selects the best tear stream
      3. Uses heuristics based on stream characteristics to select optimal tears

      The heuristics prefer streams that:

      • Have fewer downstream dependencies (minimizes propagation)
      • Are marked as recycle streams (user intent)
      • Have lower flow rates (faster convergence)
      • Break the most cycles (greedy optimization)
      Returns:
      tear stream selection result
    • selectBestTearStreamForSCC

      private ProcessEdge selectBestTearStreamForSCC(List<ProcessNode> scc, List<ProcessEdge> sccEdges)
      Selects the best tear stream for a single SCC using heuristics.
      Parameters:
      scc - the strongly connected component
      sccEdges - edges within the SCC
      Returns:
      the best tear stream edge
    • computeTearStreamScore

      private double computeTearStreamScore(ProcessEdge edge, List<ProcessNode> scc)
      Computes a heuristic score for a potential tear stream. Higher scores indicate better tear stream candidates.
      Parameters:
      edge - the candidate edge
      scc - the SCC containing the edge
      Returns:
      heuristic score
    • selectTearStreamsForFastConvergence

      public ProcessGraph.TearStreamResult selectTearStreamsForFastConvergence()
      Suggests tear streams using flow rate minimization heuristic.

      This method selects tear streams that minimize the total flow rate being torn, which typically leads to faster convergence as smaller streams have less impact on the overall mass and energy balance.

      Note: This requires flow rate information to be available in the stream objects. If not available, falls back to the default selection algorithm.

      Returns:
      tear stream selection result optimized for convergence speed
    • analyzeTearStreamSensitivity

      public ProcessGraph.SensitivityAnalysisResult analyzeTearStreamSensitivity(List<ProcessNode> scc)
      Analyzes sensitivity of potential tear streams to select optimal ones.

      This method estimates the sensitivity of each potential tear stream by analyzing how much perturbations in the tear stream values would propagate through the SCC. Lower sensitivity means faster convergence.

      The sensitivity is estimated using graph-based heuristics:

      • Path length from tear to its re-entry point
      • Number of equipment units in the recycle path
      • Branching factor (flow splitting in the loop)
      • Equipment type weights (separators have higher sensitivity than heaters)
      Parameters:
      scc - the strongly connected component to analyze
      Returns:
      sensitivity analysis result
    • computeEdgeSensitivity

      private double computeEdgeSensitivity(ProcessEdge edge, List<ProcessNode> scc, Set<ProcessNode> sccNodes)
      Computes sensitivity score for a potential tear stream.

      Lower sensitivity indicates a better tear stream candidate.

      Parameters:
      edge - the candidate tear stream
      scc - the SCC containing the edge
      sccNodes - set of nodes in the SCC for fast lookup
      Returns:
      sensitivity score
    • computePathLengthInSCC

      private int computePathLengthInSCC(ProcessNode source, ProcessNode target, Set<ProcessNode> sccNodes)
      Computes the path length from source to target within an SCC.
      Parameters:
      source - starting node
      target - ending node
      sccNodes - nodes in the SCC
      Returns:
      path length, or -1 if no path exists
    • computeEquipmentTypeSensitivity

      private double computeEquipmentTypeSensitivity(List<ProcessNode> scc)
      Computes average equipment type sensitivity for nodes in SCC.
      Parameters:
      scc - the SCC nodes
      Returns:
      average sensitivity weight
    • computeBranchingFactor

      private double computeBranchingFactor(List<ProcessNode> scc, Set<ProcessNode> sccNodes)
      Computes the average branching factor in an SCC.
      Parameters:
      scc - the SCC nodes
      sccNodes - set of SCC nodes for lookup
      Returns:
      average branching factor
    • selectTearStreamsWithSensitivity

      public ProcessGraph.TearStreamResult selectTearStreamsWithSensitivity()
      Selects tear streams using sensitivity analysis for each SCC.

      This method improves upon the heuristic-based selection by computing actual sensitivity estimates that predict convergence behavior.

      Returns:
      tear stream selection result with sensitivity-optimized tears
    • getSensitivityAnalysisReport

      public String getSensitivityAnalysisReport()
      Gets a detailed report of sensitivity analysis for all SCCs.
      Returns:
      formatted string with sensitivity analysis details
    • validateTearStreams

      public boolean validateTearStreams(List<ProcessEdge> tearStreams)
      Validates that selected tear streams break all cycles.
      Parameters:
      tearStreams - the tear streams to validate
      Returns:
      true if all cycles are broken
    • hasCycleWithoutTears

      private boolean hasCycleWithoutTears(ProcessNode node, Set<ProcessNode> visited, Set<ProcessNode> inStack, Set<ProcessEdge> tearSet)
      DFS helper to detect cycles ignoring specified tear streams.
      Parameters:
      node - the current node to check
      visited - set of already visited nodes
      inStack - set of nodes currently in the recursion stack
      tearSet - set of tear edges to ignore
      Returns:
      true if a cycle is detected, false otherwise
    • getSummary

      public String getSummary()
      Generates a summary of the graph structure.
      Returns:
      summary string
    • toString

      public String toString()
      Overrides:
      toString in class Object