1function [XN,UN,QN,RN,TN,CN,tranSysState,tranSync,sn] = solver_ssa_analyzer_nrm(sn, options)
2% SOLVER_SSA_ANALYZER_NRM Performance indices from SSA/NRM simulation
3% This variant runs
the next‑reaction–method SSA on
the network SN and
4% returns mean throughput (XN), utilisation (UN), queue length (QN),
5% response time (RN), throughput per station (TN) and
class residence
6% time (CN). Results are based on
the empirical state probabilities
7% obtained from SOLVER_SSA_NRM.
8% *** Only
the scheduling policies listed in ALLOWEDSCHED below are
9% supported. Any other policy triggers an error. Cache
nodes are not
10% supported in
this simplified variant. ***
12% Validate scheduling policies ---------------------------------------------------
13% Keep
this list in sync with
the propensity
switch of SOLVER_SSA_NRM and with
14%
the NRM eligibility gate of SOLVER_SSA_ANALYZER.
15allowedSched = [SchedStrategy.INF, SchedStrategy.EXT, SchedStrategy.PS, ...
16 SchedStrategy.LPS, SchedStrategy.DPS, SchedStrategy.GPS, ...
17 SchedStrategy.PSPRIO, SchedStrategy.DPSPRIO, SchedStrategy.GPSPRIO, ...
18 SchedStrategy.FCFS, SchedStrategy.LCFS, SchedStrategy.SIRO, ...
19 SchedStrategy.HOL, SchedStrategy.SEPT, SchedStrategy.LEPT, ...
20 SchedStrategy.LCFSPR, SchedStrategy.PAS, SchedStrategy.POLLING];
21if any(~arrayfun(@(s) any(s == allowedSched), sn.sched))
22 unsupported = unique(sn.sched(~arrayfun(@(s) any(s == allowedSched), sn.sched)));
23 names = strjoin(arrayfun(@(s) SchedStrategy.toText(s), unsupported, 'UniformOutput', false), ', ');
24 error('solver_ssa_analyzer_nrm:UnsupportedPolicy', ...
25 'The NRM method does not support
the scheduling policy: %s.', names);
28% Immediate feedback (sn.immfeed)
is not modeled by
the NRM reaction network:
29% self-loops are handled as ordinary class-switching with re-queueing. Warn so
30% an immfeed model
is not silently given
the re-queueing result;
the serial SSA
31% engine (method='serial') does honor immediate feedback.
32if isfield(sn,'immfeed') && ~isempty(sn.immfeed) && any(sn.immfeed(:))
33 line_warning(mfilename,'SolverSSA(method=nrm) does not model immediate feedback (immfeed); self-loops are treated as class-switching with re-queueing. Use method=''serial'' for immediate feedback.\n');
36% Shorthands --------------------------------------------------------------------
37M = sn.nstations; % number of stations
38K = sn.nclasses; % number of classes
39S = sn.nservers; % server multiplicities per station
40NK = sn.njobs'; % population vector (closed chains)
41PH = sn.proc; % service‑process MAPs/PHs
42sched = sn.sched; % scheduling policies
44% Pre‑allocate performance vectors/matrices --------------------------------------
45XN = NaN(1,K); % class throughput
46UN = NaN(M,K); % utilisation
47QN = NaN(M,K); % mean jobs at station
48RN = NaN(M,K); % response time
49TN = NaN(M,K); % class throughput at station (departures)
50CN = NaN(1,K); % cycle time per class
51% -------------------------------------------------------------------------------
52% 1) Run
the SSA/NRM simulator ---------------------------------------------------
53% -------------------------------------------------------------------------------
54% The mean-index buffered engine (solver_ssa_nrm)
is the SSA default
55% (options.config.state_space_gen='none') and supports every discipline in
56% allowedSched above, including
the buffer-ordered priority families (HOL/
57% SEPT/LEPT/SIRO). The explicit state-space engine (solver_ssa_nrm_space)
is
58% only for state_space_gen='full'/'reachable' and its propensity switch omits
59% those disciplines. Treat an absent state_space_gen as 'none' so a model built
60% from a generic lineDefaults options struct (which never sets
the SSA-specific
61% default) still routes to
the buffered engine rather than crashing in
the
62% space engine on an unhandled schedule.
64if isfield(options, 'config') && isfield(options.config, 'state_space_gen')
65 ssg = options.config.state_space_gen;
66 if ~(strcmp(ssg, 'none') || strcmp(ssg, 'default'))
67 useBufferedNrm = false;
71 % Compute only stead-state mean performance indices while running nrm
72 [QN, UN, RN, TN, CN, XN, ~, sn] = solver_ssa_nrm(sn, options);
74 % Use explicit state space generation version
76 % -------------------------------------------------------------------------------
77 % 1) Run
the SSA/NRM simulator ---------------------------------------------------
78 % -------------------------------------------------------------------------------
79 [pi,space,depRates,sn] = solver_ssa_nrm_space(sn, options);
82 % -------------------------------------------------------------------------------
83 % 2) Aggregate performance indices ----------------------------------------------
84 % -------------------------------------------------------------------------------
85 % Global class throughput ---------------------------------------------------------
88 refnd = sn.stationToNode(sn.refstat(k)); % reference (sink) stateful index
89 XN(k) = pi * depRates(:, (refnd-1)*K+k);
93 % Station‑level metrics -----------------------------------------------------------
96 ind = sn.stationToNode(ist); % reference (sink) stateful index
98 TN(ist,k) = pi * depRates(:, (ind-1)*K+k);
99 QN(ist,k) = pi * space(:, (ind-1)*K + k);
103 case {SchedStrategy.INF, SchedStrategy.EXT}
104 % Infinite‑server or external delay: utilisation equals mean jobs
105 UN(ist,:) = QN(ist,:);
107 case {SchedStrategy.PS, SchedStrategy.FCFS, SchedStrategy.LCFS}
108 % Class-dependent stations normalize by
the declared per-
class
109 % peak rate (sn.cdscalingpeak), giving Util = T*S/peak; ordinary
110 % stations divide by
the server
count S.
111 isCd = ~isempty(sn.cdscaling) && ist <= numel(sn.cdscaling) && ~isempty(sn.cdscaling{ist});
113 if ~isempty(PH{ist}{k})
115 sdiv = sn.cdscalingpeak(ist,k);
120 UN(ist,k) = pi * depRates(:, (ind-1)*K+k) / rates(ist,k) / sdiv;
129 % Response time (Little
's law) & cycle time --------------------------------------
133 RN(ist,k) = QN(ist,k) / TN(ist,k);
138 CN(k) = NK(k) / XN(k);
142% -------------------------------------------------------------------------------
143% 3) Post‑process and clean‑up ----------------------------------------------------
144% -------------------------------------------------------------------------------
145QN(isnan(QN)) = 0; UN(isnan(UN)) = 0; RN(isnan(RN)) = 0;
146XN(isnan(XN)) = 0; TN(isnan(TN)) = 0; CN(isnan(CN)) = 0;
148tranSysState = []; % transient traces removed in this streamlined version