LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
runTransientJson.m
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.
6%
7% RES has fields:
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.
18
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
22% in transient mode.
23S = options.samples;
24if nargin >= 2 && ~isempty(numEvents) && numEvents > 0
25 S = numEvents;
26end
27extraFlags = {'--timespan', sprintf('0,%.10g', S), '--trajectory'};
28
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.
32
33data = self.solveCli(options, extraFlags);
34
35res = struct('t', [], 'QNt', {{}}, 'respTimeSamples', {{}});
36if ~isstruct(data) || ~isfield(data, 'transient') || isempty(data.transient)
37 return;
38end
39tran = data.transient;
40if isfield(tran, 't') && ~isempty(tran.t)
41 tvec = ldesJson2mat(tran.t, [], []);
42 res.t = tvec(:);
43end
44if isfield(tran, 'respTimeSamples') && ~isempty(tran.respTimeSamples)
45 sn = self.model.getStruct;
46 res.respTimeSamples = cell(sn.nstations, sn.nclasses);
47 rts = tran.respTimeSamples;
48 if ~iscell(rts)
49 rts = num2cell(rts, [2 3]);
50 end
51 for i = 1:min(numel(rts), sn.nstations)
52 stationEntry = rts{i};
53 if ~iscell(stationEntry)
54 stationEntry = num2cell(stationEntry, 2);
55 end
56 for r = 1:min(numel(stationEntry), sn.nclasses)
57 v = stationEntry{r};
58 if ~isempty(v)
59 res.respTimeSamples{i, r} = double(v(:));
60 end
61 end
62 end
63end
64if isfield(tran, 'QNt') && ~isempty(tran.QNt)
65 sn = self.model.getStruct;
66 Nouter = sn.nstateful;
67 R = sn.nclasses;
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);
72 for i = 1:Nouter
73 classCells = cell(1, R);
74 for r = 1:R
75 classCells{r} = grid{i, r};
76 end
77 res.QNt{i} = classCells;
78 end
79end
80end