@@ -59,9 +59,11 @@ There are several IDs with different meanings:
5959- ` test_id ` : identifies one fixture, for example ` rf101_basics ` . It ties
6060 together the manifest entry and the files under ` fixtures/rf101_basics/ ` .
6161- check ` id ` : identifies one check inside ` expected.json ` , for example
62- ` static_integrity ` , ` structure_import ` , or ` twice_delta_nll_scan ` .
62+ ` static_integrity ` , ` structure_import ` , ` twice_delta_nll_scan ` , ` pdf_scan ` , or
63+ ` function_scan ` .
6364- check ` kind ` : tells the runner how to execute a check. The current kinds are
64- ` static_integrity ` , ` structure_import ` , and ` twice_delta_nll_scan ` .
65+ ` static_integrity ` , ` structure_import ` , ` twice_delta_nll_scan ` , ` pdf_scan ` , and
66+ ` function_scan ` .
6567- schema ` $id ` : JSON Schema identifier only; it is not a fixture or check ID.
6668
6769Runner output uses ` test_id::check_id ` , for example:
@@ -126,15 +128,32 @@ from the original tutorial.
126128 "kind" : " twice_delta_nll_scan" ,
127129 "target" : { "pdf" : " gauss" , "data" : " gaussData" },
128130 "reference_point" : { "mean" : 1.0 , "sigma" : 3.0 , "x" : 0.0 },
129- "scan_parameter " : " mean" ,
130- "scan_points" : [-1.0 , 0.0 , 1.0 , 2.0 , 3.0 ],
131+ "scan_parameters " : [ " mean" ] ,
132+ "scan_points" : [[ -1.0 ], [ 0.0 ], [ 1.0 ], [ 2.0 ], [ 3.0 ] ],
131133 "expected" : [888.0456117195517 , 224.26202740723056 , 0.0 , 213.06114567253644 , 856.1426350606562 ],
132134 "tolerance" : { "atol" : 1e-7 , "rtol" : 1e-8 }
133135 }
134136 ]
135137}
136138```
137139
140+ ` scan_parameters ` is a list and each entry of ` scan_points ` is a list of the same length, so a scan can vary several parameters at once.
141+
142+ A ` pdf_scan ` check has the same shape but needs no dataset, so it also works on an ` hs3.json ` with no ` data ` section at all:
143+
144+ ``` json
145+ {
146+ "id" : " pdf_scan" ,
147+ "kind" : " pdf_scan" ,
148+ "target" : { "pdf" : " gauss" , "observables" : [" x" ] },
149+ "reference_point" : { "mean" : 0.0 , "sigma" : 1.0 , "x" : 0.0 },
150+ "scan_parameters" : [" x" ],
151+ "scan_points" : [[-2.0 ], [-1.0 ], [0.0 ], [1.0 ], [2.0 ]],
152+ "expected" : [0.05399096651318806 , 0.24197072451914337 , 0.3989422804014327 , 0.24197072451914337 , 0.05399096651318806 ],
153+ "tolerance" : { "atol" : 1e-12 , "rtol" : 1e-8 }
154+ }
155+ ```
156+
138157## Check Types
139158
140159` static_integrity ` parses ` hs3.json ` as JSON. This catches malformed files
@@ -156,6 +175,29 @@ means loading the file into a `RooWorkspace` using `RooJSONFactoryWSTool`.
156175The suite prefers ` 2DeltaNLL ` rather than raw NLL because raw NLL can include
157176backend-dependent constants or offsets.
158177
178+ ` pdf_scan ` and ` function_scan ` are the quantitative checks for models that carry no data.
179+ Each:
180+
181+ 1 . Imports the HS3 model.
182+ 2 . Finds the target PDF or function.
183+ 3 . At each scan point, re-applies the ` reference_point ` and then overwrites the
184+ scanned names.
185+ 4 . Evaluates the target.
186+ 5 . Compares pointwise with the same rule as ` twice_delta_nll_scan ` .
187+
188+ They differ only in the target. ` pdf_scan ` takes
189+ ` { "pdf": ..., "observables": [...] } ` and evaluates a probability density
190+ ** normalised over the listed observables** . ` function_scan ` takes
191+ ` { "function": ... } ` and evaluates a plain function value.
192+
193+ The ` observables ` list is required for ` pdf_scan ` for the same reason the suite
194+ prefers ` 2DeltaNLL ` over raw NLL: an unnormalised pdf value carries an arbitrary
195+ backend-dependent constant. Declaring the observables in the fixture makes the normalisation domain
196+ part of the frozen contract rather than an implicit backend convention.
197+
198+ ` function_scan ` needs no normalisation set: a ` RooAbsReal ` has no normalisation to
199+ apply, so its raw value is already comparable across backends.
200+
159201## Backend Imports
160202
161203The word "import" in this suite usually means backend import of an HS3 model,
0 commit comments