1function used = getUsedLangFeatures(self)
2% USED = GETUSEDLANGFEATURES()
4% Copyright (c) 2012-2026, Imperial College London
8if ~isempty(self.getIndexClosedClasses)
9 self.setUsedLangFeature('ClosedClass');
11if ~isempty(self.getIndexOpenClasses)
12 self.setUsedLangFeature('OpenClass');
15% G-network signal classes. Signal
is an unresolved placeholder that becomes
16% OpenSignal or ClosedSignal at refreshStruct time, so all three names are
17% inspected here: getUsedLangFeatures may run before resolution.
18for r=1:getNumberOfClasses(self)
22 self.setUsedLangFeature('ClosedSignal');
24 self.setUsedLangFeature('OpenSignal');
25 else % unresolved placeholder: resolves by presence of a Source
26 if isempty(self.getIndexSourceNode)
27 self.setUsedLangFeature('ClosedSignal');
29 self.setUsedLangFeature('OpenSignal');
33 case SignalType.NEGATIVE
34 self.setUsedLangFeature('SignalType_NEGATIVE');
36 self.setUsedLangFeature('SignalType_REPLY');
37 case SignalType.CATASTROPHE
38 self.setUsedLangFeature('SignalType_CATASTROPHE');
40 % Removal specification: a distribution makes
the signal remove a
41 % batch of jobs rather than exactly one (sn.signalremdist), while a
42 % non-default policy selects which jobs are removed (sn.signalrempolicy).
43 % These are separate capabilities: a solver may honour one and not
the
44 % other, so they are reported as distinct features.
45 if ~isempty(
jobclass.removalDistribution)
46 self.setUsedLangFeature('SignalBatchRemoval');
48 if ~isempty(
jobclass.removalPolicy) &&
jobclass.removalPolicy ~= RemovalPolicy.RANDOM
49 self.setUsedLangFeature('SignalRemovalPolicy');
55for i=1:getNumberOfNodes(self)
56 for r=1:getNumberOfClasses(self)
57 try % not all
nodes have all classes
58 switch class(self.
nodes{i})
59 case {
'Queue',
'QueueingStation',
'DelayStation',
'Delay'}
60 if ~isempty(self.nodes{i}.server.serviceProcess{r})
61 % Immediate/Disabled are internal placeholders, not
62 % user-facing distributions (same exclusion as JAR)
63 distName = self.nodes{i}.server.serviceProcess{r}{3}.name;
64 if ~strcmp(distName,
'Immediate') && ~strcmp(distName,
'Disabled')
65 self.setUsedLangFeature(distName);
67 if self.
nodes{i}.numberOfServers > 1
68 %self.setUsedLangFeature(
'MultiServer')
70 self.setUsedLangFeature(SchedStrategy.toFeature(self.nodes{i}.schedStrategy));
71 self.setUsedLangFeature(RoutingStrategy.toFeature(self.nodes{i}.output.outputStrategy{r}{2}));
74 self.setUsedLangFeature(RoutingStrategy.toFeature(self.nodes{i}.output.outputStrategy{r}{2}));
76 distName = self.nodes{i}.input.sourceClasses{r}{3}.name;
77 if ~strcmp(distName,
'Immediate') && ~strcmp(distName,
'Disabled')
78 self.setUsedLangFeature(distName);
80 self.setUsedLangFeature('Source');
82 self.setUsedLangFeature('StatelessClassSwitcher');
83 self.setUsedLangFeature('ClassSwitch');
85 self.setUsedLangFeature('Fork');
86 self.setUsedLangFeature('Forker');
88 self.setUsedLangFeature('Join');
89 self.setUsedLangFeature('Joiner');
91 self.setUsedLangFeature('Sink');
93 self.setUsedLangFeature('CacheClassSwitcher');
94 self.setUsedLangFeature('Cache');
95 self.setUsedLangFeature(ReplacementStrategy.toFeature(self.
nodes{i}.replacestrategy));
96 if ~isempty(self.nodes{i}.retrievalClassIndices)
97 % delayed-hit retrieval system (setRetrievalSystem):
98 % per-item retrieval classes; JMT cannot load these
99 self.setUsedLangFeature(
'CacheRetrieval');
102 self.setUsedLangFeature(
'Transition');
103 self.setUsedLangFeature(
'Enabling');
104 self.setUsedLangFeature(
'Timing');
105 self.setUsedLangFeature(
'Firing');
106 % Inhibitor arcs: flag only when a finite threshold
is set,
107 % so plain SPNs are not gated out of
solvers lacking it.
108 if isprop(self.nodes{i},
'inhibitingConditions')
109 for m=1:numel(self.
nodes{i}.inhibitingConditions)
110 if any(isfinite(self.nodes{i}.inhibitingConditions{m}(:)))
111 self.setUsedLangFeature(
'Inhibiting');
117 self.setUsedLangFeature(
'Storage');
118 self.setUsedLangFeature(
'Linkage');
119 self.setUsedLangFeature(
'Place');
120 if isprop(self.nodes{i},
'queueing') && ~isempty(self.nodes{i}.queueing) && self.nodes{i}.queueing
121 self.setUsedLangFeature(
'QueueingPlace');
127% Class-dependent scaling (setLimitedClassDependence): registered so that
128%
solvers ignoring
the handle (e.g. JMT) reject
the model instead of
129% silently solving it as if
the scaling were absent.
130for i=1:getNumberOfNodes(self)
131 if isa(self.
nodes{i},
'Station') && ~isempty(self.
nodes{i}.lcdScaling)
132 self.setUsedLangFeature(
'ClassDependence');
136% Setup/delay-off times (setDelayOff): registered so that
solvers ignoring
137% them (CTMC, SSA, MVA, NC, FLD) reject
the model instead of silently solving
139for i=1:getNumberOfNodes(self)
140 if isa(self.
nodes{i},
'Queue') && ~isempty(self.
nodes{i}.setupTime)
141 self.setUsedLangFeature(
'SetupDelayOff');
145% Impatience (setPatience / setBalking): registered so that
solvers ignoring
146% it reject
the model instead of silently solving it impatience-free. MVA, NC,
147% MAM and FLD return
the impatience-free rho/(1-rho) (9.0 on an M/M/1 with
148% rho=0.9 against an exact 1.1565), so they must not accept these models.
149% Reneging
is flagged by impatienceTypes, not by
the patience distribution:
150% ProcessType.EXP == 0 in MATLAB, so a `~= 0` test silently misses exponential
151% patience,
the most common case.
152for i=1:getNumberOfNodes(self)
153 nodeObj = self.
nodes{i};
154 if isa(nodeObj,
'Queue') && ~isempty(nodeObj.impatienceTypes)
155 for r=1:numel(nodeObj.impatienceTypes)
156 if ~isempty(nodeObj.impatienceTypes{r}) && nodeObj.impatienceTypes{r} == ImpatienceType.RENEGING
157 self.setUsedLangFeature(
'Reneging');
163for i=1:getNumberOfNodes(self)
164 nodeObj = self.nodes{i};
165 if isa(nodeObj,
'Queue') && ~isempty(nodeObj.balkingStrategies)
166 for r=1:numel(nodeObj.balkingStrategies)
167 if ~isempty(nodeObj.balkingStrategies{r})
168 self.setUsedLangFeature(
'Balking');
174used = self.usedFeatures;