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 % Also check
for heterogeneous services
77 if ~hasEnabledService && isprop(self.nodes{ist},
'heteroServiceDistributions') && ~isempty(self.nodes{ist}.heteroServiceDistributions)
78 if self.
nodes{ist}.heteroServiceDistributions.Count > 0
79 hasEnabledService =
true;
82 % Validate that at least one
class has service configured
83 % Skip validation
for models with Cache, Petri net, or Fork/Join elements
84 hasSpecialElements =
false;
85 for jnode=1:self.getNumberOfNodes
86 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')
87 hasSpecialElements = true;
91 if ~hasEnabledService && ~hasSpecialElements
92 line_error(mfilename, sprintf('Queue ''%s'' has no service configured for any job class. Use setService() to configure service times.', self.
nodes{ist}.name));
95 if isa(self.nodes{ist}.serviceProcess{k},
'Disabled')
96 self.
nodes{ist}.setRouting(self.classes{k},RoutingStrategy.DISABLED);
99 switch SchedStrategy.toId(self.nodes{ist}.schedStrategy)
100 case SchedStrategy.SEPT
101 servTime = zeros(1,K);
103 servTime(k) = self.nodes{ist}.serviceProcess{k}.getMean;
105 if length(unique(servTime)) ~= K
106 line_error(mfilename,
'SEPT does not support identical service time means.');
108 [servTimeSorted] = sort(unique(servTime));
109 self.nodes{ist}.schedStrategyPar = zeros(1,K);
111 if ~isnan(servTime(k))
112 self.nodes{ist}.schedStrategyPar(k) = find(servTimeSorted == servTime(k));
114 self.nodes{ist}.schedStrategyPar(k) = find(isnan(servTimeSorted));
117 case SchedStrategy.LEPT
118 servTime = zeros(1,K);
120 servTime(k) = self.nodes{ist}.serviceProcess{k}.getMean;
122 if length(unique(servTime)) ~= K
123 line_error(mfilename,
'LEPT does not support identical service time means.');
125 [servTimeSorted] = sort(unique(servTime),
'descend');
126 self.nodes{ist}.schedStrategyPar = zeros(1,K);
128 self.nodes{ist}.schedStrategyPar(k) = find(servTimeSorted == servTime(k));
132 hasEnabledService =
false;
134 if k > length(self.nodes{ist}.server.serviceProcess) || isempty(self.nodes{ist}.server.serviceProcess{k})
135 self.
nodes{ist}.serviceProcess{k} = Disabled.getInstance();
136 self.nodes{ist}.classCap(k) = 0;
137 self.nodes{ist}.dropRule(k) = DropStrategy.WAITQ;
138 self.nodes{ist}.schedStrategyPar(k) = 0;
139 self.nodes{ist}.server.serviceProcess{k} = {[],ServiceStrategy.LI,Disabled.getInstance()};
141 % Check
if this class has an enabled service
142 if ~isa(self.
nodes{ist}.server.serviceProcess{k}{3},
'Disabled')
143 hasEnabledService =
true;
147 % Validate that at least one
class has service configured
148 % Skip validation
for models with Cache, Petri net, or Fork/Join elements
149 hasSpecialElements =
false;
150 for jnode=1:self.getNumberOfNodes
151 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')
152 hasSpecialElements = true;
156 if ~hasEnabledService && ~hasSpecialElements
157 line_error(mfilename, sprintf('Delay ''%s'' has no service configured for any job class. Use setService() to configure service times.', self.
nodes{ist}.name));
160 if isa(self.nodes{ist}.serviceProcess{k},
'Disabled')
161 self.
nodes{ist}.setRouting(self.classes{k},RoutingStrategy.DISABLED);
164 switch SchedStrategy.toId(self.nodes{ist}.schedStrategy)
165 case SchedStrategy.SEPT
166 servTime = zeros(1,K);
168 servTime(k) = self.nodes{ist}.serviceProcess{k}.getMean;
170 [~,self.nodes{ist}.schedStrategyPar] = sort(servTime);
174 self.getSink.setRouting(self.classes{k},RoutingStrategy.DISABLED);
178 if k > length(self.nodes{ist}.input.sourceClasses) || isempty(self.nodes{ist}.input.sourceClasses{k})
179 self.
nodes{ist}.input.sourceClasses{k} = {[],ServiceStrategy.LI,Disabled.getInstance()};
180 self.nodes{ist}.setArrival(self.classes{k},Disabled.getInstance());
185 if k > length(self.nodes{ist}.server.serviceProcess) || isempty(self.nodes{ist}.server.serviceProcess{k})
186 self.
nodes{ist}.schedStrategyPar(k) = 0;
187 self.nodes{ist}.server.serviceProcess{k} = {[],ServiceStrategy.LI,Disabled.getInstance()};
192 if k > length(self.nodes{ist}.server.serviceProcess) || isempty(self.nodes{ist}.server.serviceProcess{k})
193 % self.nodes{i}.schedStrategyPar(k) = 0;
194 self.nodes{ist}.server.serviceProcess{k} = {[],ServiceStrategy.LI,Disabled.getInstance()};
200 if isempty(self.getIndexSourceStation) || ist ~= self.getIndexSourceStation
202 switch self.stations{ist}.server.className
206 self.stations{ist}.setProbRouting(self.classes{r}, self.stations{ist}, 0.0);
208 if isempty(self.stations{ist}.server.serviceProcess{r})
209 self.stations{ist}.server.serviceProcess{r} = {[],ServiceStrategy.LI,Disabled.getInstance()};
216 % Check
if model has Cache, Petri net, or Fork/Join elements
217 hasSpecialElements =
false;
218 for inode=1:self.getNumberOfNodes
219 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')
220 hasSpecialElements = true;
225 % Validate that each job class has service configured at least one station
226 % Skip validation for models with Cache, Petri net, or Fork/Join elements
227 if ~hasSpecialElements
229 hasServiceAtAnyStation = false;
230 isUsedInCacheSetRead = false;
231 isOpenClass = isa(self.
classes{k},
'OpenClass');
233 % Check
if job
class is used in setRead at a Cache node
234 for inode=1:self.getNumberOfNodes
235 if isa(self.nodes{inode},
'Cache')
236 if size(self.
nodes{inode}.popularity,2) >= k && ~isempty(self.nodes{inode}.popularity{k}) && ~isa(self.nodes{inode}.popularity{k},
'Disabled')
237 isUsedInCacheSetRead =
true;
244 if k <= length(self.stations{ist}.server.serviceProcess) && ...
245 ~isempty(self.stations{ist}.server.serviceProcess{k}) && ...
246 length(self.stations{ist}.server.serviceProcess{k}) >= 3 && ...
247 ~isa(self.stations{ist}.server.serviceProcess{k}{3},
'Disabled')
248 hasServiceAtAnyStation = true;
253 % Only closed
classes need explicit setService configuration (open
classes route to Sink by design)
254 if ~hasServiceAtAnyStation && ~isUsedInCacheSetRead && ~isOpenClass
255 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));