1function [isSingleQueue, fluidInfo] = fluid_is_single_queue(sn)
2%FLUID_IS_SINGLE_QUEUE Check
if network
is a valid single-queue topology
for MFQ
4% [isSingleQueue, fluidInfo] = FLUID_IS_SINGLE_QUEUE(sn)
6% Validates that the network structure
is suitable
for the MFQ solver.
7% MFQ
requires: Source -> single Queue -> Sink topology with single server.
10% sn (
struct): Network structure from Network.getStruct()
13% isSingleQueue (logical): True if topology
is valid for MFQ
14% fluidInfo (struct): Contains topology information and error message if invalid
17 fluidInfo.errorMsg =
'';
18 fluidInfo.sourceIdx = [];
19 fluidInfo.queueIdx = [];
20 fluidInfo.sinkIdx = [];
21 fluidInfo.sourceStation = [];
22 fluidInfo.queueStation = [];
24 % Check 1: Must have at least one open
class
25 if ~any(isinf(sn.njobs))
26 fluidInfo.errorMsg = 'Not an open model - all
classes are closed';
27 isSingleQueue = false;
31 % Work at the node level (like fj_isfj.m does)
32 % Find Source, Queue, and Sink
nodes
33 sourceNodeIdx = find(sn.nodetype == NodeType.Source);
34 queueNodeIdx = find(sn.nodetype == NodeType.Queue);
35 sinkNodeIdx = find(sn.nodetype == NodeType.Sink);
37 % Check: exactly one source
38 if length(sourceNodeIdx) == 0
39 fluidInfo.errorMsg = 'No source node found';
40 isSingleQueue = false;
42 elseif length(sourceNodeIdx) > 1
43 fluidInfo.errorMsg = sprintf('Multiple source
nodes found (%d)', length(sourceNodeIdx));
44 isSingleQueue = false;
48 % Check: exactly one sink
49 if length(sinkNodeIdx) == 0
50 fluidInfo.errorMsg = 'No sink node found';
51 isSingleQueue = false;
53 elseif length(sinkNodeIdx) > 1
54 fluidInfo.errorMsg = sprintf('Multiple sink
nodes found (%d)', length(sinkNodeIdx));
55 isSingleQueue = false;
59 % Check: exactly one queue
60 if length(queueNodeIdx) == 0
61 fluidInfo.errorMsg = 'No queue node found';
62 isSingleQueue = false;
64 elseif length(queueNodeIdx) > 1
65 fluidInfo.errorMsg = sprintf('Multiple queue
nodes found (%d) - MFQ supports single queue only', length(queueNodeIdx));
66 isSingleQueue = false;
70 % Get station indices (for stations that exist)
71 sourceStation = sn.nodeToStation(sourceNodeIdx);
72 queueStation = sn.nodeToStation(queueNodeIdx);
74 % Check: Single-server or infinite-server constraint
75 c = sn.nservers(queueStation);
77 fluidInfo.errorMsg = sprintf('Multi-server queue (c=%d) not supported - MFQ requires c=1 or c=Inf', c);
78 isSingleQueue = false;
82 % Check: No self-loops (independence assumption)
83 % Check routing matrix for self-loops in the queue station
86 % Check if queue routes back to itself
87 rtIdx = (queueStation - 1) * K + k;
88 if sn.rt(rtIdx, rtIdx) > 0
89 fluidInfo.errorMsg = 'Self-loop detected at queue - violates independence assumption';
90 isSingleQueue = false;
96 fluidInfo.sourceIdx = sourceNodeIdx;
97 fluidInfo.queueIdx = queueNodeIdx;
98 fluidInfo.sinkIdx = sinkNodeIdx;
99 fluidInfo.sourceStation = sourceStation;
100 fluidInfo.queueStation = queueStation;
101 isSingleQueue = true;