1function [QN,UN,RN,TN,AN,WN] = getEnsembleAvg(self)
2% [QN,UN,RN,TN,AN,WN] = GETENSEMBLEAVG(SELF)
4% Check
if solver was properly constructed (may have returned early due to unsupported features)
5if isempty(self.ensemble)
6 QN = []; UN = []; RN = []; TN = []; AN = []; WN = [];
10% Show library attribution if verbose and not yet shown
11if self.options.verbose ~= VerboseLevel.SILENT && ~GlobalConstants.isLibraryAttributionShown()
12 libs = SolverLN.getLibrariesUsed([], self.options);
14 line_printf('The solver will leverage %s.\n', strjoin(libs, ', '));
15 GlobalConstants.setLibraryAttributionShown(true);
19iterate(self); % run iterations
20QN = nan(self.lqn.nidx,1);
21UN = nan(self.lqn.nidx,1);
22RN = nan(self.lqn.nidx,1);
23TN = nan(self.lqn.nidx,1);
24PN = nan(self.lqn.nidx,1); % utilization will be first stored here
25SN = nan(self.lqn.nidx,1); % response time will be first stored here
26WN = nan(self.lqn.nidx,1); % residence time
27AN = nan(self.lqn.nidx,1); % not available yet
28WN_processed = false(self.lqn.nidx,1); % track activities already accumulated into task WN
31 clientIdx = self.ensemble{e}.attribute.clientIdx;
32 serverIdx = self.ensemble{e}.attribute.serverIdx;
33 sourceIdx = self.ensemble{e}.attribute.sourceIdx;
34 % determine processor metrics
35 if self.ensemble{e}.stations{serverIdx}.attribute.ishost
36 hidx = self.ensemble{e}.stations{serverIdx}.attribute.idx;
39 for c=1:self.ensemble{e}.getNumberOfClasses
40 if self.ensemble{e}.classes{c}.completes
44 t = max(t, self.results{end,e}.TN(clientIdx,c));
47 t = max(t, self.results{end,e}.TN(sourceIdx,c));
49 TN(hidx) = TN(hidx) + max(t,self.results{end,e}.TN(serverIdx,c));
51 type = self.ensemble{e}.classes{c}.attribute(1);
53 case LayeredNetworkElement.ACTIVITY
54 aidx = self.ensemble{e}.classes{c}.attribute(2);
55 tidx = self.lqn.parent(aidx);
56 if isnan(PN(aidx)), PN(aidx)=0; end
57 if isnan(PN(tidx)), PN(tidx)=0; end
58 PN(aidx) = PN(aidx) + self.results{end,e}.UN(serverIdx,c);
59 PN(tidx) = PN(tidx) + self.results{end,e}.UN(serverIdx,c);
60 PN(hidx) = PN(hidx) + self.results{end,e}.UN(serverIdx,c);
63 TN(hidx) = NaN; % added
for consistency with LQNS
66 % determine remaining metrics
67 for c=1:self.ensemble{e}.getNumberOfClasses
68 type = self.ensemble{e}.classes{c}.attribute(1);
70 case LayeredNetworkElement.TASK
71 tidx = self.ensemble{e}.classes{c}.attribute(2);
72 if self.ensemble{e}.stations{serverIdx}.attribute.ishost
74 % store the result in the processor
76 TN(tidx) = self.results{end,e}.TN(clientIdx,c);
81 case LayeredNetworkElement.ENTRY
82 eidx = self.ensemble{e}.classes{c}.attribute(2);
83 tidx = self.lqn.parent(eidx);
84 % For phase-2 models, use residt (caller
's view with overtaking)
85 % Otherwise use servt (total service time = response time)
86 if self.hasPhase2 && self.servt_ph2(eidx) > GlobalConstants.FineTol
87 SN(eidx) = self.residt(eidx); % Phase-1 + overtaking correction
89 SN(eidx) = self.servt(eidx);
91 if self.ensemble{e}.stations{serverIdx}.attribute.ishost
93 % store the result in the processor model
94 if isnan(TN(eidx)), TN(eidx)=0; end
95 TN(eidx) = self.results{end,e}.TN(clientIdx,c);
100 case LayeredNetworkElement.CALL
101 cidx = self.ensemble{e}.classes{c}.attribute(2);
102 aidx = self.lqn.callpair(cidx,1);
103 % Only sync calls contribute to caller's response time
104 if self.lqn.calltype(cidx) == CallType.SYNC
105 SN(aidx) = SN(aidx) + self.results{end,e}.RN(serverIdx,c) * self.lqn.callproc{cidx}.getMean();
107 if isnan(QN(aidx)), QN(aidx)=0; end
108 QN(aidx) = QN(aidx) + self.results{end,e}.QN(serverIdx,c);
109 case LayeredNetworkElement.ACTIVITY
110 aidx = self.ensemble{e}.classes{c}.attribute(2);
111 tidx = self.lqn.parent(aidx);
112 if isnan(QN(tidx)), QN(tidx)=0; end
113 QN(tidx) = QN(tidx) + self.results{end,e}.QN(serverIdx,c);
114 if isnan(TN(aidx)), TN(aidx)=0; end
115 if isnan(QN(aidx)), QN(aidx)=0; end
117 % For forwarding targets: propagate activity metrics to entry and task
118 % Check
if task has its own
class (non-forwarding targets
do)
119 hasTaskClass = any(self.ensemble{e}.attribute.tasks(:,2) == tidx);
121 % Propagate activity throughput to task
for forwarding targets
122 if isnan(TN(tidx)), TN(tidx)=0; end
123 switch self.ensemble{e}.classes{c}.type
124 case JobClassType.CLOSED
125 TN(tidx) = TN(tidx) + self.results{end,e}.TN(serverIdx,c);
126 case JobClassType.OPEN
127 TN(tidx) = TN(tidx) + self.results{end,e}.TN(sourceIdx,c);
131 % Find the entry
this activity
is bound to
132 for eidx_check = self.lqn.entriesof{tidx}
133 if self.lqn.graph(eidx_check, aidx) > 0 % Activity
is bound to
this entry
134 if isnan(TN(eidx_check)), TN(eidx_check)=0; end
135 if isnan(QN(eidx_check)), QN(eidx_check)=0; end
136 if isnan(SN(eidx_check)), SN(eidx_check)=0; end
137 % Add activity metrics to entry (
for entries without their own
classes)
139 switch self.ensemble{e}.classes{c}.type
140 case JobClassType.CLOSED
141 actTput = self.results{end,e}.TN(serverIdx,c);
142 case JobClassType.OPEN
143 actTput = self.results{end,e}.TN(sourceIdx,c);
145 % Only add
if entry doesn
't have its own class (forwarding target case)
146 hasEntryClass = any(self.ensemble{e}.attribute.entries(:,2) == eidx_check);
148 TN(eidx_check) = TN(eidx_check) + actTput;
149 QN(eidx_check) = QN(eidx_check) + self.results{end,e}.QN(serverIdx,c);
150 SN(eidx_check) = SN(eidx_check) + self.results{end,e}.RN(serverIdx,c);
155 switch self.ensemble{e}.classes{c}.type
156 case JobClassType.CLOSED
157 TN(aidx) = TN(aidx) + self.results{end,e}.TN(serverIdx,c);
158 case JobClassType.OPEN
159 TN(aidx) = TN(aidx) + self.results{end,e}.TN(sourceIdx,c);
161 % SN(aidx) = self.servt(aidx);
162 if isnan(SN(aidx)), SN(aidx)=0; end
163 SN(aidx) = SN(aidx) + self.results{end,e}.RN(serverIdx,c);
164 if isnan(RN(aidx)), RN(aidx)=0; end
165 RN(aidx) = RN(aidx) + self.results{end,e}.RN(serverIdx,c);
166 if isnan(WN(aidx)), WN(aidx)=0; end
167 if isnan(WN(tidx)), WN(tidx)=0; end
168 % Use self.residt (computed via QN/TN_ref in updateMetricsDefault)
169 % instead of layer WN to avoid fork+loop visit distortion
170 WN(aidx) = self.residt(aidx);
171 if ~WN_processed(aidx)
172 WN(tidx) = WN(tidx) + self.residt(aidx);
173 WN_processed(aidx) = true;
175 if isnan(QN(aidx)), QN(aidx)=0; end
176 QN(aidx) = QN(aidx) + self.results{end,e}.QN(serverIdx,c);
181for e=1:self.lqn.nentries
182 eidx = self.lqn.eshift + e;
183 tidx = self.lqn.parent(eidx);
184 if isnan(UN(tidx)), UN(tidx)=0; end
186 % Phase-2 support: utilization includes both phases
187 if self.hasPhase2 && self.servt_ph2(eidx) > GlobalConstants.FineTol
188 % Phase-1 utilization
189 self.util_ph1(eidx) = TN(eidx) * self.servt_ph1(eidx);
190 % Phase-2 utilization
191 self.util_ph2(eidx) = TN(eidx) * self.servt_ph2(eidx);
192 % Total utilization = both phases (server is busy during both)
193 UN(eidx) = self.util_ph1(eidx) + self.util_ph2(eidx);
195 % Standard calculation for entries without phase-2
196 UN(eidx) = TN(eidx)*SN(eidx);
199 for aidx=self.lqn.actsof{tidx}
200 UN(aidx) = TN(aidx)*SN(aidx);
202 UN(tidx) = UN(tidx) + UN(eidx);
205for idx=find(self.ignore)