3% Flow-Equivalent Server (FES) Aggregation Example
5% This example demonstrates how to use ModelAdapter.aggregateFES to replace
6% a subset of stations in a closed product-form queueing network with a
7% single Flow-Equivalent Server (FES).
9% The FES has Limited Joint Dependence (LJD) service rates where the rate
10%
for class-c in state (n1,...,nK) equals the throughput of
class-c in an
11% isolated subnetwork consisting only of the subset stations.
13fprintf(
'=== Flow-Equivalent Server (FES) Aggregation Example ===\n\n');
15%% Create original 4-station tandem network with 2
classes
16fprintf(
'Creating original 4-station network...\n');
18N1 = 3; % number of
class-1 jobs
19N2 = 2; % number of
class-2 jobs
21model = Network(
'OriginalModel');
24node{1} = Delay(model,
'ThinkTime');
25node{2} = Queue(model,
'Queue1', SchedStrategy.PS);
26node{3} = Queue(model,
'Queue2', SchedStrategy.PS);
27node{4} = Queue(model,
'Queue3', SchedStrategy.PS);
30jobclass{1} = ClosedClass(model,
'Class1', N1, node{1}, 0);
31jobclass{2} = ClosedClass(model,
'Class2', N2, node{1}, 0);
34node{1}.setService(
jobclass{1}, Exp.fitMean(5.0));
35node{1}.setService(
jobclass{2}, Exp.fitMean(4.0));
37node{2}.setService(
jobclass{1}, Exp.fitMean(1.5));
38node{2}.setService(
jobclass{2}, Exp.fitMean(2.0));
40node{3}.setService(
jobclass{1}, Exp.fitMean(1.0));
41node{3}.setService(
jobclass{2}, Exp.fitMean(1.2));
43node{4}.setService(
jobclass{1}, Exp.fitMean(0.8));
44node{4}.setService(
jobclass{2}, Exp.fitMean(1.0));
46% Set up tandem routing (all jobs visit all stations in order)
47P = model.initRoutingMatrix();
55%% Solve original model with MVA
56fprintf(
'\n--- Solving Original Model ---\n');
57solverOriginal = SolverMVA(model);
58AvgTableOriginal = solverOriginal.getAvgTable;
59fprintf(
'Original model results:\n');
60disp(AvgTableOriginal);
62%% Aggregate stations 2 and 3 into a Flow-Equivalent Server
63fprintf(
'\n--- Creating FES Model ---\n');
64fprintf(
'Aggregating Queue1 and Queue2 into a single FES...\n');
66stationSubset = {node{2}, node{3}};
67options.verbose =
true;
68options.solver =
'mva';
71 [fesModel, fesStation, deaggInfo] = ModelAdapter.aggregateFES(model, stationSubset, options);
73 fprintf(
'\nFES model created successfully!\n');
74 fprintf(
'FES station name: %s\n', fesStation.getName());
75 fprintf(
'Number of stations in FES model: %d\n', fesModel.getNumberOfStations());
78 fprintf(
'\n--- Solving FES Model ---\n');
79 solverFES = SolverMVA(fesModel);
80 AvgTableFES = solverFES.getAvgTable;
81 fprintf(
'FES model results:\n');
84 %% Compare throughputs
85 fprintf(
'\n--- Throughput Comparison ---\n');
87 % Extract original throughputs (at Delay station, which
is common)
91 % Find throughput in original
92 for row = 1:height(AvgTableOriginal)
93 if strcmp(
string(AvgTableOriginal.JobClass(row)), className) && ...
94 strcmp(
string(AvgTableOriginal.Station(row)),
'ThinkTime')
95 tputOrig = AvgTableOriginal.Tput(row);
100 % Find throughput in FES model
101 for row = 1:height(AvgTableFES)
102 if strcmp(
string(AvgTableFES.JobClass(row)), className) && ...
103 strcmp(
string(AvgTableFES.Station(row)), 'ThinkTime')
104 tputFES = AvgTableFES.Tput(row);
109 relError = abs(tputOrig - tputFES) / max(tputOrig, 1e-10) * 100;
110 fprintf('%s: Original=%.4f, FES=%.4f, RelError=%.2f%%\n', ...
111 className, tputOrig, tputFES, relError);
114 %% Examine deaggregation info
115 fprintf('\n--- Deaggregation Info ---\n');
116 fprintf('Subset station indices: %s\n', mat2str(deaggInfo.subsetIndices));
117 fprintf('Complement station indices: %s\n', mat2str(deaggInfo.complementIndices));
118 fprintf('Cutoffs used: %s\n', mat2str(deaggInfo.cutoffs));
119 fprintf('FES node index: %d\n', deaggInfo.fesNodeIdx);
122 fprintf('Error during FES aggregation: %s\n', ME.message);
123 fprintf('Stack trace:\n');
124 for i = 1:length(ME.stack)
125 fprintf(' %s (line %d)\n', ME.stack(i).name, ME.stack(i).line);
129fprintf('\n=== Example Complete ===\n');