LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
renv_rotterdam_blending.m
1% renv_rotterdam_blending - Exact reproduction of the fyp26 SOQN blending result
2%
3% Reproduces the 24-environment exponential blending accuracy result of the
4% fyp26 Rotterdam container-terminal study (Dhingra 2018), using the same
5% level-dependent QBD blocks and the cyclic resolvent blending that the
6% SolverENV state-vector analyzer uses internally.
7%
8% Per-environment model (PoissonSOQNSpec): a semi-open SOQN with N tokens, a
9% Poisson arrival rate lambda_h (hour h of the day), and two flow-equivalent
10% server (FES) subnetworks in tandem (upstream S1, downstream S2), each a
11% load-dependent station whose throughput curve mu1(n)/mu2(n) is calibrated by
12% exact MVA on the underlying closed subnetwork. The LD-QBD state is (n,k):
13% level n = jobs upstream of S2 (S1 + external backlog), phase k = S2 occupancy.
14%
15% The 24 hourly environments are visited cyclically with exponential sojourns
16% (mean = hour duration). Each visit applies the resolvent s*pi*(sI-Q)^{-1}
17% (exit = time-average, memoryless), entry vectors are chained to an L1 fixed
18% point, and the blend weights stages by their time fraction. Mean external
19% wait W and external queue length Qex are compared to the Dhingra simulation
20% (Table 13).
21%
22% Copyright (c) 2012-2026, Imperial College London
23% All rights reserved.
24
25clear; warning off;
26
27%% Dhingra 24-hour schedule and simulation ground truth (Table 13)
28dailyRateHr = [ 6, 30, 40, 62, 76, 79, 119, 164, 152, 130, 79, 70, ...
29 57, 57, 113, 130, 162, 202, 148, 118, 92, 62, 36, 8];
30dailyDurHr = [0.51,0.84,0.76,0.98,0.67,2.33,1.80,0.62,0.36,1.30,1.02,0.98, ...
31 0.86,1.56,1.30,1.57,0.34,0.22,0.47,1.33,1.56,0.93,0.71,1.11];
32lambda = dailyRateHr * 0.5 / 60; % arrivals per minute (buildLambdasMin)
33durMin = dailyDurHr * 60; % hour durations in minutes
34fracW = durMin / sum(durMin); % time-fraction blend weights f_i
35
36simN = 24:34;
37simW = [154.0661,118.3936,99.962,88.2379,80.9272,75.0887,70.745,67.4326,64.9719,62.7913,61.2534];
38simQex = [89.8508,63.3970,49.3650,40.8783,35.3546,30.6543,27.4111,24.7218,22.3622,20.3893,18.9252];
39
40tailFactor = 15; % M_trunc = N*(1+tailFactor) (ACC_TAIL)
41params = struct('numEntryServers',6,'numStacks',29,'entryServiceTime',6.0, ...
42 'travelToStackTime',5.6,'stackServiceTime',6.0,'numExitServers',6, ...
43 'travelToExitTime',5.6,'exitServiceTime',6.0);
44
45%% Reproduce the blending for a few token counts and compare to simulation
46Nlist = [24 28 34];
47fprintf('Rotterdam SOQN exponential blending vs Dhingra simulation (tailFactor=%d)\n', tailFactor);
48fprintf('%4s | %10s %10s %7s | %10s %10s %7s\n', 'N', 'W_blend','W_sim','err%','Qex_bl','Qex_sim','err%');
49for N = Nlist
50 mu1 = fesCurve(N, params, 'up');
51 mu2 = fesCurve(N, params, 'down');
52 [Wbl, Qexbl] = blendSOQN(N, lambda, durMin, fracW, mu1, mu2, tailFactor);
53 j = find(simN == N);
54 fprintf('%4d | %10.4f %10.4f %6.2f | %10.4f %10.4f %6.2f\n', ...
55 N, Wbl, simW(j), 100*abs(Wbl-simW(j))/simW(j), ...
56 Qexbl, simQex(j), 100*abs(Qexbl-simQex(j))/simQex(j));
57end
58
59%% ---- local functions ------------------------------------------------------
60function mu = fesCurve(N, p, which)
61% Load-dependent FES throughput curve mu(l), l=0..N, by exact MVA on the
62% closed subnetwork at each population l (Norton flow-equivalent).
63mu = zeros(1, N+1);
64for l = 1:N
65 if strcmp(which, 'up')
66 m = Network('S1');
67 eg = Queue(m,'EntryGates',SchedStrategy.FCFS); eg.setNumberOfServers(p.numEntryServers);
68 tv = Delay(m,'TravelToStack');
69 st = cell(1,p.numStacks);
70 for i=1:p.numStacks, st{i}=Queue(m,sprintf('Stack%d',i),SchedStrategy.FCFS); st{i}.setNumberOfServers(1); end
71 cls = ClosedClass(m,'Trucks',l,eg);
72 eg.setService(cls,Exp(1/p.entryServiceTime));
73 tv.setService(cls,Exp(1/p.travelToStackTime));
74 for i=1:p.numStacks, st{i}.setService(cls,Exp(1/p.stackServiceTime)); end
75 ns = 2 + p.numStacks; P = m.initRoutingMatrix();
76 P{1}(1,2) = 1; % EntryGates -> TravelToStack
77 P{1}(2,3:ns) = 1/p.numStacks; % TravelToStack -> Stack_i
78 P{1}(3:ns,1) = 1; % Stack_i -> EntryGates
79 m.link(P);
80 T = SolverMVA(m,'method','exact','verbose',false).getAvgTable;
81 mu(l+1) = T.Tput(1); % throughput at EntryGates
82 else
83 m = Network('S2');
84 tv = Delay(m,'TravelToExit');
85 xg = Queue(m,'ExitGates',SchedStrategy.FCFS); xg.setNumberOfServers(p.numExitServers);
86 cls = ClosedClass(m,'Trucks',l,tv);
87 tv.setService(cls,Exp(1/p.travelToExitTime));
88 xg.setService(cls,Exp(1/p.exitServiceTime));
89 P = m.initRoutingMatrix(); P{1}(1,2)=1; P{1}(2,1)=1; m.link(P);
90 T = SolverMVA(m,'method','exact','verbose',false).getAvgTable;
91 mu(l+1) = T.Tput(2); % throughput at ExitGates
92 end
93end
94end
95
96function Q = soqnGenerator(N, lam, mu1, mu2, tailFactor)
97% Flat generator of the Poisson SOQN LD-QBD. State (n,k), flat index n*M+k+1.
98% level n=0..Mtr (S1+backlog), phase k=0..N (S2 occupancy).
99M = N + 1; Mtr = N + tailFactor*N; dim = (Mtr+1)*M;
100ri = zeros(0,1); ci = zeros(0,1); vi = zeros(0,1);
101for n = 0:Mtr
102 for k = 0:N
103 row = n*M + k + 1;
104 j = min(n, N-k);
105 m1 = mu1(j+1); % mu1.at(min(n,N-k))
106 m2 = mu2(k+1); % mu2.at(k)
107 atTop = (n == Mtr);
108 lamEff = lam * (~atTop); % no arrivals leave the truncated chain at top
109 % diagonal
110 ri(end+1,1)=row; ci(end+1,1)=row; vi(end+1,1)=-(lamEff + m1 + m2); %#ok<AGROW>
111 % S2 completion within level: (n,k)->(n,k-1) at mu2(k)
112 if k>=1
113 ri(end+1,1)=row; ci(end+1,1)=n*M+(k-1)+1; vi(end+1,1)=m2; %#ok<AGROW>
114 end
115 % arrival up: (n,k)->(n+1,k) at lam
116 if n<Mtr
117 ri(end+1,1)=row; ci(end+1,1)=(n+1)*M+k+1; vi(end+1,1)=lam; %#ok<AGROW>
118 end
119 % S1 completion down: (n,k)->(n-1,k+1) at mu1(min(n,N-k))
120 if n>=1 && k<=N-1
121 ri(end+1,1)=row; ci(end+1,1)=(n-1)*M+(k+1)+1; vi(end+1,1)=m1; %#ok<AGROW>
122 end
123 end
124end
125Q = sparse(ri, ci, vi, dim, dim);
126end
127
128function [W, Qex] = blendSOQN(N, lambda, durMin, fracW, mu1, mu2, tailFactor)
129% Cyclic exponential blending over the 24 hourly SOQN environments, via the
130% resolvent s*pi*(sI-Q)^{-1}, to an L1 fixed point; then blend and extract W,Qex.
131K = numel(lambda);
132M = N + 1; Mtr = N + tailFactor*N; dim = (Mtr+1)*M;
133Q = cell(1,K);
134for h = 1:K, Q{h} = soqnGenerator(N, lambda(h), mu1, mu2, tailFactor); end
135
136piEnter = cell(1,K);
137pi0 = sparse(1, dim); pi0(1) = 1; % empty system
138piEnter{1} = pi0;
139for h = 2:K, piEnter{h} = pi0; end
140
141Ssp = speye(dim);
142for iter = 1:200
143 prev = piEnter;
144 for h = 1:K
145 s = 1/durMin(h);
146 piEnter{mod(h,K)+1} = s * (piEnter{h} / (s*Ssp - Q{h})); % resolvent = exit
147 end
148 l1 = 0;
149 for h = 1:K, l1 = max(l1, sum(abs(piEnter{h}-prev{h}))); end
150 if l1 < 1e-10, break; end
151end
152
153% Blend (exp sojourn: exit = time-average), weighted by time fraction
154piAvg = sparse(1, dim);
155for h = 1:K
156 s = 1/durMin(h);
157 piEx = s * (piEnter{h} / (s*Ssp - Q{h}));
158 piAvg = piAvg + fracW(h) * piEx;
159end
160piAvg = full(piAvg); piAvg(piAvg<0)=0; piAvg = piAvg/sum(piAvg);
161
162% Metrics (extractMetricsPoissonSOQN)
163QLex=0; QL1=0; QL2=0; tput=0;
164for n = 0:Mtr
165 for k = 0:N
166 p = piAvg(n*M+k+1);
167 if p==0, continue; end
168 extQ = max(0, n+k-N); s1 = min(n, N-k);
169 QLex = QLex + extQ*p; QL1 = QL1 + s1*p; QL2 = QL2 + k*p;
170 if k>=1, tput = tput + mu2(k+1)*p; end
171 end
172end
173Qex = QLex;
174W = (QLex + QL1 + QL2) / tput; % external wait + internal time (Little)
175end