1function [Q,U,R,T,C,X,lG,STeff,it,method] = solver_nc(sn, options)
3M = sn.nstations; %number of stations
5NK = sn.njobs
'; % initial population per class
14% Check for special LCFS + LCFS-PR 2-station network
15lcfsStat = find(sched == SchedStrategy.LCFS);
16lcfsprStat = find(sched == SchedStrategy.LCFSPR);
17if ~isempty(lcfsStat) && ~isempty(lcfsprStat)
18 % Validate LCFS network topology
19 if length(lcfsStat) ~= 1 || length(lcfsprStat) ~= 1
20 line_error(mfilename, 'LCFS NC
requires exactly one LCFS and one LCFS-PR station.
');
24 inchain = sn.inchain{c};
25 Nchain(c) = sum(NK(inchain)); %#ok<FNDSB>
28 line_error(mfilename, 'LCFS NC
requires a closed queueing network.
');
30 % Check for self-loops in routing matrix
32 nclasses = sn.nclasses;
33 for ist = [lcfsStat, lcfsprStat]
35 if rt((ist-1)*nclasses+r, (ist-1)*nclasses+r) > 0
36 line_error(mfilename, 'LCFS NC does not support self-loops at stations.
');
40 % Call specialized LCFS NC solver
41 [Q,U,R,T,C,X,lG] = solver_nc_lcfsqn(sn, options, lcfsStat, lcfsprStat);
46elseif ~isempty(lcfsStat)
47 % LCFS without LCFS-PR is not supported
48 line_error(mfilename, 'LCFS scheduling
requires a paired LCFS-PR station.
');
53 inchain = sn.inchain{c};
54 Nchain(c) = sum(NK(inchain)); %#ok<FNDSB>
56openChains = find(isinf(Nchain));
57closedChains = find(~isinf(Nchain));
64if all(sched~=SchedStrategy.FCFS) options.iter_max=1; end
66while max(abs(1-eta./eta_1)) > options.iter_tol & it < options.iter_max
72 [Lchain,STchain,Vchain,alpha,Nchain] = sn_get_demands_chain(sn);
74 inchain = sn.inchain{c};
75 isOpenChain = any(isinf(sn.njobs(inchain)));
77 % we assume that the visits in L(i,inchain) are equal to 1
78 if isOpenChain && ist == sn.refstat(inchain(1)) % if this is a source ST = 1 / arrival rates
79 lambda(c) = 1 ./ STchain(ist,c);
85 inchain = sn.inchain{c};
87 % we assume that the visits in L(i,inchain) are equal to 1
88 STchain(ist,c) = ST(ist,inchain) * alpha(ist,inchain)';
89 Lchain(ist,c) = Vchain(ist,c) * STchain(ist,c);
94 STchain(~isfinite(STchain))=0;
95 Lchain(~isfinite(Lchain))=0;
102 if isinf(nservers(ist)) % infinite server
103 infServers(end+1) = ist;
105 Z(ist,:) = Lchain(ist,:);
108 if strcmpi(options.method,
'exact') && nservers(ist)>1
109 %options.method =
'default';
111 line_warning(mfilename,sprintf(
'%s does not support exact multiserver yet. Switching to approximate method.\n',
'SolverNC'));
114 % Seidmann
's approximation
115 Lms(ist,:) = Lchain(ist,:) / nservers(ist);
117 Zms(ist,:) = Lchain(ist,:) * (nservers(ist)-1)/nservers(ist);
122 [lG, Xchain, Qchain, method] = pfqn_nc(lambda,Lms,Nchain,sum(Z,1)+sum(Zms,1), options);
124 if sum(Zms,1) > GlobalConstants.FineTol
125 % in this case, we need to use the iterative approximation below
131 [Q,U,R,T,C,X,lG,STeff,it,method] = deal([],[],[],[],[],[],[],[],1,options.method);
139 % an approximate method may report out-of-domain via an empty lG;
140 % return gracefully, do not assign [] into a scalar slot. see
141 % _kb/06-solver-catalog.md (NC section, unknown-method/empty-lG)
142 lGr_r = pfqn_nc(lambda,Lms,oner(Nchain,r),sum(Z,1)+sum(Zms,1), options);
144 [Q,U,R,T,C,X,lG,STeff,it,method] = deal([],[],[],[],[],[],[],[],1,options.method);
148 Xchain(r) = exp(lGr(r) - lG);
151 if isinf(nservers(ist)) % infinite server
152 Qchain(ist,r) = Lchain(ist,r) * Xchain(r);
154 % add replica of station i and move job of class r
156 % this is the most costly operation so we record
158 [lGar_ist_r,~,~,method] = pfqn_nc([lambda,0],[Lms(setdiff(1:size(Lms,1),ist),:),zeros(size(Lms,1)-1,1); Lms(ist,:),1], [oner(Nchain,r),1], [sum(Z,1)+sum(Zms,1),0], options);
159 if isempty(lGar_ist_r)
160 [Q,U,R,T,C,X,lG,STeff,it,method] = deal([],[],[],[],[],[],[],[],1,options.method);
163 lGar(ist,r) = lGar_ist_r;
164 Qchain(ist,r) = Zms(ist,r) * Xchain(r) + Lms(ist,r) * exp(lGar(ist,r) - lG);
168 Qchain(isnan(Qchain))=0;
172 Qchain(ist,r) = lambda(r)*Lchain(ist,r)/(1-lambda(openChains)*Lchain(ist,openChains)'/nservers(ist))*(1+sum(Qchain(ist,closedChains)));
176 % just fill the delay servers
180 if isinf(nservers(ist)) % infinite server
181 Qchain(ist,r) = Lchain(ist,r) * Xchain(r);
190 line_warning(mfilename,
'Normalizing constant computations produced a floating-point range exception. Model is likely too large.\n');
196 Rchain = Qchain ./ repmat(Xchain,M,1) ./ Vchain;
197 Rchain(infServers,:) = Lchain(infServers,:) ./ Vchain(infServers,:);
198 Tchain = repmat(Xchain,M,1) .* Vchain;
200 %Uchain = Tchain .* Lchain;
201 %Cchain = Nchain ./ Xchain - Z;
203 [Q,U,R,T,~,X] = sn_deaggregate_chain_results(sn, Lchain, ST, STchain, Vchain, alpha, [], [], Rchain, Tchain, [], Xchain);
204 STeff = ST;% effective service time at the last iteration
205 [ST,gamma,~,~,~,~,eta] = npfqn_nonexp_approx(options.config.highvar,sn,ST0,V,SCV,T,U,gamma,nservers);
208Q=abs(Q); R=abs(R); X=abs(X); U=abs(U);
209X(~isfinite(X))=0; U(~isfinite(U))=0; Q(~isfinite(Q))=0; R(~isfinite(R))=0;
211% renormalize qlen and tput to correct
for unforeseen population constraint deviations
213 inchain = sn.inchain{c};
214 Nchain(c) = sum(NK(inchain)); %#ok<FNDSB>
215 if isfinite(Nchain(c))
216 q_den = sum(sum(Q(:,inchain)));
218 ratio = Nchain(c)/ q_den;
222 Q(:,inchain) = ratio * Q(:,inchain);
223 X(inchain) = ratio * X(inchain);
224 T(:,inchain) = ratio * T(:,inchain);
225 U(:,inchain) = ratio * U(:,inchain);
226 R(:,inchain) = Q(:,inchain) ./ T(:,inchain);