1function RD = getCdfRespT(self, R)
4% Empirical response time CDF, measured on the simulated sample path.
6% A simulator must report what it observed: the base NetworkSolver
7% implementation fabricates an exponential law with the right mean, which
is
8% an analytical fallback and says nothing about the tail. The LDES engine
9% records every per-job response time (Solver_ssj.responseTimeSamples ->
10% LDESResult.respTimeSamples -> the JSON
"transient.respTimeSamples" block,
11% exported under --trajectory), so the CDF here
is the ecdf of those samples.
13% RD
is an (nstations x nclasses) cell of [F(t), t] matrices, the convention
14% every other getCdfRespT follows. A (station,
class) pair with no observation
15%
is left empty rather than filled with a guess.
17% Copyright (c) 2012-2026, Imperial College London
21RD = cell(sn.nstations, sn.nclasses);
22if GlobalConstants.DummyMode
26 R = self.getAvgRespTHandles; %
#ok<NASGU>
30% Ask the engine
for the per-job response time samples. They are a
31% distributional output, not a trajectory, so they have their own CLI flag and
32% their own top-level JSON block; the flag also forces the jar runner, since
33% the AOT native binary predates it (see solveCli).
34data = self.solveCli(self.getOptions, {
'--respt-samples'});
35samplesCell = ldesResptSamples(data, sn.nstations, sn.nclasses);
36if isempty(samplesCell)
37 line_error(mfilename, [
'The LDES run returned no response time samples, so an empirical CDF ' ...
38 'cannot be built. Increase options.samples, or use getPerctRespT(...,''forktail'') ' ...
39 'for the analytical fork-join tail.']);
44 if i <= size(samplesCell,1) && r <= size(samplesCell,2)
45 samples = samplesCell{i, r};
48 F = (1:numel(x))
' / numel(x);
49 % collapse repeated observations, keeping the largest CDF value
50 [x, lastIdx] = unique(x, 'last
');
51 RD{i, r} = [F(lastIdx), x];
58self.setDistribResults(RD, runtime);
61function out = ldesResptSamples(data, nstations, nclasses)
62% OUT = LDESRESPTSAMPLES(DATA, NSTATIONS, NCLASSES)
63% Normalize the engine's respTimeSamples block into an (nstations x nclasses)
64% cell of
column vectors. jsondecode yields a cell of cells when the per-
class
65% sample counts differ and a numeric array when they happen to match, so both
66% shapes have to be accepted.
68if ~isstruct(data) || ~isfield(data,
'respTimeSamples') || isempty(data.respTimeSamples)
71rts = data.respTimeSamples;
73 rts = num2cell(rts, [2 3]);
75out = cell(nstations, nclasses);
76for i = 1:min(numel(rts), nstations)
77 stationEntry = rts{i};
78 if ~iscell(stationEntry)
79 stationEntry = num2cell(stationEntry, 2);
81 for r = 1:min(numel(stationEntry), nclasses)
84 out{i, r} = double(v(:));