1function refreshStruct(self, hardRefresh)
4% Copyright (c) 2012-2026, Imperial College London
12%% store invariant information
13if self.hasStruct && ~hardRefresh
14 rtorig = self.sn.rtorig; %
this must be destroyed with resetNetwork
17if self.hasStruct && ~hardRefresh
18 nodetypes = sn.nodetypes;
19 classnames = sn.classnames;
20 nodenames = sn.nodenames;
23 nodetypes = getNodeTypes(self);
24 classnames = getClassNames(self);
25 nodenames = getNodeNames(self);
26 refstat = getReferenceStations(self);
28 % Append FCR names and types to node lists (only when refreshing)
29 for f = 1:length(self.regions)
30 fcr = self.regions{f};
31 nodenames{end+1} = fcr.getName();
32 nodetypes(end+1) = NodeType.FiniteCapacityRegion;
35conn = self.getConnectionMatrix;
36njobs = getNumberOfJobs(self);
37numservers = getStationServers(self);
38lldscaling = getLimitedLoadDependence(self);
39cdscaling = getLimitedClassDependence(self);
41%% init minimal structure
42sn = NetworkStruct(); % create in self to ensure propagation
47 sn.rtorig = self.sn.rtorig;
48 if isfield(self.sn,
'reward')
49 sn.reward = self.sn.reward; % preserve reward definitions
54% sn.nnodes counts physical
nodes only; FCRs are
virtual nodes appended to nodenames/nodetypes
55sn.nnodes = numel(self.nodes);
56sn.nclasses = length(classnames);
58%% get routing strategies
59routing = zeros(sn.nnodes, sn.nclasses);
62 if isempty(self.nodes{ind}.output.outputStrategy{r})
63 routing(ind,r) = RoutingStrategy.DISABLED;
65 routing(ind,r) = RoutingStrategy.fromText(self.
nodes{ind}.output.outputStrategy{r}{2});
69sn.isslc =
false(sn.nclasses,1);
71 if isa(self.classes{r},
'SelfLoopingClass')
75sn.issignal = false(sn.nclasses,1);
76sn.signaltype = cell(sn.nclasses,1);
78 sn.signaltype{r} = NaN;
80% Detect Signal classes and populate issignal/signaltype
82 if isa(self.classes{r},
'Signal')
83 sn.issignal(r) = true;
84 sn.signaltype{r} = self.classes{r}.signalType;
87sn.nclosedjobs = sum(njobs(isfinite(njobs)));
88sn.nservers = numservers;
89sn.isstation = (nodetypes == NodeType.Source | nodetypes == NodeType.Delay | nodetypes == NodeType.Queue | nodetypes == NodeType.Join | nodetypes == NodeType.Place);
90sn.nstations = sum(sn.isstation);
91sn.scv = ones(sn.nstations,sn.nclasses);
94sn.space = cell(sn.nstations,1);
98sn.lldscaling = lldscaling;
99sn.cdscaling = cdscaling;
100sn.nodetype = nodetypes;
101sn.nstations = sum(sn.isstation);
102sn.isstateful = (nodetypes == NodeType.Source | nodetypes == NodeType.Delay | nodetypes == NodeType.Queue | nodetypes == NodeType.Cache | nodetypes == NodeType.Join | nodetypes == NodeType.Router | nodetypes == NodeType.Place | nodetypes == NodeType.Transition);
103sn.isstatedep = false(sn.nnodes,3); % col 1: buffer, col 2: srv, col 3: routing
105for ind = 1:sn.nstations
106 if isa(self.stations{ind},'Queue
')
107 sn.isfunction(ind) = ~isempty(self.stations{ind}.setupTime);
111 switch sn.nodetype(ind)
113 sn.isstatedep(ind,2) = true; % state dependent service
114 % case NodeType.Place
115 % self.nodes{ind}.init();
116 % case NodeType.Transition
117 % self.nodes{ind}.init(); % this erases enablingConditions
121 switch sn.routing(ind,r)
122 case {RoutingStrategy.RROBIN, RoutingStrategy.WRROBIN, RoutingStrategy.JSQ, RoutingStrategy.RL, RoutingStrategy.KCHOICES}
123 sn.isstatedep(ind,3) = true; % state dependent routing
128sn.nstateful = sum(sn.isstateful);
129sn.state = cell(sn.nstations,1);
133sn.nodenames = nodenames;
134sn.classnames = classnames;
137sn.nodeToStateful =[];
140sn.stationToStateful =[];
141sn.statefulToNode =[];
142sn.statefulToStation =[];
144 sn.nodeToStateful(ind) = nd2sf(sn,ind);
145 sn.nodeToStation(ind) = nd2st(sn,ind);
147for ist=1:sn.nstations
148 sn.stationToNode(ist) = st2nd(sn,ist);
149 sn.stationToStateful(ist) = st2sf(sn,ist);
151for isf=1:sn.nstateful
152 sn.statefulToNode(isf) = sf2nd(sn,isf);
153 sn.statefulToStation(isf) = sf2st(sn,isf);
155sn.fj = self.getForkJoins();
157refreshPriorities(self);
158refreshProcesses(self);
160% Check if priorities are specified but no priority-aware scheduling policy is used
162if any(sn.classprio(:) > 0)
163 % Priority classes exist, check if any station uses priority-aware scheduling
164 prioScheds = [SchedStrategy.PSPRIO, SchedStrategy.DPSPRIO, SchedStrategy.GPSPRIO, ...
165 SchedStrategy.HOL, SchedStrategy.FCFSPRIO, SchedStrategy.LCFSPRIO, ...
166 SchedStrategy.LCFSPRPRIO, SchedStrategy.LCFSPIPRIO];
167 if ~any(ismember(sn.sched, prioScheds))
168 line_warning(mfilename, 'Priority classes are specified but no priority-aware scheduling policy (PSPRIO, DPSPRIO, GPSPRIO, HOL, FCFSPRIO, LCFSPRIO, LCFSPRPRIO, LCFSPIPRIO) is used in the model. Priorities will be ignored.\n
');
172if any(nodetypes == NodeType.Cache)
173 % this also refreshes the routing matrix and the visits
174 refreshChains(self, false); % wantVisits
176 % this also refreshes the routing matrix and the visits
177 refreshChains(self, true); % wantVisits
180refclasses = getReferenceClasses(self);
181refclass = zeros(1,sn.nchains);
183 isect = intersect(sn.inchain{c},find(refclasses));
188sn.refclass = refclass;
190refreshLocalVars(self); % depends on chains (rtnodes)
191refreshPetriNetNodes(self);
192refreshSync(self); % this assumes that refreshChain is called before
193refreshGlobalSync(self);
196self.hasStruct = true;
198if any(sn.fj(:)) % if there are forks
199 % try to recompute visits after mixed-model transformation:
200 % we obtain the visits of auxiliary class from the transformed model
201 % then we sum them to the original classes
203 [nonfjmodel, fjclassmap, forkmap, fanOut] = ModelAdapter.mmt(self);
205 % in this case, one of the forks is degenerate with a single
206 % outgoing link so no visit correction is needed
207 line_warning(mfilename,'The specified fork-join topology has partial support, only SolverJMT simulation results may be reliable.\n
');
210 fsn = nonfjmodel.getStruct();
211 for new_chain=(sn.nchains+1):fsn.nchains
212 anyAuxClass = fsn.inchain{new_chain}(1);
213 origFork = forkmap(anyAuxClass);
214 origChain = find(sn.chains(:,fjclassmap(anyAuxClass))); % original chain of the class
215 fsn.nodevisits{new_chain}(fsn.nodetype == NodeType.Source | fsn.nodetype == NodeType.Sink | fsn.nodetype == NodeType.Fork,:) = 0;
216 Vaux = fsn.nodevisits{new_chain}(:,fsn.inchain{new_chain});
217 if fsn.nnodes > sn.nnodes % if source-sink were added
218 Vaux(fsn.nodetype == NodeType.Source | fsn.nodetype == NodeType.Sink,:) = [];
220 X = sn.nodevisits{origChain};
221 for jaux=1:length(fsn.inchain{new_chain})
222 j = sn.inchain{origChain}(jaux); % class in the original chain
223 self.sn.nodevisits{origChain}(:,j) = sn.nodeparam{origFork}.fanOut*(X(:,j) + Vaux(:,jaux));
228sn = refreshRegions(self);
232function stat_idx = nd2st(sn, node_idx)
233% STAT_IDX = ND2ST(NODE_IDX)
235if sn.isstation(node_idx)
236 stat_idx = at(cumsum(sn.isstation),node_idx);
242function node_idx = st2nd(sn,stat_idx)
243% NODE_IDX = ST2ND(SELF,STAT_IDX)
245v = cumsum(sn.isstation) == stat_idx;
247 node_idx = find(v, 1);
253function sful_idx = st2sf(sn,stat_idx)
254% SFUL_IDX = ST2SF(SELF,STAT_IDX)
256sful_idx = nd2sf(sn,st2nd(sn,stat_idx));
259function sful_idx = nd2sf(sn, node_idx)
260% SFUL_IDX = ND2SF(NODE_IDX)
262if sn.isstateful(node_idx)
263 sful_idx = at(cumsum(sn.isstateful),node_idx);
269function node_idx = sf2nd(sn,stat_idx)
270% NODE_IDX = SF2ND(SELF,STAT_IDX)
272v = cumsum(sn.isstateful) == stat_idx;
274 node_idx = find(v, 1);
280function stat_idx = sf2st(sn,sful_idx)
281% STAT_IDX = SF2ST(SELF,SFUL_IDX)
283stat_idx = nd2st(sn,sf2nd(sn,sful_idx));