@@ -223,3 +223,85 @@ def test_beta_values_per_bin(self, result):
223223 betas = result .fit_beta [0 , 0 ]
224224 for i , (_ , beta_true , _ ) in enumerate (self ._GROUP_PARAMS ):
225225 assert_allclose (betas [i ], beta_true , rtol = 0.50 , err_msg = f"bin { i } : beta mismatch" )
226+
227+
228+ # ---------------------------------------------------------------------------
229+ # extension_grid
230+ # ---------------------------------------------------------------------------
231+
232+
233+ class TestKingPSFFitterExtensionGrid :
234+ def test_default_is_point_source (self ):
235+ rng = np .random .default_rng (RNG_SEED )
236+ events = _make_events (500 , np .radians (1.0 ), 2.5 , "aux" , np .zeros (500 ), rng )
237+ fitter = KingPSFFitter (
238+ events , parametrization_bins = {"aux" : [- 1.0 , 1.0 ]}, minimum_counts = 100 , weight_field = None
239+ )
240+ assert_allclose (fitter .extension_grid , [0.0 ])
241+
242+ def test_negative_extension_raises (self ):
243+ rng = np .random .default_rng (RNG_SEED )
244+ events = _make_events (500 , np .radians (1.0 ), 2.5 , "aux" , np .zeros (500 ), rng )
245+ with pytest .raises (ValueError ):
246+ KingPSFFitter (
247+ events ,
248+ parametrization_bins = {"aux" : [- 1.0 , 1.0 ]},
249+ minimum_counts = 100 ,
250+ weight_field = None ,
251+ extension_grid = [- 0.1 , 0.0 ],
252+ )
253+
254+ def test_extension_grid_is_sorted (self ):
255+ rng = np .random .default_rng (RNG_SEED )
256+ events = _make_events (500 , np .radians (1.0 ), 2.5 , "aux" , np .zeros (500 ), rng )
257+ fitter = KingPSFFitter (
258+ events ,
259+ parametrization_bins = {"aux" : [- 1.0 , 1.0 ]},
260+ minimum_counts = 100 ,
261+ weight_field = None ,
262+ extension_grid = [np .radians (2.0 ), 0.0 , np .radians (1.0 )],
263+ )
264+ assert_allclose (fitter .extension_grid , [0.0 , np .radians (1.0 ), np .radians (2.0 )])
265+
266+ @pytest .fixture (scope = "class" )
267+ def multi_ext_result (self ):
268+ rng = np .random .default_rng (RNG_SEED )
269+ alpha_true , beta_true = np .radians (1.0 ), 2.5
270+ n = 100_000
271+ events = _make_events (n , alpha_true , beta_true , "aux" , np .zeros (n ), rng )
272+ extension_grid = np .radians ([0.0 , 1.0 , 2.0 ])
273+ fitter = KingPSFFitter (
274+ events ,
275+ parametrization_bins = {"aux" : [- 1.0 , 1.0 ]},
276+ dpsi_nbins = 100 ,
277+ minimum_counts = 100 ,
278+ weight_field = None ,
279+ extension_grid = extension_grid ,
280+ )
281+ result = fitter .fit_all_bins (verbose = False )
282+ return result , alpha_true , beta_true , extension_grid
283+
284+ def test_shape_matches_extension_grid (self , multi_ext_result ):
285+ result , _ , _ , extension_grid = multi_ext_result
286+ assert result ["alpha" ].shape == (len (extension_grid ), 1 , 1 )
287+ assert result ["beta" ].shape == (len (extension_grid ), 1 , 1 )
288+ assert_allclose (result ["extension_grid" ], extension_grid )
289+
290+ def test_fitted_values_finite_and_valid (self , multi_ext_result ):
291+ result , _ , _ , _ = multi_ext_result
292+ assert np .all (np .isfinite (result ["alpha" ]))
293+ assert np .all (np .isfinite (result ["beta" ]))
294+ assert np .all (result ["alpha" ] > 0 )
295+ assert np .all (result ["beta" ] > 1 )
296+
297+ def test_zero_extension_recovers_point_source_fit (self , multi_ext_result ):
298+ """extension=0 should reproduce the un-smeared point-source fit."""
299+ result , alpha_true , beta_true , _ = multi_ext_result
300+ assert_allclose (result ["alpha" ][0 , 0 , 0 ], alpha_true , rtol = 0.1 )
301+ assert_allclose (result ["beta" ][0 , 0 , 0 ], beta_true , rtol = 0.2 )
302+
303+ def test_alpha_increases_with_extension (self , multi_ext_result ):
304+ """A wider source extension should widen the fitted PSF."""
305+ result , _ , _ , _ = multi_ext_result
306+ alphas = result ["alpha" ][:, 0 , 0 ]
307+ assert alphas [0 ] < alphas [1 ] < alphas [2 ]
0 commit comments