1function [nonfjmodel, fjclassmap, fjforkmap, fj_auxiliary_delays] = ht(model)
2 % Transforms the queueing network containing a FJ subsystem into a queueing network without one.
3 % Fork node replaced by routers
4 % One artificial
class is created for each parallel branch and for each class
5 % Join node replaced by delay
6 % We add another delay to model the sojourn time of the original
classes.
8 % This approach
is derived by PHILIP HEIDELBERGER and KISHOR S. TRIVEDI in
9 %
"Analytic Queueing Models for Programs with Internal Concurrency"
11 fjclassmap = []; % s = fjclassmap(r)
for auxiliary
class r gives the index s of the original class
12 fjforkmap = []; % f = fjforkmap(r)
for auxiliary
class r gives the associated fork node f
13 nonfjmodel = model.copy();
14 nonfjmodel.allowReplace =
true;
15 P = nonfjmodel.getLinkedRoutingMatrix;
16 nonfjmodel.resetNetwork(
true);
17 nonfjmodel.resetStruct();
18 Vnodes = cellsum(sn.nodevisits);
20 forkIndexes = find(sn.nodetype == NodeType.Fork)
';
21 fj_auxiliary_delays = {}; % d = fj_auxiliary_delays{j} for join j gives the additional delay d to mimic the sojourn time of the original classes
23 %% Replace each fork with a router
25 nonfjmodel.nodes{f} = Router(nonfjmodel, nonfjmodel.nodes{f}.name);
26 forkedClasses{f,1} = find(Vnodes(f,:)>0);
29 %% Replace each join with a delay
30 for j=find(sn.nodetype == NodeType.Join)'
31 nonfjmodel.nodes{j} = Delay(nonfjmodel, nonfjmodel.nodes{j}.name);
32 nonfjmodel.stations{model.nodes{j}.stationIndex} = nonfjmodel.nodes{j};
33 for c=1:length(nonfjmodel.classes)
34 nonfjmodel.
nodes{j}.setService(nonfjmodel.classes{c},Immediate());
37 %% Add another delay to mimic the sojourn time of the original
classes for the artificial
classes
38 new_delay = Delay(nonfjmodel, [
'Auxiliary Delay - ', nonfjmodel.nodes{j}.name]);
39 fj_auxiliary_delays{j} = new_delay.index;
40 for r=1:length(nonfjmodel.classes)
41 new_delay.setService(nonfjmodel.
classes{r}, Immediate());
45 P{r, s}(new_delay.index, :) =
P{r,s}(j, :);
46 P{r, s}(:, new_delay.index) = 0.0;
49 P{r, r}(j, new_delay.index) = 1.0;
53 % nonfjmodel.stations={nonfjmodel.stations{1:model.getNumberOfStations}}
'; % remove automatically added station and put it where the join was
54 nonfjmodel.connections = zeros(length(nonfjmodel.nodes));
55 %% Create the auxiliary classes
57 joinIdx = find(sn.fj(f,:));
59 line_error(mfilename,'SolverMVA supports at present only a single join station per fork node.
');
61 forkedChains = find(sum(sn.chains(:,forkedClasses{f}),2));
63 aux_classes = {}; % aux = aux_classes{r, par} gives the auxiliary
class created for original class r and parallel branch par
64 inchain = find(sn.chains(fc,:)); inchain = inchain(:)
';
66 if sn.nodevisits{fc}(f,r) == 0
69 parallel_branches = length(model.nodes{f}.output.outputStrategy{r}{3}); % Assumption: every class forks into exactly the same parallel branches
70 for par=1:parallel_branches % One auxiliary class for each parallel branch
71 if model.nodes{f}.output.tasksPerLink > 1
72 line_error(mfilename, 'Multiple tasks per link are not supported in H-T.
');
74 if isa(model.classes{r},'OpenClass
')
75 line_error(mfilename, 'H-T method can be used only on closed models.
');
77 aux_population = model.nodes{f}.output.tasksPerLink * model.classes{r}.population;
78 aux_classes{r, par} = ClosedClass(nonfjmodel, [nonfjmodel.classes{r}.name,'.
',nonfjmodel.nodes{f}.name, '.B
', int2str(par)], aux_population, nonfjmodel.nodes{fj_auxiliary_delays{joinIdx}}, 0);
79 fjclassmap(aux_classes{r, par}.index) = nonfjmodel.classes{r}.index;
80 fjforkmap(aux_classes{r, par}.index) = f;
81 % Set the service rates at the join node and at the stations
86 nonfjmodel.nodes{i}.setService(aux_classes{r, par},Immediate());
87 case {NodeType.Source, NodeType.Fork}
90 nonfjmodel.nodes{i}.setService(aux_classes{r, par},model.nodes{i}.getService(model.classes{r}).copy());
94 nonfjmodel.nodes{fj_auxiliary_delays{joinIdx}}.setService(aux_classes{r, par}, Immediate());
98 % Set the routing of the artificial classes
100 if sn.nodevisits{fc}(f,r) == 0
103 parallel_branches = length(model.nodes{f}.output.outputStrategy{r}{3});
105 if sn.nodevisits{fc}(f,s) == 0
108 for par=1:parallel_branches
109 P{aux_classes{r, par}, aux_classes{s, par}} = P{r, s};
110 P{aux_classes{r, par}, aux_classes{s, par}}(f, :) = 0.0;
111 P{aux_classes{r, par}, aux_classes{s, par}}(f, model.nodes{f}.output.outputStrategy{r}{3}{par}{1}.index) = 1.0;
112 P{aux_classes{r, par}, aux_classes{s, par}}(joinIdx, fj_auxiliary_delays{joinIdx}) = 1.0;
113 P{aux_classes{r, par}, aux_classes{s, par}}(fj_auxiliary_delays{joinIdx}, :) = 0.0;
114 P{aux_classes{r, par}, aux_classes{s, par}}(fj_auxiliary_delays{joinIdx}, f) = 1.0;
116 % Route the original classes straight to the join to avoid the interference with the artificial classes
118 P{r,s}(f, joinIdx) = 1.0;
123 nonfjmodel.relink(P);
124 % snPrintRoutingMatrix(nonfjmodel.getStruct)
125 % nonfjmodel.jsimgView