1function [capacity, classcap, droprule] = refreshCapacity(self)
2% [CAPACITY, CLASSCAP, DROPRULE] = REFRESHCAPACITY()
4% Copyright (c) 2012-2026, Imperial College London
6%I = getNumberOfStatefulNodes(self);
7M = getNumberOfStations(self);
8K = getNumberOfClasses(self);
10% set zero buffers
for classes that are disabled
11classcap = Inf*ones(M,K);
12chaincap = Inf*ones(M,K);
14droprule = DropStrategy.WAITQ*ones(M,K);
19 inchain = sn.inchain{c};
21 chainCap = sum(njobs(inchain));
23 station = self.getStationByIndex(ist);
25 if sn.nodetype(sn.stationToNode(ist)) ~= NodeType.Source
26 % Guard against dropRule array not having entry
for class r.
27 % A 0 also means
"not set": assigning dropRule(k)
for a
class k
28 % beyond
the current length grows
the array and fills
the gap
29 % with 0, which
is not a DropStrategy member (WAITQ
is -1), so
30 % those classes must fall through to
the default below rather
31 % than leak a 0 into sn.droprule.
32 if length(station.dropRule) >= r && ~isempty(station.dropRule(r)) ...
33 && station.dropRule(r) ~= 0
34 stationDropRule = station.dropRule(r);
38 % An EXPLICIT setDropRule(WAITQ) at a plain finite buffer
39 % reachable by an OPEN
class asks for something LINE does not
40 % implement:
the arrival should wait until
the buffer frees, but
41 % there
is no upstream queue to wait in (
the Source
is an
42 % infinite generator). No solver honours it -- SolverCTMC drops
43 %
the arrival, SolverJMT blocks at
the Source and ignores
the cap
44 % -- so reject rather than pick one silently. Only
the explicit
45 % rule
is rejected:
the WAITQ marker that
this function derives
46 % below
for a no-buffer station or a closed
class is untouched,
47 % as are
the blocking policies LINE does implement (BAS/BBS/RSRD).
48 % Place and Join write
the WAITQ marker themselves (Place in its
49 % constructor,
for every
class), so
for those
nodes the marker
is
50 % an internal
default and NOT a user request: exclude them, or a
51 % bounded place would be rejected
for a rule its user never set.
52 isUserRuleNode = ~isa(station,
'Place') && ~isa(station,
'Join');
53 % classCap(r) == 0
is the "no per-class constraint" sentinel
54 % (a
class absent from
the station), NOT a real finite buffer, so
55 % it must not
count as a finite capacity here -- otherwise
the gate
56 % below rejects a WAITQ open
class for a station it never
visits
57 % (e.g. InitClass at a Delay it does not route to). A real finite
58 % per-
class buffer
is strictly positive and non-infinite.
59 classCapIsFinite = length(station.classCap) >= r ...
60 && station.classCap(r) > 0 && ~isinf(station.classCap(r));
61 stationCapIsFinite = ~isempty(station.cap) && station.cap >= 0 ...
62 && ~isinf(station.cap) && station.cap < intmax;
63 if isUserRuleNode && ~isempty(stationDropRule) && stationDropRule == DropStrategy.WAITQ ...
64 && isinf(njobs(r)) && (stationCapIsFinite || classCapIsFinite)
65 line_error(mfilename, sprintf([
'Station ''%s'' declares setDropRule(WAITQ) for the open class ''%s'' at a finite capacity. ' ...
66 'LINE does not implement waiting-room blocking for an open arrival at a plain finite buffer: no solver honours this combination ' ...
67 '(SolverCTMC drops the arrival, SolverJMT blocks at the Source and ignores the capacity). ' ...
68 'Use setDropRule(DropStrategy.DROP) for a loss station (M/M/1/K), or one of the blocking policies LINE implements ' ...
69 '(DropStrategy.BAS, DropStrategy.BBS, DropStrategy.RSRD) for blocking between stations.'], ...
70 station.getName(), self.classes{r}.getName()));
72 % Derive
the rule from
the final station capacity when
the user
73 % has not set one, so that
the result does not depend on whether
74 % setCapacity was called before or after setService.
75 if isempty(stationDropRule)
76 if isinf(station.cap) || station.cap >= intmax
77 % No buffer:
the rule
is never consulted.
78 stationDropRule = DropStrategy.WAITQ;
79 elseif station.cap >= 0 && ~isinf(njobs(r))
80 % Real finite buffer and a CLOSED
class. DROP would make
81 %
the JMT
export destroy jobs, and LINE
's reference
82 % (CTMC) conserves the closed population instead, so DROP
83 % is not a defensible default here. What a closed class
84 % should do at a full station is currently unsettled in
85 % LINE itself (CTMC blocks and conserves the population,
86 % SSA and LDES drop), so leave the WAITQ marker rather
87 % than decide it here.
88 stationDropRule = DropStrategy.WAITQ;
90 % Real finite buffer and an OPEN class: LINE's reference
91 % (CTMC) loses
the arrival, so DROP.
92 % Also
the cap<0
case, which JMT2LINE produces by copying
93 % JMT
's "size=-1 means unlimited" sentinel into cap: sn.cap
94 % below then ignores it and falls back to the population,
95 % and saveBufferCapacity emits size=-1, so the rule is
96 % unreachable and its historical DROP value is kept rather
97 % than churning every imported model.
98 stationDropRule = DropStrategy.DROP;
101 droprule(ist,r) = stationDropRule;
103 if isnan(rates(ist,r)) && sn.nodetype(sn.stationToNode(ist)) ~= NodeType.Place
107 chaincap(ist,c) = chainCap;
108 classcap(ist,r) = chainCap;
109 % Guard against classCap array not having entry for class r
110 if length(station.classCap) >= r && station.classCap(r) >= 0
111 classcap(ist,r) = min(classcap(ist,r), station.classCap(r));
114 classcap(ist,r) = min(classcap(ist,r), station.cap);
121 station = self.getStationByIndex(ist);
122 % If station has explicit finite cap set, use it directly (Kendall K notation)
123 % Otherwise use minimum of chain cap sum and class cap sum
124 if station.cap >= 0 && ~isinf(station.cap)
125 % Explicit capacity set - use directly as total capacity
126 capacity(ist,1) = station.cap;
128 capacity(ist,1) = min([sum(chaincap(ist,:)),sum(classcap(ist,:))]);
131self.sn.cap = capacity;
132self.sn.classcap = classcap;
133self.sn.droprule = droprule;