1+ # ─────────────────────────────────────────────────────────────────────────────
2+ # Deterministic collapse (Monte Carlo on deterministic ranges only)
3+ # Policy: transform (valuespec, pctspec) → (merged_valuespec, nothing)
4+ # ─────────────────────────────────────────────────────────────────────────────
5+
6+ # Percent helpers
7+ @inline _pct (u) = float (u) / 100
8+ @inline _expand_nom (nom:: Number , u:: Number ) = (nom* (1 - _pct (u)), nom* (1 + _pct (u)))
9+ @inline _expand_bounds (lo:: Number , hi:: Number , u1:: Number , u2:: Number ) =
10+ (lo* (1 - _pct (u1)), hi* (1 + _pct (u2)))
11+
12+ # Deterministic collapse with pct interpreted as percent (not absolute)
13+ @inline function _det_pair (spec, pct)
14+ pct === nothing && return (spec, nothing )
15+
16+ # A) spec = (lo,hi,N1), pct = (u1,u2,N2) → ((lo(1-u1%), hi(1+u2%), N1*N2), nothing)
17+ if (spec isa Tuple && length (spec)== 3 && all (x-> x isa Number, spec)) &&
18+ (pct isa Tuple && length (pct) == 3 && all (x-> x isa Number, pct))
19+ lo, hi, N1 = float (spec[1 ]), float (spec[2 ]), Int (spec[3 ])
20+ u1, u2, N2 = float (pct[1 ]), float (pct[2 ]), Int (pct[3 ])
21+ lo_det, hi_det = _expand_bounds (lo, hi, u1, u2)
22+ return ((lo_det, hi_det, N1 * N2), nothing )
23+ end
24+
25+ # B) spec = (lo,hi,N1), pct = u → ((lo(1-u%), hi(1+u%), N1), nothing)
26+ if (spec isa Tuple && length (spec)== 3 && all (x-> x isa Number, spec)) && (pct isa Number)
27+ lo, hi, N1 = float (spec[1 ]), float (spec[2 ]), Int (spec[3 ])
28+ u = float (pct)
29+ lo_det, hi_det = _expand_bounds (lo, hi, u, u)
30+ return ((lo_det, hi_det, N1), nothing )
31+ end
32+
33+ # C) spec = nom, pct = (u1,u2,N2) → ((nom(1-u1%), nom(1+u2%), N2), nothing)
34+ if (spec isa Number) && (pct isa Tuple && length (pct)== 3 && all (x-> x isa Number, pct))
35+ nom = float (spec)
36+ u1, u2, N2 = float (pct[1 ]), float (pct[2 ]), Int (pct[3 ])
37+ lo_det, hi_det = _expand_nom (nom, u1);
38+ _, hi2 = _expand_nom (nom, u2)
39+ # reuse lo_det from u1 and hi from u2 to respect asymmetric pct
40+ return ((lo_det, hi2, N2), nothing )
41+ end
42+
43+ # D) spec = nom, pct = u → ((nom(1-u%), nom(1+u%), 2), nothing)
44+ if (spec isa Number) && (pct isa Number)
45+ nom = float (spec);
46+ u = float (pct)
47+ lo_det, hi_det = _expand_nom (nom, u)
48+ return ((lo_det, hi_det, 2 ), nothing )
49+ end
50+
51+ # E) spec === nothing — no sensible base for %; safest is to drop pct
52+ return (spec, nothing )
53+ end
54+
55+ # Normalizer: accept a field already in (spec,pct) or as a scalar → return (spec’, nothing)
56+ @inline _det_field (x) = (x isa Tuple && length (x)== 2 ) ? _det_pair (x[1 ], x[2 ]) : (x, nothing )
57+
58+ # ---- MaterialSpec ----
59+ function determinize (ms:: MaterialSpec )
60+ MaterialSpec (
61+ rho = _det_field (ms. rho),
62+ eps_r = _det_field (ms. eps_r),
63+ mu_r = _det_field (ms. mu_r),
64+ T0 = _det_field (ms. T0),
65+ alpha = _det_field (ms. alpha),
66+ )
67+ end
68+
69+ # ---- PartSpec (dim, args, material) ----
70+ function determinize (ps:: PartSpec )
71+ dim_det = _det_field (ps. dim)
72+ # each arg can be scalar or (spec,pct)
73+ args_det = map (a -> (a isa Tuple && length (a)== 2 ) ? _det_field (a) : a, ps. args) |> Tuple
74+ mat_det = determinize (ps. material)
75+ return PartSpec (
76+ ps. component,
77+ ps. part_type,
78+ ps. n_layers;
79+ dim = dim_det,
80+ args = args_det,
81+ material = mat_det,
82+ )
83+ end
84+
85+ # ---- CableBuilderSpec (vector/nested parts) ----
86+ function determinize (cbs:: CableBuilderSpec )
87+ parts_det = PartSpec[determinize (p) for p in cbs. parts]
88+ return CableBuilderSpec (cbs. cable_id, parts_det, cbs. nominal)
89+ end
90+
91+ # ─────────────────────────────────────────────────────────────────────────────
92+ # Deterministic collapse for SystemBuilderSpec (non-materializing)
93+ # ─────────────────────────────────────────────────────────────────────────────
94+
95+ @inline _det_axis (a) = (a isa Tuple && length (a)== 2 ) ? _det_pair (a[1 ], a[2 ]) : a
96+ # determinize EarthSpec
97+ function determinize (e:: EarthSpec )
98+ EarthSpec (
99+ rho = _det_field (e. rho),
100+ eps_r = _det_field (e. eps_r),
101+ mu_r = _det_field (e. mu_r),
102+ t = _det_field (e. t),
103+ )
104+ end
105+
106+ # determinize _Pos (keep anchors; just collapse dx/dy specs)
107+ function determinize (p:: _Pos )
108+ _Pos (p. x0, p. y0, _det_axis (p. dx), _det_axis (p. dy), p. conn)
109+ end
110+
111+ # determinize SystemBuilderSpec
112+ function determinize (s:: SystemBuilderSpec )
113+ SystemBuilderSpec (
114+ s. system_id,
115+ determinize (s. builder),
116+ [determinize (p) for p in s. positions];
117+ length = _det_field (s. length),
118+ temperature = _det_field (s. temperature),
119+ earth = determinize (s. earth),
120+ f = s. frequencies,
121+ )
122+ end
0 commit comments