LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
getAvg.m
1function [QNclass,UNclass,RNclass,TNclass,ANclass,WNclass] = getAvg(self,Q,U,R,T,A,W)
2% [QNCLASS,UNCLASS,RNCLASS,TNCLASS,ANCLASS,WNCLASS] = GETAVG(SELF,Q,U,R,T,A,W)
3%
4% Compute steady-state average metrics (queue length, utilization, response time,
5% throughput, arrival rate, residence time) for all stations and job classes.
6%
7% Copyright (c) 2012-2026, Imperial College London
8% All rights reserved.
9
10sn = self.model.getStruct();
11
12if strcmp(self.options.lang,'java') && ~strcmp(self.name,'SolverDES')
13 T0=tic;
14 M = sn.nstations;
15 R = sn.nclasses;
16 % object already created by NetworkSolver.setLang()
17 self.obj.model.reset();
18 self.obj.getOptions.verbose = jline.VerboseLevel.STD;
19 SolverResult = self.obj.getAvg();
20 QN = JLINE.from_jline_matrix(SolverResult.QN);
21 UN = JLINE.from_jline_matrix(SolverResult.UN);
22 RN = JLINE.from_jline_matrix(SolverResult.RN);
23 TN = JLINE.from_jline_matrix(SolverResult.TN);
24 AN = JLINE.from_jline_matrix(SolverResult.AN);
25 WN = JLINE.from_jline_matrix(SolverResult.WN);
26 runtime=SolverResult.runtime;
27 method=SolverResult.method;
28 self.setAvgResults(QN,UN,RN,TN,AN,WN,[],[],runtime,method,1);
29 QNclass = reshape(QN,M,R);
30 UNclass = reshape(UN,M,R);
31 RNclass = reshape(RN,M,R);
32 TNclass = reshape(TN,M,R);
33 ANclass = reshape(AN,M,R);
34 WNclass = reshape(WN,M,R);
35 return
36end
37
38%%
39if nargin == 1 % no parameter
40 if isempty(self.model.handles) || ~isfield(self.model.handles,'Q') || ...
41 ~isfield(self.model.handles,'U') || ~isfield(self.model.handles,'R') || ...
42 ~isfield(self.model.handles,'T') || ~isfield(self.model.handles,'A') || ...
43 ~isfield(self.model.handles,'W')
44 reset(self); % reset results in case there are partial results saved
45 end
46 [Q,U,R,T,A,W] = self.getAvgHandles;
47elseif nargin == 2
48 handlers = Q;
49 [Q,U,R,T,A,W] = deal(handlers{:}); % set Q=handlers{1}, U=handlers{2}, ...
50end
51
52if isfield(self.options,'timespan')
53 if isfinite(self.options.timespan(2))
54 line_error(mfilename,'The getAvg method does not support the timespan option, use the getTranAvg method instead.');
55 end
56else
57 self.options.timespan = [0,Inf];
58end
59
60if ~self.hasAvgResults() || ~self.options.cache
61 runAnalyzer(self);
62 % the next line is required because getAvg can alter the chain
63 % structure in the presence of caches so we need to reload sn
64 sn = self.model.getStruct;
65 if ~self.hasAvgResults
66 line_error(mfilename,'Unable to return results for this model.');
67 end
68end % else return cached value
69
70
71M = sn.nstations;
72K = sn.nclasses;
73
74% Check if this is an SPN model (Places don't have response times)
75hasSPN = any(sn.nodetype == NodeType.Place) || any(sn.nodetype == NodeType.Transition);
76
77if ~isempty(R)
78 RNclass = filterMetric(R, self.result.Avg.R, [], sn, K, M);
79else
80 RNclass = [];
81end
82
83if ~isempty(Q)
84 % For SPNs, don't zero Q based on R because Places don't have response times
85 if hasSPN
86 zeroMaskQ = [];
87 else
88 zeroMaskQ = RNclass < 10 * GlobalConstants.FineTol;
89 end
90 QNclass = filterMetric(Q, self.result.Avg.Q, zeroMaskQ, sn, K, M);
91else
92 QNclass = [];
93end
94
95if ~isempty(U)
96 % For SPNs, don't zero U based on R because Places don't have response times
97 if hasSPN
98 zeroMaskU = [];
99 else
100 zeroMaskU = RNclass < 10 * GlobalConstants.FineTol;
101 end
102 UNclass = filterMetric(U, self.result.Avg.U, zeroMaskU, sn, K, M);
103else
104 UNclass = [];
105end
106
107if ~isempty(T)
108 TNclass = filterMetric(T, self.result.Avg.T, [], sn, K, M);
109else
110 TNclass = [];
111end
112
113if ~isempty(A)
114 zeroMask = false(size(RNclass));
115 zeroMask(sn.nodeToStation(sn.nodetype==NodeType.Source),:) = true;
116 ANclass = filterMetric(A, self.result.Avg.A, zeroMask, sn, K, M);
117else
118 ANclass = [];
119end
120
121if ~isempty(W)
122 WNclass = sn_get_residt_from_respt(sn, RNclass, W);
123else
124 WNclass = [];
125end
126
127if ~isempty(UNclass)
128 unstableQueues = find(sum(UNclass,2)>0.99 * sn.nservers);
129 if any(unstableQueues) && any(isinf(sn.njobs))
130 line_warning(mfilename,'The model has unstable queues, performance metrics may grow unbounded.\n')
131 end
132end
133end
134
135function outData = filterMetric(handle, metric, zeroMask, sn, K, M)
136% post-process Avg measure
137outData = zeros(M, K);
138for k = 1:K
139 for i = 1:M
140 if ~handle{i,k}.disabled && ~isempty(metric)
141 outData(i,k) = metric(i,k);
142 else
143 outData(i,k) = NaN;
144 end
145 end
146end
147
148% NaN values indicate that a metric is disabled
149outData(isnan(outData)) = 0;
150% set to zero entries associated to immediate transitions
151outData(zeroMask) = 0;
152% round to zero numerical perturbations
153outData(outData < GlobalConstants.FineTol) = 0;
154
155% set to zero metrics for classes that are unreachable
156% but skip this check for fork-join models where the visits calculation
157% doesn't correctly capture the parent class visiting Join and downstream nodes
158% also skip when no chains are defined (routing not specified)
159% also skip for SPN models where Places don't have traditional visits
160hasForkJoin = any(sn.nodetype == NodeType.Fork) && any(sn.nodetype == NodeType.Join);
161hasSPN = any(sn.nodetype == NodeType.Place) || any(sn.nodetype == NodeType.Transition);
162
163if sn.nchains > 0 && ~hasSPN % Only check reachability if chains are defined and not SPN
164 for k = 1:K
165 c = sn.chains(:, k)>0;
166 if any(c) % Only check if class k belongs to a chain
167 for i = 1:M
168 if sn.visits{c}(i,k) == 0
169 % For fork-join models, don't zero out if the metric has a non-zero value
170 % from simulation - the visits calculation doesn't capture fork-join semantics
171 % where Join outputs the parent class
172 if hasForkJoin && metric(i,k) > GlobalConstants.FineTol
173 continue; % Trust the simulation result
174 end
175 outData(i,k) = 0;
176 end
177 end
178 end
179 end
180end
181end