1%{ @file fes_build_isolated.m
2 % @brief Builds isolated subnetwork data from a station subset
for FES computation
4 % @author LINE Development Team
8 % @brief Builds isolated subnetwork data from a station subset
11 % Extracts the data needed to analyze an isolated subnetwork containing
12 % only the specified stations. This data
is used with pfqn_mva to compute
13 % throughputs
for the Flow-Equivalent Server (FES) service rates.
17 % [L, mi,
visits, isDelay] = fes_build_isolated(sn, subsetIndices, stochCompS)
22 % <tr><th>Name<th>Description
23 % <tr><td>sn<td>Network structure (from model.getStruct())
24 % <tr><td>subsetIndices<td>Array of station indices to include (1-based)
25 % <tr><td>stochCompS<td>Stochastic complement routing matrix
for subset (K*M_sub x K*M_sub)
30 % <tr><th>Name<th>Description
31 % <tr><td>L<td>Service demands matrix (M_sub x K), L(i,k) =
visits(i,k) / rate(i,k)
32 % <tr><td>mi<td>Number of servers per station (1 x M_sub), Inf
for Delay
nodes
33 % <tr><td>
visits<td>Visit ratios matrix (M_sub x K) from stochastic complement
34 % <tr><td>isDelay<td>Boolean array (1 x M_sub),
true if station
is a Delay node
37function [L, mi, visits, isDelay] = fes_build_isolated(sn, subsetIndices, stochCompS)
40M_sub = length(subsetIndices);
43L = zeros(M_sub, K); % Service demands
44mi = zeros(1, M_sub); % Number of servers
45isDelay =
false(1, M_sub); % Delay node flags
47% Extract station properties from sn
49 origIdx = subsetIndices(i);
50 nodeIdx = sn.stationToNode(origIdx);
51 nodeType = sn.nodetype(nodeIdx);
53 % Check
if this is a Delay node
54 if nodeType == NodeType.Delay
56 mi(i) = Inf; % Infinite servers
for Delay
59 % Get number of servers from nservers field
60 mi(i) = sn.nservers(origIdx);
61 if isnan(mi(i)) || mi(i) < 1
67% Compute visit ratios from stochastic complement routing matrix
68% stochCompS
is indexed by (station-1)*K + class
69% Solve pi * S = pi for stationary distribution (by class)
73 % Extract routing sub-matrix for class k within subset
74 % The stochCompS
is organized as blocks: station i, class k -> station j, class k
75 P_k = zeros(M_sub, M_sub);
81 if rowIdx <= size(stochCompS, 1) && colIdx <= size(stochCompS, 2)
82 P_k(i, j) = stochCompS(rowIdx, colIdx);
87 % Normalize rows if needed
89 rowSum = sum(P_k(i, :));
90 if rowSum > GlobalConstants.FineTol && abs(rowSum - 1) > GlobalConstants.FineTol
91 P_k(i, :) = P_k(i, :) / rowSum;
92 elseif rowSum < GlobalConstants.FineTol
93 % If no routing defined, route to self
98 % Compute visit ratios by solving pi *
P = pi
99 % This
is the stationary distribution of the embedded DTMC
100 % Use (I -
P' + e*e'/M)' * pi' = e'/M where e
is ones vector
104 % Solve for stationary distribution
105 A = I - P_k' + (e * e') / M_sub;
108 pi_k = (A' \ (e / M_sub))';
110 % If singular, use uniform distribution
111 pi_k = ones(1, M_sub) / M_sub;
114 % Ensure non-negative and normalize
117 pi_k = pi_k / sum(pi_k);
119 pi_k = ones(1, M_sub) / M_sub;
125% Compute service demands: L(i,k) =
visits(i,k) * service_time(i,k)
127 origIdx = subsetIndices(i);
130 % Get service rate from sn
131 rate = sn.rates(origIdx, k);
133 if isnan(rate) || rate <= 0 || isinf(rate)
134 % Disabled or invalid service
137 % Service demand =
visits * service time
138 % For MVA, we normalize
visits relative to first station
139 serviceTime = 1 / rate;
140 L(i, k) =
visits(i, k) * serviceTime;
145% Normalize demands so that total visit to first station
is 1
146% This
is the standard MVA convention
149 L(:, k) = L(:, k) /
visits(1, k);