1function ok = refreshServicesFromBase(nonfjmodel, prov)
2% OK = REFRESHSERVICESFROMBASE(NONFJMODEL, PROV)
4% Re-feed a transformed model produced by ModelAdapter.mmt from
the current
5% service parameters of
the base model it was derived from, so that
the
6% transformation can be reused across
the iterations of an outer fixed point
7% instead of rebuilt. SolverLN re-solves each layer once per iteration and only
8%
the rates change between iterations;
the fork topology
the transformation
9% encodes does not. Rebuilding costs a full model.copy() every time.
11% PROV
is the provenance
struct returned by ModelAdapter.mmt. Three kinds of
12% slot, and all three must be handled differently:
13% prov.serviceSrc base-derived -> re-read from
the base model
14% prov.immediateSlots transformation-owned -> RESET to Immediate. The fork
15% loop in SolverMVA.runAnalyzer overwrites every join with
16% its current synchronisation delay on each pass, so a
17% reused model still holds
the previous outer iteration
's
18% converged value. Leaving these alone silently warm-starts
19% the fork loop and shifts the results.
20% prov.auxArrival owned by the forkLambda fixed point -> reset to what a
21% cold mmt call would have set.
22% Reading a transformation-owned slot from the base model is the converse error
23% and corrupts the transform outright.
25% Returns false when the provenance cannot be applied, in which case the caller
26% must fall back to a cold ModelAdapter.mmt.
29if isempty(prov) || ~isstruct(prov) || ~isfield(prov,'baseModel
') || isempty(prov.baseModel)
33nclasses = length(base.classes);
35% Base-derived service slots.
36for k = 1:size(prov.serviceSrc,1)
37 i = prov.serviceSrc(k,1); c = prov.serviceSrc(k,2); r = prov.serviceSrc(k,3);
38 if i > length(nonfjmodel.nodes) || c > length(nonfjmodel.classes) || r > nclasses
41 svc = base.nodes{i}.getService(base.classes{r});
45 % copy() mirrors the cold path, which hands every slot its own distribution
46 % object rather than aliasing one across classes.
47 nonfjmodel.nodes{i}.setService(nonfjmodel.classes{c}, svc.copy());
50% Transformation-owned initial conditions.
51for k = 1:size(prov.immediateSlots,1)
52 i = prov.immediateSlots(k,1); c = prov.immediateSlots(k,2);
53 if i > length(nonfjmodel.nodes) || c > length(nonfjmodel.classes)
56 nonfjmodel.nodes{i}.setService(nonfjmodel.classes{c}, Immediate());
59% Auxiliary-class arrivals, as a cold call would leave them.
60if ~isempty(prov.auxArrival)
61 source = nonfjmodel.getSource;
65 for k = 1:size(prov.auxArrival,1)
66 c = prov.auxArrival(k,1); r = prov.auxArrival(k,2); disableAux = prov.auxArrival(k,3);
67 if c > length(nonfjmodel.classes)
71 source.setArrival(nonfjmodel.classes{c}, Disabled.getInstance);
72 elseif r <= length(prov.forkLambdaInit)
73 source.setArrival(nonfjmodel.classes{c}, Exp(prov.forkLambdaInit(r)));