1function res = runTransientJson(self, numEvents)
2% RES = RUNTRANSIENTJSON(NUMEVENTS)
3% Run a fully JSON-mediated transient LDES simulation over the horizon
4% [0, samples] (or [0, numEvents] when numEvents>0) with trajectory export,
5% and
return the parsed trajectory. Mirrors the Python-native _run_transient.
8% .t - (nTimePoints x 1) time vector, or [] if none
9% .QNt - 1 x Nouter cell; RES.QNt{i}
is a 1 x R cell whose {r} entry
is the
10% (nTimePoints x 2) [value, time] trajectory of
class r at the i-th
11% stateful node, or []
if absent. Nouter matches the engine
's outer
12% transient dimension (stateful-node major, as consumed by sample()).
13% .respTimeSamples - (nstations x nclasses) cell of per-job response time
14% samples recorded by the engine (empty when the run produced none).
15% These are the sample-path observations that getCdfRespT turns into
16% an empirical CDF, so that a simulator reports measured percentiles
17% instead of the exponential approximation of the base class.
19options = self.getOptions;
20% The horizon is passed explicitly via --timespan; options.samples (the
21% steady-state event budget) is left untouched since the engine ignores it
24if nargin >= 2 && ~isempty(numEvents) && numEvents > 0
27extraFlags = {'--timespan
', sprintf('0,%.10g
', S), '--trajectory
'};
29% see _kb/06-solver-catalog.md (Wrappers: LDES ensemble transient needs --replications)
30% --replications and --numthreads are emitted centrally by solveCli for every
31% analysis, steady-state included, so nothing is added here.
33data = self.solveCli(options, extraFlags);
35res = struct('t
', [], 'QNt
', {{}}, 'respTimeSamples
', {{}});
36if ~isstruct(data) || ~isfield(data, 'transient
') || isempty(data.transient)
40if isfield(tran, 't
') && ~isempty(tran.t)
41 tvec = ldesJson2mat(tran.t, [], []);
44if isfield(tran, 'respTimeSamples
') && ~isempty(tran.respTimeSamples)
45 sn = self.model.getStruct;
46 res.respTimeSamples = cell(sn.nstations, sn.nclasses);
47 rts = tran.respTimeSamples;
49 rts = num2cell(rts, [2 3]);
51 for i = 1:min(numel(rts), sn.nstations)
52 stationEntry = rts{i};
53 if ~iscell(stationEntry)
54 stationEntry = num2cell(stationEntry, 2);
56 for r = 1:min(numel(stationEntry), sn.nclasses)
59 res.respTimeSamples{i, r} = double(v(:));
64if isfield(tran, 'QNt
') && ~isempty(tran.QNt)
65 sn = self.model.getStruct;
66 Nouter = sn.nstateful;
68 % Normalize into an Nouter x R cell of [nRows x 2] matrices, then repackage
69 % as a 1 x Nouter cell of 1 x R class-cells (the layout sample() consumes).
70 grid = ldesTrajCell(tran.QNt, Nouter, R);
71 res.QNt = cell(1, Nouter);
73 classCells = cell(1, R);
75 classCells{r} = grid{i, r};
77 res.QNt{i} = classCells;