4% Preprocess model to ensure consistent parameterization.
6% Copyright (c) 2012-2026, Imperial College London
9% Convert Signal placeholders to OpenSignal or ClosedSignal based on routing
10self.convertSignalPlaceholders();
12% Initialize Cache node properties always (even
if checks disabled)
13% This
is required
for proper cache functionality
14K = getNumberOfClasses(self);
15for ist=1:self.getNumberOfNodes
16 if isa(self.nodes{ist},
'Cache')
18 if k > length(self.
nodes{ist}.popularity) || isempty(self.nodes{ist}.popularity{k})
19 self.nodes{ist}.popularity{k} = Disabled.getInstance();
22 if isempty(self.nodes{ist}.accessProb)
23 self.
nodes{ist}.accessProb = cell(K,self.nodes{ist}.items.nitems);
25 for k=1:self.nodes{ist}.items.nitems
26 % accessProb{v,k}(l,p)
is the cost (probability)
for a user-v request to item k in list l to access list p
27 if isempty(self.nodes{ist}.graph)
28 self.
nodes{ist}.accessProb{v,k} = diag(ones(1,self.nodes{ist}.nLevels),1);
29 self.nodes{ist}.accessProb{v,k}(1+self.nodes{ist}.nLevels,1+self.nodes{ist}.nLevels) = 1;
31 self.nodes{ist}.accessProb{v,k} = self.nodes{ist}.graph{k};
36 self.nodes{ist}.server.hitClass = round(self.nodes{ist}.server.hitClass);
37 self.nodes{ist}.server.missClass = round(self.nodes{ist}.server.missClass);
41% Skip remaining validation
if checks are disabled
47 M = getNumberOfStations(self);
48 K = getNumberOfClasses(self);
49 for ist=1:self.getNumberOfNodes
50 switch class(self.
nodes{ist})
57 self.
nodes{ist}.classCap(k) = Inf;
58 self.nodes{ist}.dropRule(k) = DropStrategy.WAITQ;
61 hasEnabledService =
false;
63 if k > length(self.nodes{ist}.server.serviceProcess) || isempty(self.nodes{ist}.server.serviceProcess{k})
64 self.
nodes{ist}.serviceProcess{k} = Disabled.getInstance();
65 self.nodes{ist}.classCap(k) = 0;
66 self.nodes{ist}.dropRule(k) = DropStrategy.WAITQ;
67 self.nodes{ist}.schedStrategyPar(k) = 0;
68 self.nodes{ist}.server.serviceProcess{k} = {[],ServiceStrategy.LI,Disabled.getInstance()};
70 % Check
if this class has an enabled service
71 if ~isa(self.
nodes{ist}.server.serviceProcess{k}{3},
'Disabled')
72 hasEnabledService =
true;
76 % Validate that at least one
class has service configured
77 % Skip validation
for models with Cache, Petri net, or Fork/Join elements
78 hasSpecialElements =
false;
79 for jnode=1:self.getNumberOfNodes
80 if isa(self.nodes{jnode},
'Cache') || isa(self.nodes{jnode},
'Place') || isa(self.nodes{jnode},
'Transition') || isa(self.nodes{jnode},
'Fork') || isa(self.nodes{jnode},
'Join')
81 hasSpecialElements = true;
85 if ~hasEnabledService && ~hasSpecialElements
86 line_error(mfilename, sprintf('Queue ''%s'' has no service configured for any job class. Use setService() to configure service times.', self.
nodes{ist}.name));
89 if isa(self.nodes{ist}.serviceProcess{k},
'Disabled')
90 self.
nodes{ist}.setRouting(self.classes{k},RoutingStrategy.DISABLED);
93 switch SchedStrategy.toId(self.nodes{ist}.schedStrategy)
94 case SchedStrategy.SEPT
95 servTime = zeros(1,K);
97 servTime(k) = self.nodes{ist}.serviceProcess{k}.getMean;
99 if length(unique(servTime)) ~= K
100 line_error(mfilename,
'SEPT does not support identical service time means.');
102 [servTimeSorted] = sort(unique(servTime));
103 self.nodes{ist}.schedStrategyPar = zeros(1,K);
105 if ~isnan(servTime(k))
106 self.nodes{ist}.schedStrategyPar(k) = find(servTimeSorted == servTime(k));
108 self.nodes{ist}.schedStrategyPar(k) = find(isnan(servTimeSorted));
111 case SchedStrategy.LEPT
112 servTime = zeros(1,K);
114 servTime(k) = self.nodes{ist}.serviceProcess{k}.getMean;
116 if length(unique(servTime)) ~= K
117 line_error(mfilename,
'LEPT does not support identical service time means.');
119 [servTimeSorted] = sort(unique(servTime),
'descend');
120 self.nodes{ist}.schedStrategyPar = zeros(1,K);
122 self.nodes{ist}.schedStrategyPar(k) = find(servTimeSorted == servTime(k));
126 hasEnabledService =
false;
128 if k > length(self.nodes{ist}.server.serviceProcess) || isempty(self.nodes{ist}.server.serviceProcess{k})
129 self.
nodes{ist}.serviceProcess{k} = Disabled.getInstance();
130 self.nodes{ist}.classCap(k) = 0;
131 self.nodes{ist}.dropRule(k) = DropStrategy.WAITQ;
132 self.nodes{ist}.schedStrategyPar(k) = 0;
133 self.nodes{ist}.server.serviceProcess{k} = {[],ServiceStrategy.LI,Disabled.getInstance()};
135 % Check
if this class has an enabled service
136 if ~isa(self.
nodes{ist}.server.serviceProcess{k}{3},
'Disabled')
137 hasEnabledService =
true;
141 % Validate that at least one
class has service configured
142 % Skip validation
for models with Cache, Petri net, or Fork/Join elements
143 hasSpecialElements =
false;
144 for jnode=1:self.getNumberOfNodes
145 if isa(self.nodes{jnode},
'Cache') || isa(self.nodes{jnode},
'Place') || isa(self.nodes{jnode},
'Transition') || isa(self.nodes{jnode},
'Fork') || isa(self.nodes{jnode},
'Join')
146 hasSpecialElements = true;
150 if ~hasEnabledService && ~hasSpecialElements
151 line_error(mfilename, sprintf('Delay ''%s'' has no service configured for any job class. Use setService() to configure service times.', self.
nodes{ist}.name));
154 if isa(self.nodes{ist}.serviceProcess{k},
'Disabled')
155 self.
nodes{ist}.setRouting(self.classes{k},RoutingStrategy.DISABLED);
158 switch SchedStrategy.toId(self.nodes{ist}.schedStrategy)
159 case SchedStrategy.SEPT
160 servTime = zeros(1,K);
162 servTime(k) = self.nodes{ist}.serviceProcess{k}.getMean;
164 [~,self.nodes{ist}.schedStrategyPar] = sort(servTime);
168 self.getSink.setRouting(self.classes{k},RoutingStrategy.DISABLED);
172 if k > length(self.nodes{ist}.input.sourceClasses) || isempty(self.nodes{ist}.input.sourceClasses{k})
173 self.
nodes{ist}.input.sourceClasses{k} = {[],ServiceStrategy.LI,Disabled.getInstance()};
174 self.nodes{ist}.setArrival(self.classes{k},Disabled.getInstance());
179 if k > length(self.nodes{ist}.server.serviceProcess) || isempty(self.nodes{ist}.server.serviceProcess{k})
180 self.
nodes{ist}.schedStrategyPar(k) = 0;
181 self.nodes{ist}.server.serviceProcess{k} = {[],ServiceStrategy.LI,Disabled.getInstance()};
186 if k > length(self.nodes{ist}.server.serviceProcess) || isempty(self.nodes{ist}.server.serviceProcess{k})
187 % self.nodes{i}.schedStrategyPar(k) = 0;
188 self.nodes{ist}.server.serviceProcess{k} = {[],ServiceStrategy.LI,Disabled.getInstance()};
194 if isempty(self.getIndexSourceStation) || ist ~= self.getIndexSourceStation
196 switch self.stations{ist}.server.className
200 self.stations{ist}.setProbRouting(self.classes{r}, self.stations{ist}, 0.0);
202 if isempty(self.stations{ist}.server.serviceProcess{r})
203 self.stations{ist}.server.serviceProcess{r} = {[],ServiceStrategy.LI,Disabled.getInstance()};
210 % Check
if model has Cache, Petri net, or Fork/Join elements
211 hasSpecialElements =
false;
212 for inode=1:self.getNumberOfNodes
213 if isa(self.nodes{inode},
'Cache') || isa(self.nodes{inode},
'Place') || isa(self.nodes{inode},
'Transition') || isa(self.nodes{inode},
'Fork') || isa(self.nodes{inode},
'Join')
214 hasSpecialElements = true;
219 % Validate that each job class has service configured at least one station
220 % Skip validation for models with Cache, Petri net, or Fork/Join elements
221 if ~hasSpecialElements
223 hasServiceAtAnyStation = false;
224 isUsedInCacheSetRead = false;
225 isOpenClass = isa(self.
classes{k},
'OpenClass');
227 % Check
if job
class is used in setRead at a Cache node
228 for inode=1:self.getNumberOfNodes
229 if isa(self.nodes{inode},
'Cache')
230 if size(self.
nodes{inode}.popularity,2) >= k && ~isempty(self.nodes{inode}.popularity{k}) && ~isa(self.nodes{inode}.popularity{k},
'Disabled')
231 isUsedInCacheSetRead =
true;
238 if k <= length(self.stations{ist}.server.serviceProcess) && ...
239 ~isempty(self.stations{ist}.server.serviceProcess{k}) && ...
240 length(self.stations{ist}.server.serviceProcess{k}) >= 3 && ...
241 ~isa(self.stations{ist}.server.serviceProcess{k}{3},
'Disabled')
242 hasServiceAtAnyStation = true;
247 % Only closed
classes need explicit setService configuration (open
classes route to Sink by design)
248 if ~hasServiceAtAnyStation && ~isUsedInCacheSetRead && ~isOpenClass
249 line_error(mfilename, sprintf('Job class ''%s'' has no service configured at any station. Every job class must have service configured at least one station using setService().', self.
classes{k}.name));