1function [QN, UN, RN, TN, xvec_it, QNt, UNt, TNt, xvec_t, t, iters, runtime] = solver_fluid_closing(sn, options)
2% [QN, UN, RN, TN, XVEC, QNt, UNt, TNt, XVEC_T, T, ITERS, RUNTIME] = SOLVER_FLUID_CLOSING(QN, OPTIONS)
4% Copyright (c) 2012-2026, Imperial College London
8% inner iteration of fluid analysis
9[Q,xvec_it,QNt,UNt,xvec_t,t,iters,runtime] = solver_fluid(sn, options);
11%% assumes the existence of a delay node through which all
classes pass
12delayNodes = zeros(1,sn.nstations);
13delayrefstat = zeros(1,sn.nstations); % delay
nodes that are also reference
nodes
15 if sn.sched(i) == SchedStrategy.INF
20 if sn.refstat(k) > 0 % artificial
classes do not need to have a reference node
21 delayrefstat(sn.refstat(k)) = 1;
25%% simpler version: no chain analysis
33%Qlength
for all stations,
classes,
36%% Tput
for all
classes in each station
37Rlittle = zeros(sn.nstations,sn.nclasses); % throughput of every
class at each station
38TN = zeros(sn.nstations,sn.nclasses); % throughput of every
class at each station
45Xservice = cell(M,K); %throughput per
class, station and phase
49 idx = sum(sum(phases(1:i-1,:))) + sum( phases(i,1:k-1) );
50 Xservice{i,k} = zeros(phases(i,k),1);
52 TN(i,k) = TN(i,k) + QN(idx+f)*Lambda{i}{k}(f)*phi{i}{k}(f);
53 TNt{i,k} = TNt{i,k} + QNt{i,k}*Lambda{i}{k}(f)*phi{i}{k}(f);
54 Xservice{i,k}(f) = QN(idx+f)*Lambda{i}{k}(f);
58 xi = sum(Q(i,:)); %number of jobs in the station
61 xi_t = xi_t + QNt{i,r};
64 if xi>0 || sn.sched(i)==SchedStrategy.EXT % step in to compute source tput in all cases
66 case SchedStrategy.FCFS
67 wni = GlobalConstants.FineTol;
68 w = zeros(1,sn.nclasses);
70 idx = sum(sum(phases(1:i-1,:))) + sum( phases(i,1:k-1) );
71 wi(k) = map_mean(sn.proc{i}{k});
72 wni = wni + wi(k)*sum(QN((idx+1):(idx+phases(i,k))));
76 wni_t = wni_t + wi(r)* QNt{i,r};
82 idx = sum(sum(phases(1:i-1,:))) + sum( phases(i,1:k-1) );
83 Xservice{i,k} = zeros(phases(i,k),1);
86 case SchedStrategy.EXT
88 TN(i,k) = TN(i,k) + (1-sum(QN(idx+(2:phases(i,k)))))*Lambda{i}{k}(f)*phi{i}{k}(f);
89 TNt{i,k} = TNt{i,k} + (1-sum(xvec_t(:,idx+(2:phases(i,k))),2))*Lambda{i}{k}(f)*phi{i}{k}(f);
90 Xservice{i,k}(f) = (1-sum(QN(idx+(2:phases(i,k)))))*Lambda{i}{k}(f);
92 TN(i,k) = TN(i,k) + QN(idx+f)*Lambda{i}{k}(f)*phi{i}{k}(f);
93 TNt{i,k} = TNt{i,k} + xvec_t(:,idx+f)*Lambda{i}{k}(f)*phi{i}{k}(f);
94 Xservice{i,k}(f) = QN(idx+f)*Lambda{i}{k}(f);
96 case {SchedStrategy.INF, SchedStrategy.PS}
97 TN(i,k) = TN(i,k) + QN(idx+f)*Lambda{i}{k}(f)*phi{i}{k}(f)/xi*min(xi,sn.nservers(i));
98 TNt{i,k} = TNt{i,k} + xvec_t(:,idx+f)*Lambda{i}{k}(f)*phi{i}{k}(f)./xi_t.*min(xi_t,sn.nservers(i));
99 Xservice{i,k}(f) = QN(idx+f)*Lambda{i}{k}(f)/xi*min(xi,sn.nservers(i));
100 case SchedStrategy.DPS
101 w = sn.schedparam(i,:);
102 wxi = w*Q(i,:)
'; %number of jobs in the station
103 wxi_t = w(1)*QNt{i,1};
105 wxi_t = wxi_t + w(r)*QNt{i,r};
107 TN(i,k) = TN(i,k) + QN(idx+f)*Lambda{i}{k}(f)*phi{i}{k}(f)*w(k)/wxi*min(xi,sn.nservers(i));
108 TNt{i,k} = TNt{i,k} + xvec_t(:,idx+f)*Lambda{i}{k}(f)*phi{i}{k}(f)*w(k)./wxi_t.*min(xi_t,sn.nservers(i));
109 Xservice{i,k}(f) = QN(idx+f)*Lambda{i}{k}(f)*w(k)/wxi*min(xi,sn.nservers(i));
110 case {SchedStrategy.FCFS, SchedStrategy.SIRO}
111 switch options.method
112 case {'default
','closing
','pnorm
','softmin
'}
113 TN(i,k) = TN(i,k) + QN(idx+f)*Lambda{i}{k}(f)*phi{i}{k}(f)/xi*min(xi,sn.nservers(i));
114 TNt{i,k} = TNt{i,k} + xvec_t(:,idx+f)*Lambda{i}{k}(f)*phi{i}{k}(f)./xi_t.*min(xi_t,sn.nservers(i));
115 Xservice{i,k}(f) = QN(idx+f)*Lambda{i}{k}(f)/xi*min(xi,sn.nservers(i));
117 TN(i,k) = TN(i,k) + QN(idx+f)*Lambda{i}{k}(f)*phi{i}{k}(f)*wi(k)/wni*min(xi,sn.nservers(i));
118 TNt{i,k} = TNt{i,k} + xvec_t(:,idx+f)*Lambda{i}{k}(f)*phi{i}{k}(f)./xi_t.*min(xi_t,sn.nservers(i));
119 Xservice{i,k}(f) = QN(idx+f)*Lambda{i}{k}(f)*wi(k)/wni*min(xi,sn.nservers(i));
121 %Tfull(i,k) = Tfull(i,k) + Qfull(idx+f)*Lambda{i}{k}(f)*phi{i}{k}(f)*min(xi,sn.nservers(i))*wi(k);
122 %Tfull_t{i,k} = Tfull_t{i,k} + ymean_t(:,idx+f)*Lambda{i}{k}(f)*phi{i}{k}(f).*min(xi_t,sn.nservers(i)) * wi(k);
123 %Xservice{i,k}(f) = Qfull(idx+f)*Lambda{i}{k}(f)*min(xi,sn.nservers(i))*wi(k);
125 % Tfull(i,k) = Tfull(i,k) + Qfull(idx+f)*Lambda{i}{k}(f)*phi{i}{k}(f)*min(xi,sn.nservers(i))*wi(k)/wni;
126 % Tfull_t{i,k} = Tfull_t{i,k} + ymean_t(:,idx+f)*Lambda{i}{k}(f)*phi{i}{k}(f).*min(xi_t,sn.nservers(i)) * wi(k)/wni;
127 % Xservice{i,k}(f) = Qfull(idx+f)*Lambda{i}{k}(f)*min(xi,sn.nservers(i))*wi(k)/wni;
130 line_error(mfilename,'Unsupported scheduling policy
');
139 if isscalar(TNt{i,j})
140 TNt{i,j} = TNt{i,j} * ones(size(xvec_t(:,1),1),1);
145origK = size(chains,1);
146% This is approximate, Little's law does not hold in transient
147R = zeros(sn.nstations, sn.nclasses);
148for i = 1:sn.nstations
149 R(i, TN(i,:)>0) = Q(i,TN(i,:)>0) ./ TN(i,TN(i,:)>0);
153newR = zeros(sn.nstations,origK);
155newQ = zeros(sn.nstations,origK);
156% determine eventual probability of visiting each station in each
class (expected number of
visits)
157% weight response time of each original
class with the expected number of
158%
visits to each station in each associated artificial
class
159idxNR = reshape(1:sn.nstations*sn.nclasses, sn.nclasses, sn.nstations); %indices of non-delay (non-reference)
nodes
160idxNR = reshape(idxNR(:,delayrefstat==0),1,[]);
161rtTrans = sn.rt(idxNR,idxNR); %transient transition matrix
for non-reference
nodes
162eventualVisit = pinv( eye(size(rtTrans)) - rtTrans ); % pinv handles better self-looping customers setting the relevant entries to zero instead than infinity
163idxOrigClasses = zeros(origK,1);
166 idxOrigClasses(k) = find(chains(k,:),1);
167 refNode = refstat(idxOrigClasses(k));
169 eventualVisitProb = reshape( sn.rt((refNode-1)*sn.nclasses+k,idxNR)*eventualVisit, sn.nclasses , sn.nstations-sum(delayrefstat>0) )
'; %#ok<MINV> %probability of eventual visit
170 eventualVisitProb = eventualVisitProb(:,chains(k,:)==1);
172 newR(refNode,k) = sum( R(refNode,chains(k,:)==1),2 );
173 newR(delayrefstat==0,k) = sum( R(delayrefstat==0,chains(k,:)==1).*eventualVisitProb, 2 );
174 newR(refNode,chains(k,:)==1) = R(refNode,chains(k,:)==1);
175 newR(delayrefstat==0,chains(k,:)==1) = R(delayrefstat==0,chains(k,:)==1).*eventualVisitProb;
177 %X(k,find(chains(k,:)))= sum(Tfull(refNode,find(chains(k,:))));
178 X(1,k) = sum( TN(refNode,chains(k,:)==1) );
179 newQ(:,k) = sum( Q(:,chains(k,:)==1),2 );
189 idx = Xservice{i,k}>0;
190 UN(i,k) = sum(Xservice{i,k}(idx)./ Lambda{i}{k}(idx));
192 case SchedStrategy.FCFS
193 switch options.method
195 TN(i,k) = sum(Xservice{i,k}(idx));
201UN(delayNodes==0,:) = UN(delayNodes==0,:)./repmat(sn.nservers(delayNodes==0),1,K);