diff --git a/dataset/cluster/simple/data.fits b/dataset/cluster/simple/data.fits index 6f895062b..6e527a11d 100644 Binary files a/dataset/cluster/simple/data.fits and b/dataset/cluster/simple/data.fits differ diff --git a/dataset/cluster/simple/dataset_point.png b/dataset/cluster/simple/dataset_point.png index 0fcfce616..f1ceb0ae4 100644 Binary files a/dataset/cluster/simple/dataset_point.png and b/dataset/cluster/simple/dataset_point.png differ diff --git a/dataset/cluster/simple/galaxies_images.png b/dataset/cluster/simple/galaxies_images.png deleted file mode 100644 index e0b3de485..000000000 Binary files a/dataset/cluster/simple/galaxies_images.png and /dev/null differ diff --git a/dataset/cluster/simple/host_halo_centre.json b/dataset/cluster/simple/host_halo_centre.json deleted file mode 100644 index c90864493..000000000 --- a/dataset/cluster/simple/host_halo_centre.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "type": "instance", - "class_path": "autoarray.structures.grids.irregular_2d.Grid2DIrregular", - "arguments": { - "values": { - "type": "ndarray", - "array": [ - [ - 0.0, - 0.0 - ] - ], - "dtype": "float64" - } - } -} \ No newline at end of file diff --git a/dataset/cluster/simple/main_lens_centres.json b/dataset/cluster/simple/main_lens_centres.json deleted file mode 100644 index 2af6f796b..000000000 --- a/dataset/cluster/simple/main_lens_centres.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "type": "instance", - "class_path": "autoarray.structures.grids.irregular_2d.Grid2DIrregular", - "arguments": { - "values": { - "type": "ndarray", - "array": [ - [ - 0.0, - 0.0 - ], - [ - 10.0, - 8.0 - ] - ], - "dtype": "float64" - } - } -} \ No newline at end of file diff --git a/dataset/cluster/simple/noise_map.fits b/dataset/cluster/simple/noise_map.fits index 996853c66..234ec0e15 100644 Binary files a/dataset/cluster/simple/noise_map.fits and b/dataset/cluster/simple/noise_map.fits differ diff --git a/dataset/cluster/simple/point_dataset_0.json b/dataset/cluster/simple/point_dataset_0.json index a08989032..3dba503b3 100644 --- a/dataset/cluster/simple/point_dataset_0.json +++ b/dataset/cluster/simple/point_dataset_0.json @@ -3,44 +3,49 @@ "class_path": "autolens.point.dataset.PointDataset", "arguments": { "time_delays": null, - "positions": { + "fluxes_noise_map": null, + "fluxes": null, + "time_delays_noise_map": null, + "redshift": 1.0, + "name": "point_0", + "positions_noise_map": { "type": "instance", - "class_path": "autoarray.structures.grids.irregular_2d.Grid2DIrregular", + "class_path": "autoarray.structures.arrays.irregular.ArrayIrregular", "arguments": { "values": { "type": "ndarray", "array": [ - [ - 1.0, - 0.0 - ], - [ - 0.0, - 1.0 - ] + 0.005, + 0.005, + 0.005 ], "dtype": "float64" } } }, - "positions_noise_map": { + "positions": { "type": "instance", - "class_path": "autoarray.structures.arrays.irregular.ArrayIrregular", + "class_path": "autoarray.structures.grids.irregular_2d.Grid2DIrregular", "arguments": { "values": { "type": "ndarray", "array": [ - 0.005, - 0.005 + [ + -9.166406249999998, + -19.276733167466432 + ], + [ + 0.0359375, + -0.0739730032399208 + ], + [ + 1.8125, + 23.34028674178623 + ] ], "dtype": "float64" } } - }, - "time_delays_noise_map": null, - "fluxes": null, - "redshift": 1.0, - "name": "point_0", - "fluxes_noise_map": null + } } } \ No newline at end of file diff --git a/dataset/cluster/simple/point_dataset_1.json b/dataset/cluster/simple/point_dataset_1.json index 890646e8d..b566489dc 100644 --- a/dataset/cluster/simple/point_dataset_1.json +++ b/dataset/cluster/simple/point_dataset_1.json @@ -3,44 +3,49 @@ "class_path": "autolens.point.dataset.PointDataset", "arguments": { "time_delays": null, - "positions": { + "fluxes_noise_map": null, + "fluxes": null, + "time_delays_noise_map": null, + "redshift": 2.0, + "name": "point_1", + "positions_noise_map": { "type": "instance", - "class_path": "autoarray.structures.grids.irregular_2d.Grid2DIrregular", + "class_path": "autoarray.structures.arrays.irregular.ArrayIrregular", "arguments": { "values": { "type": "ndarray", "array": [ - [ - 1.0, - 0.0 - ], - [ - 0.0, - 1.0 - ] + 0.005, + 0.005, + 0.005 ], "dtype": "float64" } } }, - "positions_noise_map": { + "positions": { "type": "instance", - "class_path": "autoarray.structures.arrays.irregular.ArrayIrregular", + "class_path": "autoarray.structures.grids.irregular_2d.Grid2DIrregular", "arguments": { "values": { "type": "ndarray", "array": [ - 0.005, - 0.005 + [ + -15.96796875, + 19.00429600919258 + ], + [ + 0.68125, + -0.5133004737014016 + ], + [ + 13.6921875, + -14.30926557794663 + ] ], "dtype": "float64" } } - }, - "time_delays_noise_map": null, - "fluxes": null, - "redshift": 2.0, - "name": "point_1", - "fluxes_noise_map": null + } } } \ No newline at end of file diff --git a/dataset/cluster/simple/point_datasets.csv b/dataset/cluster/simple/point_datasets.csv index a460418f0..7c9c3b36a 100644 --- a/dataset/cluster/simple/point_datasets.csv +++ b/dataset/cluster/simple/point_datasets.csv @@ -1,5 +1,7 @@ name,y,x,positions_noise,redshift -point_0,1.0,0.0,0.005,1.0 -point_0,0.0,1.0,0.005,1.0 -point_1,1.0,0.0,0.005,2.0 -point_1,0.0,1.0,0.005,2.0 +point_0,-9.166406249999998,-19.276733167466432,0.005,1.0 +point_0,0.0359375,-0.0739730032399208,0.005,1.0 +point_0,1.8125,23.34028674178623,0.005,1.0 +point_1,-15.96796875,19.00429600919258,0.005,2.0 +point_1,0.68125,-0.5133004737014016,0.005,2.0 +point_1,13.6921875,-14.30926557794663,0.005,2.0 diff --git a/dataset/cluster/simple/psf.fits b/dataset/cluster/simple/psf.fits index 3d342eff1..940efcd87 100644 Binary files a/dataset/cluster/simple/psf.fits and b/dataset/cluster/simple/psf.fits differ diff --git a/dataset/cluster/simple/source_centres.json b/dataset/cluster/simple/source_centres.json deleted file mode 100644 index 1dc149240..000000000 --- a/dataset/cluster/simple/source_centres.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "type": "instance", - "class_path": "autoarray.structures.grids.irregular_2d.Grid2DIrregular", - "arguments": { - "values": { - "type": "ndarray", - "array": [ - [ - 0.3, - 0.5 - ], - [ - -0.8, - 1.2 - ] - ], - "dtype": "float64" - } - } -} \ No newline at end of file diff --git a/dataset/cluster/simple/tracer.json b/dataset/cluster/simple/tracer.json index e3608c49a..8dc008087 100644 --- a/dataset/cluster/simple/tracer.json +++ b/dataset/cluster/simple/tracer.json @@ -2,11 +2,6 @@ "type": "instance", "class_path": "autolens.lens.tracer.Tracer", "arguments": { - "cosmology": { - "type": "instance", - "class_path": "autogalaxy.cosmology.model.Planck15", - "arguments": {} - }, "galaxies": { "type": "list", "values": [ @@ -14,38 +9,38 @@ "type": "instance", "class_path": "autogalaxy.galaxy.galaxy.Galaxy", "arguments": { - "redshift": 0.5, "label": null, + "redshift": 0.5, "bulge": { "type": "instance", "class_path": "autogalaxy.profiles.light.standard.sersic.SersicSph", "arguments": { - "sersic_index": 4.0, - "effective_radius": 3.0, - "intensity": 1.5, "centre": { "type": "tuple", "values": [ 0.0, 0.0 ] - } + }, + "sersic_index": 4.0, + "intensity": 1.5, + "effective_radius": 3.0 } }, "mass": { "type": "instance", "class_path": "autogalaxy.profiles.mass.total.dual_pseudo_isothermal_mass.dPIEMassSph", "arguments": { - "b0": 3.0, - "rs": 20.0, - "ra": 8.0, "centre": { "type": "tuple", "values": [ 0.0, 0.0 ] - } + }, + "rs": 20.0, + "b0": 3.0, + "ra": 8.0 } } } @@ -54,38 +49,38 @@ "type": "instance", "class_path": "autogalaxy.galaxy.galaxy.Galaxy", "arguments": { - "redshift": 0.5, "label": null, + "redshift": 0.5, "bulge": { "type": "instance", "class_path": "autogalaxy.profiles.light.standard.sersic.SersicSph", "arguments": { - "sersic_index": 3.5, - "effective_radius": 1.5, - "intensity": 0.8, "centre": { "type": "tuple", "values": [ 10.0, 8.0 ] - } + }, + "sersic_index": 3.5, + "intensity": 0.8, + "effective_radius": 1.5 } }, "mass": { "type": "instance", "class_path": "autogalaxy.profiles.mass.total.dual_pseudo_isothermal_mass.dPIEMassSph", "arguments": { - "b0": 1.2, - "rs": 12.0, - "ra": 5.0, "centre": { "type": "tuple", "values": [ 10.0, 8.0 ] - } + }, + "rs": 12.0, + "b0": 1.2, + "ra": 5.0 } } } @@ -94,38 +89,38 @@ "type": "instance", "class_path": "autogalaxy.galaxy.galaxy.Galaxy", "arguments": { - "redshift": 0.5, "label": null, + "redshift": 0.5, "bulge": { "type": "instance", "class_path": "autogalaxy.profiles.light.standard.sersic.SersicSph", "arguments": { - "sersic_index": 3.0, - "effective_radius": 0.8, - "intensity": 0.4, "centre": { "type": "tuple", "values": [ 5.5, -6.5 ] - } + }, + "sersic_index": 3.0, + "intensity": 0.4, + "effective_radius": 0.8 } }, "mass": { "type": "instance", "class_path": "autogalaxy.profiles.mass.total.dual_pseudo_isothermal_mass.dPIEMassSph", "arguments": { - "b0": 0.12, - "rs": 10.0, - "ra": 0.1, "centre": { "type": "tuple", "values": [ 5.5, -6.5 ] - } + }, + "rs": 10.0, + "b0": 0.12, + "ra": 0.1 } } } @@ -134,38 +129,38 @@ "type": "instance", "class_path": "autogalaxy.galaxy.galaxy.Galaxy", "arguments": { - "redshift": 0.5, "label": null, + "redshift": 0.5, "bulge": { "type": "instance", "class_path": "autogalaxy.profiles.light.standard.sersic.SersicSph", "arguments": { - "sersic_index": 3.0, - "effective_radius": 0.8, - "intensity": 0.32, "centre": { "type": "tuple", "values": [ -7.5, 3.0 ] - } + }, + "sersic_index": 3.0, + "intensity": 0.32, + "effective_radius": 0.8 } }, "mass": { "type": "instance", "class_path": "autogalaxy.profiles.mass.total.dual_pseudo_isothermal_mass.dPIEMassSph", "arguments": { - "b0": 0.096, - "rs": 10.0, - "ra": 0.1, "centre": { "type": "tuple", "values": [ -7.5, 3.0 ] - } + }, + "rs": 8.94427190999916, + "b0": 0.1073312629199899, + "ra": 0.1 } } } @@ -174,38 +169,38 @@ "type": "instance", "class_path": "autogalaxy.galaxy.galaxy.Galaxy", "arguments": { - "redshift": 0.5, "label": null, + "redshift": 0.5, "bulge": { "type": "instance", "class_path": "autogalaxy.profiles.light.standard.sersic.SersicSph", "arguments": { - "sersic_index": 3.0, - "effective_radius": 0.8, - "intensity": 0.25, "centre": { "type": "tuple", "values": [ 12.0, -5.0 ] - } + }, + "sersic_index": 3.0, + "intensity": 0.25, + "effective_radius": 0.8 } }, "mass": { "type": "instance", "class_path": "autogalaxy.profiles.mass.total.dual_pseudo_isothermal_mass.dPIEMassSph", "arguments": { - "b0": 0.075, - "rs": 10.0, - "ra": 0.1, "centre": { "type": "tuple", "values": [ 12.0, -5.0 ] - } + }, + "rs": 7.905694150420949, + "b0": 0.09486832980505139, + "ra": 0.1 } } } @@ -214,38 +209,38 @@ "type": "instance", "class_path": "autogalaxy.galaxy.galaxy.Galaxy", "arguments": { - "redshift": 0.5, "label": null, + "redshift": 0.5, "bulge": { "type": "instance", "class_path": "autogalaxy.profiles.light.standard.sersic.SersicSph", "arguments": { - "sersic_index": 3.0, - "effective_radius": 0.8, - "intensity": 0.2, "centre": { "type": "tuple", "values": [ -4.0, -9.0 ] - } + }, + "sersic_index": 3.0, + "intensity": 0.2, + "effective_radius": 0.8 } }, "mass": { "type": "instance", "class_path": "autogalaxy.profiles.mass.total.dual_pseudo_isothermal_mass.dPIEMassSph", "arguments": { - "b0": 0.06, - "rs": 10.0, - "ra": 0.1, "centre": { "type": "tuple", "values": [ -4.0, -9.0 ] - } + }, + "rs": 7.0710678118654755, + "b0": 0.08485281374238571, + "ra": 0.1 } } } @@ -254,38 +249,38 @@ "type": "instance", "class_path": "autogalaxy.galaxy.galaxy.Galaxy", "arguments": { - "redshift": 0.5, "label": null, + "redshift": 0.5, "bulge": { "type": "instance", "class_path": "autogalaxy.profiles.light.standard.sersic.SersicSph", "arguments": { - "sersic_index": 3.0, - "effective_radius": 0.8, - "intensity": 0.16, "centre": { "type": "tuple", "values": [ 3.0, 13.0 ] - } + }, + "sersic_index": 3.0, + "intensity": 0.16, + "effective_radius": 0.8 } }, "mass": { "type": "instance", "class_path": "autogalaxy.profiles.mass.total.dual_pseudo_isothermal_mass.dPIEMassSph", "arguments": { - "b0": 0.048, - "rs": 10.0, - "ra": 0.1, "centre": { "type": "tuple", "values": [ 3.0, 13.0 ] - } + }, + "rs": 6.324555320336759, + "b0": 0.0758946638440411, + "ra": 0.1 } } } @@ -294,38 +289,38 @@ "type": "instance", "class_path": "autogalaxy.galaxy.galaxy.Galaxy", "arguments": { - "redshift": 0.5, "label": null, + "redshift": 0.5, "bulge": { "type": "instance", "class_path": "autogalaxy.profiles.light.standard.sersic.SersicSph", "arguments": { - "sersic_index": 3.0, - "effective_radius": 0.8, - "intensity": 0.13, "centre": { "type": "tuple", "values": [ -14.0, 4.0 ] - } + }, + "sersic_index": 3.0, + "intensity": 0.13, + "effective_radius": 0.8 } }, "mass": { "type": "instance", "class_path": "autogalaxy.profiles.mass.total.dual_pseudo_isothermal_mass.dPIEMassSph", "arguments": { - "b0": 0.039, - "rs": 10.0, - "ra": 0.1, "centre": { "type": "tuple", "values": [ -14.0, 4.0 ] - } + }, + "rs": 5.7008771254956905, + "b0": 0.06841052550594828, + "ra": 0.1 } } } @@ -334,38 +329,38 @@ "type": "instance", "class_path": "autogalaxy.galaxy.galaxy.Galaxy", "arguments": { - "redshift": 0.5, "label": null, + "redshift": 0.5, "bulge": { "type": "instance", "class_path": "autogalaxy.profiles.light.standard.sersic.SersicSph", "arguments": { - "sersic_index": 3.0, - "effective_radius": 0.8, - "intensity": 0.1, "centre": { "type": "tuple", "values": [ 15.0, 9.0 ] - } + }, + "sersic_index": 3.0, + "intensity": 0.1, + "effective_radius": 0.8 } }, "mass": { "type": "instance", "class_path": "autogalaxy.profiles.mass.total.dual_pseudo_isothermal_mass.dPIEMassSph", "arguments": { - "b0": 0.03, - "rs": 10.0, - "ra": 0.1, "centre": { "type": "tuple", "values": [ 15.0, 9.0 ] - } + }, + "rs": 5.0, + "b0": 0.06, + "ra": 0.1 } } } @@ -374,38 +369,38 @@ "type": "instance", "class_path": "autogalaxy.galaxy.galaxy.Galaxy", "arguments": { - "redshift": 0.5, "label": null, + "redshift": 0.5, "bulge": { "type": "instance", "class_path": "autogalaxy.profiles.light.standard.sersic.SersicSph", "arguments": { - "sersic_index": 3.0, - "effective_radius": 0.8, - "intensity": 0.08, "centre": { "type": "tuple", "values": [ -9.0, -12.0 ] - } + }, + "sersic_index": 3.0, + "intensity": 0.08, + "effective_radius": 0.8 } }, "mass": { "type": "instance", "class_path": "autogalaxy.profiles.mass.total.dual_pseudo_isothermal_mass.dPIEMassSph", "arguments": { - "b0": 0.024, - "rs": 10.0, - "ra": 0.1, "centre": { "type": "tuple", "values": [ -9.0, -12.0 ] - } + }, + "rs": 4.47213595499958, + "b0": 0.05366563145999495, + "ra": 0.1 } } } @@ -414,38 +409,38 @@ "type": "instance", "class_path": "autogalaxy.galaxy.galaxy.Galaxy", "arguments": { - "redshift": 0.5, "label": null, + "redshift": 0.5, "bulge": { "type": "instance", "class_path": "autogalaxy.profiles.light.standard.sersic.SersicSph", "arguments": { - "sersic_index": 3.0, - "effective_radius": 0.8, - "intensity": 0.06, "centre": { "type": "tuple", "values": [ 8.5, 5.5 ] - } + }, + "sersic_index": 3.0, + "intensity": 0.06, + "effective_radius": 0.8 } }, "mass": { "type": "instance", "class_path": "autogalaxy.profiles.mass.total.dual_pseudo_isothermal_mass.dPIEMassSph", "arguments": { - "b0": 0.018, - "rs": 10.0, - "ra": 0.1, "centre": { "type": "tuple", "values": [ 8.5, 5.5 ] - } + }, + "rs": 3.872983346207417, + "b0": 0.046475800154489, + "ra": 0.1 } } } @@ -454,38 +449,38 @@ "type": "instance", "class_path": "autogalaxy.galaxy.galaxy.Galaxy", "arguments": { - "redshift": 0.5, "label": null, + "redshift": 0.5, "bulge": { "type": "instance", "class_path": "autogalaxy.profiles.light.standard.sersic.SersicSph", "arguments": { - "sersic_index": 3.0, - "effective_radius": 0.8, - "intensity": 0.05, "centre": { "type": "tuple", "values": [ -6.5, 11.0 ] - } + }, + "sersic_index": 3.0, + "intensity": 0.05, + "effective_radius": 0.8 } }, "mass": { "type": "instance", "class_path": "autogalaxy.profiles.mass.total.dual_pseudo_isothermal_mass.dPIEMassSph", "arguments": { - "b0": 0.015, - "rs": 10.0, - "ra": 0.1, "centre": { "type": "tuple", "values": [ -6.5, 11.0 ] - } + }, + "rs": 3.5355339059327378, + "b0": 0.042426406871192854, + "ra": 0.1 } } } @@ -494,22 +489,22 @@ "type": "instance", "class_path": "autogalaxy.galaxy.galaxy.Galaxy", "arguments": { - "redshift": 0.5, "label": null, + "redshift": 0.5, "dark": { "type": "instance", "class_path": "autogalaxy.profiles.mass.dark.nfw_mcr.NFWMCRLudlowSph", "arguments": { - "redshift_object": 0.5, - "redshift_source": 2.0, - "mass_at_200": 1995262314968882.8, "centre": { "type": "tuple", "values": [ 0.0, 0.0 ] - } + }, + "redshift_object": 0.5, + "redshift_source": 2.0, + "mass_at_200": 1995262314968882.8 } } } @@ -518,32 +513,32 @@ "type": "instance", "class_path": "autogalaxy.galaxy.galaxy.Galaxy", "arguments": { - "redshift": 1.0, "label": null, + "redshift": 1.0, "bulge": { "type": "instance", "class_path": "autogalaxy.profiles.light.standard.sersic_core.SersicCore", "arguments": { - "centre": { + "intensity": 2.0, + "sersic_index": 1.0, + "ell_comps": { "type": "tuple", "values": [ - 0.3, - 0.5 + 0.0962250448649376, + -0.05555555555555551 ] }, - "ell_comps": { + "effective_radius": 0.3, + "centre": { "type": "tuple", "values": [ - 0.0962250448649376, - -0.05555555555555551 + 0.3, + 0.5 ] }, - "radius_break": 0.025, - "sersic_index": 1.0, - "intensity": 2.0, + "gamma": 0.25, "alpha": 3.0, - "effective_radius": 0.3, - "gamma": 0.25 + "radius_break": 0.025 } } } @@ -552,37 +547,42 @@ "type": "instance", "class_path": "autogalaxy.galaxy.galaxy.Galaxy", "arguments": { - "redshift": 2.0, "label": null, + "redshift": 2.0, "bulge": { "type": "instance", "class_path": "autogalaxy.profiles.light.standard.sersic_core.SersicCore", "arguments": { - "centre": { + "intensity": 2.0, + "sersic_index": 1.0, + "ell_comps": { "type": "tuple", "values": [ - -0.8, - 1.2 + 1.360718665719281e-17, + -0.11111111111111108 ] }, - "ell_comps": { + "effective_radius": 0.3, + "centre": { "type": "tuple", "values": [ - 1.360718665719281e-17, - -0.11111111111111108 + -0.8, + 1.2 ] }, - "radius_break": 0.025, - "sersic_index": 1.0, - "intensity": 2.0, + "gamma": 0.25, "alpha": 3.0, - "effective_radius": 0.3, - "gamma": 0.25 + "radius_break": 0.025 } } } } ] + }, + "cosmology": { + "type": "instance", + "class_path": "autogalaxy.cosmology.model.Planck15", + "arguments": {} } } } \ No newline at end of file diff --git a/dataset/cluster/simple/tracer.png b/dataset/cluster/simple/tracer.png deleted file mode 100644 index bde921964..000000000 Binary files a/dataset/cluster/simple/tracer.png and /dev/null differ diff --git a/dataset/cluster/simple/visualization_critical_curves.png b/dataset/cluster/simple/visualization_critical_curves.png deleted file mode 100644 index d1fd6b661..000000000 Binary files a/dataset/cluster/simple/visualization_critical_curves.png and /dev/null differ diff --git a/dataset/cluster/simple/visualization_overlaid_positions.png b/dataset/cluster/simple/visualization_overlaid_positions.png deleted file mode 100644 index 1d2b43eab..000000000 Binary files a/dataset/cluster/simple/visualization_overlaid_positions.png and /dev/null differ diff --git a/dataset/cluster/simple/visualization_per_source_grid.png b/dataset/cluster/simple/visualization_per_source_grid.png deleted file mode 100644 index a3af3928a..000000000 Binary files a/dataset/cluster/simple/visualization_per_source_grid.png and /dev/null differ diff --git a/llms-full.txt b/llms-full.txt index 3734727ce..45b558ad4 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -249,7 +249,7 @@ AUTO-GENERATED by PyAutoBuild — do not edit by hand; regenerate with generate. - Contents: Dataset & Mask, Galaxy Centres, Model Fit, Source Flux, Source Magnification, Impact of Extra Galaxies, Interpolated Source, Parametric Comparison - [Features: Group Scaling Relation Fit](scripts/group/features/scaling_relation/fit.py): A group-scale strong lens often has many foreground galaxies near the line of sight to the source, on top of one or more primary lens galaxies. The **three-tier API** splits these galaxies into populations that the lens model treats differently: - Contents: Prerequisites, Dataset & Mask, Centres + Luminosities, Over Sampling, MGE Basis, Galaxies, Tracer, Three-Tier Deflection Tour, Intensities, Wrap Up -- [__Log Likelihood Function: Group Scaling Relation__](scripts/group/features/scaling_relation/likelihood_function.py): This script describes the additional steps required to compute the `log_likelihood` for a group-scale strong lens whose foreground galaxy population is split across three tiers — main lens galaxies (modelled via the group `lens_dict` API), individually-modelled extras (each with its own free `einstein_radius`), and scaling-tier extras (whose Einstein radii are derived from a shared two-parameter relation `einstein_radius = scaling_factor * luminosity ** scaling_exponent`). +- [__Log Likelihood Function: Group Scaling Relation__](scripts/group/features/scaling_relation/likelihood_function.py): This script describes the additional steps required to compute the `log_likelihood` for a group-scale strong lens whose foreground galaxy population is split across three tiers — main lens galaxies (modelled via the group `lens_dict` API), individually-modelled extras (each with its own free `einstein_radius`), and scaling-tier extras (whose Einstein radii are derived from a shared reference-anchored relation `einstein_radius = einstein_radius_ref * (luminosity / luminosity_ref) ** 0.5`, the Lenstool convention). - Contents: Prerequisites, Dataset & Mask., Centres + Luminosities, Galaxies, Three-Tier Deflection, Manual Ray-Tracing, Source-Plane Image, Fit Check., Wrap Up. - [Modeling Features (Group): Scaling Relations](scripts/group/features/scaling_relation/modeling.py): Group-scale strong lenses can have many galaxies in the foreground beyond the primary lens. As the number grows, modelling each galaxy individually becomes impractical: a system with 10 companions would gain 10 extra Einstein-radius free parameters, and the data is rarely informative enough to constrain them all. - Contents: Three-Tier API, Centres, Luminosities, Dataset & Mask, Main Lens Galaxies, Extra Galaxies, Scaling Galaxies, Model, Over Sampling, Search and Analysis, Wrap Up diff --git a/scripts/cluster/csv_api.py b/scripts/cluster/csv_api.py index 6a72ac789..96ec9db71 100644 --- a/scripts/cluster/csv_api.py +++ b/scripts/cluster/csv_api.py @@ -390,8 +390,8 @@ scaling tier is implicitly one profile class per member, so naming each member and emitting an ``attr_name`` column would be more overhead than signal — every row uses the same ``dPIEMassSph`` mass profile with -parameters derived from the shared ``scaling_factor`` and -``scaling_exponent`` modelling parameters. +parameters derived from the reference-anchored scaling relation's shared +``b0_ref`` normalization (see ``modeling.py``). ``al.galaxy_table_to_csv`` and ``al.galaxy_table_from_csv`` are the schema-specific writers/readers. The simulator emits 10 scaling members diff --git a/scripts/cluster/likelihood_function.py b/scripts/cluster/likelihood_function.py index d667ccf3e..324e09f72 100644 --- a/scripts/cluster/likelihood_function.py +++ b/scripts/cluster/likelihood_function.py @@ -141,18 +141,23 @@ host_halo_galaxy = galaxies_by_name["host_halo"] source_galaxies = [galaxies_by_name["source_0"], galaxies_by_name["source_1"]] -# Scaling tier: per-member dPIE built from the legacy CSV + shared scaling relation. +# Scaling tier: per-member dPIE built from the legacy CSV + the reference-anchored +# scaling relation (Lenstool convention; see modeling.py for the full rationale). scaling_galaxies = [] -SCALING_FACTOR_TRUTH = 0.3 -SCALING_EXPONENT_TRUTH = 1.0 +SCALING_B0_REF_TRUTH = 0.12 +SCALING_EXPONENT = 0.5 +SCALING_RS_REF = 10.0 +luminosity_ref = max(scaling_table.luminosities) for centre, luminosity in zip( scaling_table.centres.in_list, scaling_table.luminosities ): - b0 = SCALING_FACTOR_TRUTH * luminosity**SCALING_EXPONENT_TRUTH + luminosity_ratio = luminosity / luminosity_ref + b0 = SCALING_B0_REF_TRUTH * luminosity_ratio**SCALING_EXPONENT + rs = SCALING_RS_REF * luminosity_ratio**SCALING_EXPONENT scaling_galaxies.append( al.Galaxy( redshift=redshift_lens, - mass=al.mp.dPIEMassSph(centre=tuple(centre), ra=0.1, rs=10.0, b0=b0), + mass=al.mp.dPIEMassSph(centre=tuple(centre), ra=0.1, rs=rs, b0=b0), ) ) @@ -162,8 +167,8 @@ The tracer carries: - 2 main lens galaxies (BCG + satellite) — individually-modelled dPIE mass profiles. - - 10 scaling-tier member galaxies — dPIE mass profiles whose ``b0`` is derived from the shared - scaling relation ``b0 = scaling_factor × luminosity^scaling_exponent``. + - 10 scaling-tier member galaxies — dPIE mass profiles whose ``b0`` and ``rs`` derive from the + reference-anchored scaling relation ``b0 = b0_ref × (L/L_ref)^0.5`` (Lenstool convention). - 1 host dark matter halo — ``NFWMCRLudlowSph`` at the cluster centre. - 2 source galaxies — ``Point`` profiles at distinct redshifts (multi-plane). diff --git a/scripts/cluster/modeling.py b/scripts/cluster/modeling.py index 88b1dffe4..f5ac81294 100644 --- a/scripts/cluster/modeling.py +++ b/scripts/cluster/modeling.py @@ -44,18 +44,19 @@ - There are 2 main lens galaxies with ``dPIEMassSph`` total mass distributions, each with their centre fixed to the values written out by the simulator [6 parameters]. - There are 10 scaling-tier member galaxies. Each carries a ``dPIEMassSph`` mass with centre fixed, - ``ra`` and ``rs`` fixed at the simulator truth values, and ``b0`` derived from the *shared* - scaling-relation parameters and the per-member luminosity [2 parameters total for the entire tier]. + ``ra`` fixed, and ``b0`` / ``rs`` derived from the shared reference-anchored scaling relation and the + per-member luminosity [1 parameter total for the entire tier]. - There is 1 standalone ``NFWMCRLudlowSph`` host dark matter halo with its centre fixed and a free ``mass_at_200`` [1 parameter]. - There are 2 source galaxies modeled as ``Point`` sources, each with its redshift pinned to the value in its ``PointDataset`` row [4 parameters]. -The number of free parameters and therefore the dimensionality of non-linear parameter space is N=13. +The number of free parameters and therefore the dimensionality of non-linear parameter space is N=12. The defining feature of cluster modeling is the scaling tier: 10 lower-mass members are fit jointly -with just *2 free parameters* (``scaling_factor`` and ``scaling_exponent``). Adding more members to -``scaling_galaxies.csv`` in the future does not grow the dimensionality of parameter space. +with a *single free parameter* (``b0_ref``, the lens strength of the brightest member; the relation's +exponent is fixed at the Faber-Jackson value of 0.5, following the Lenstool convention). Adding more +members to ``scaling_galaxies.csv`` in the future does not grow the dimensionality of parameter space. __Simulation__ @@ -247,10 +248,10 @@ with a ``dPIEMassSph`` total mass profile whose centre is fixed to ``main_lens_centres[i]``. - ``scaling_galaxies``: The 10 scaling-tier cluster members. Each carries a ``dPIEMassSph`` mass with - centre fixed (from the CSV), ``ra`` and ``rs`` fixed at the simulator truth values, and ``b0`` - derived from the *shared* ``scaling_factor`` and ``scaling_exponent`` parameters plus the per-member - luminosity. The whole tier contributes just 2 free parameters to the model regardless of how many - members are in the CSV. + centre fixed (from the CSV), ``ra`` fixed, and ``b0`` / ``rs`` derived from the shared + reference-anchored scaling relation (single free normalization ``b0_ref``, exponents fixed at 0.5) + plus the per-member luminosity. The whole tier contributes 1 free parameter to the model regardless + of how many members are in the CSV. - ``host_halo``: A single standalone ``Galaxy`` carrying the cluster's ``NFWMCRLudlowSph`` dark matter halo. The halo is *not* tied to any individual member — it sits "on top of" the members and @@ -285,23 +286,42 @@ - The 2 main lens galaxies each have a ``dPIEMassSph`` mass profile with centre fixed and free ``ra``, ``rs``, ``b0`` — 3 free parameters per galaxy [6 parameters]. - - The 10 scaling-tier members share two free parameters: ``scaling_factor`` and ``scaling_exponent``. - Each member's ``b0`` is computed as ``scaling_factor * luminosity ** scaling_exponent``; ``ra`` and - ``rs`` are held fixed at the simulator truth values (0.1" and 10.0") [2 parameters]. + - The 10 scaling-tier members share a single free parameter: ``b0_ref``, the lens strength of the + *brightest* member (the reference galaxy). Each member's ``b0`` and ``rs`` are computed as + ``b0_ref * (L / L_ref) ** 0.5`` and ``rs_ref * (L / L_ref) ** 0.5`` with the exponents fixed; + ``ra`` (0.1") and ``rs_ref`` (10.0") are held fixed at the simulator truth values [1 parameter]. - The host halo has an ``NFWMCRLudlowSph`` mass profile with centre fixed and a free ``mass_at_200`` [1 parameter]. - Each source has a ``Point`` model with free ``centre_0`` / ``centre_1`` priors initialised from the mean of that source's observed positions [4 parameters]. -The number of free parameters and therefore the dimensionality of non-linear parameter space is N=13. +The number of free parameters and therefore the dimensionality of non-linear parameter space is N=12. __Scaling Relation__ -The scaling relation has the form ``b0 = scaling_factor * luminosity ** scaling_exponent``. With 10 -members and shared (``scaling_factor``, ``scaling_exponent``) the relation is well-constrained: as long -as the per-member luminosities span a meaningful dynamic range, the multi-image positions pull the two -relation parameters tightly. The simulator's truth values are ``scaling_factor = 0.3`` and -``scaling_exponent = 1.0``. Priors below are wider than the truth to give the search room. +The scaling relation is reference-anchored, the convention used by Lenstool and essentially every +published cluster strong-lensing analysis (Limousin et al. 2005; Eliasdottir et al. 2007; Bergamini et +al. 2019): + + b0_i = b0_ref * (L_i / L_ref) ** 0.5 + rs_i = rs_ref * (L_i / L_ref) ** 0.5 + +The single free parameter ``b0_ref`` is the lens strength of the brightest scaling member — a physically +interpretable quantity (roughly that galaxy's Einstein radius) for which a prior range is easy to +motivate, unlike an abstract multiplicative factor whose units depend on the (arbitrary) luminosity +normalization. The exponent is *fixed* at 0.5 rather than fitted: b0 ∝ sigma² for the dPIE, and +Faber-Jackson (sigma ∝ L^(1/4)) gives b0 ∝ L^(1/2). The truncation radius scales with the same fixed +exponent, mirroring Lenstool's r_cut ∝ L^(1/2). Only luminosity *ratios* enter, so the CSV's luminosity +units are irrelevant; magnitude catalogues convert via ``L_i / L_ref = 10 ** (0.4 * (m_ref - m_i))``. + +Freeing the exponent (or ``rs_ref``) is a one-line change shown in the code comment below — useful as a +systematics test, at the cost of the degeneracy between normalization and slope that the fixed-exponent +convention exists to avoid. Kinematic calibrations (Bergamini et al. 2019: sigma ∝ L^0.27-0.28 from MUSE +member kinematics, i.e. a b0 exponent ≈ 0.55, with the r_cut exponent from the fundamental plane) are +the standard refinement when member velocity dispersions are available. + +The simulator's truth value is ``b0_ref = 0.12`` arcsec. The prior below is much wider than the truth to +give the search room. """ redshift_lens = 0.5 source_redshifts = [dataset.redshift for dataset in dataset_list] @@ -339,28 +359,39 @@ mean=float(np.mean(positions[:, 1])), sigma=3.0 ) -# Scaling Tier Members (dPIEMassSph, b0 derived from shared scaling relation). +# Scaling Tier Members (dPIEMassSph, b0 and rs derived from the reference-anchored +# scaling relation). # -# scaling_factor and scaling_exponent are defined ONCE outside the loop. Every -# member's b0 is a derived prior of these two shared parameters plus its own -# (fixed) luminosity, so the entire tier contributes 2 free parameters regardless +# b0_ref is defined ONCE outside the loop — it is the tier's only free parameter, +# the lens strength of the brightest (reference) member. Every member's b0 and rs +# are derived priors of b0_ref (or plain fixed values, for rs) scaled by its +# luminosity ratio to the reference, with the exponents fixed at the Faber-Jackson +# value of 0.5. The entire tier therefore contributes 1 free parameter regardless # of how many members are in scaling_galaxies.csv. +# +# To free the exponent as a systematics test, replace the fixed value with e.g. +# `scaling_exponent = af.UniformPrior(lower_limit=0.0, upper_limit=1.0)` — every +# member's b0 then derives from two shared parameters, as in older versions of +# this example. -scaling_factor = af.UniformPrior(lower_limit=0.0, upper_limit=1.0) -scaling_exponent = af.UniformPrior(lower_limit=0.0, upper_limit=2.0) +scaling_b0_ref = af.UniformPrior(lower_limit=0.0, upper_limit=1.0) +scaling_exponent = 0.5 +scaling_luminosity_ref = max(scaling_galaxies_luminosity_list) scaling_ra_fixed = 0.1 -scaling_rs_fixed = 10.0 +scaling_rs_ref_fixed = 10.0 scaling_galaxies_list = [] for centre, luminosity in zip( scaling_galaxies_centres, scaling_galaxies_luminosity_list ): + luminosity_ratio = luminosity / scaling_luminosity_ref + mass = af.Model(al.mp.dPIEMassSph) mass.centre = tuple(centre) mass.ra = scaling_ra_fixed - mass.rs = scaling_rs_fixed - mass.b0 = scaling_factor * luminosity**scaling_exponent + mass.rs = scaling_rs_ref_fixed * luminosity_ratio**scaling_exponent + mass.b0 = scaling_b0_ref * luminosity_ratio**scaling_exponent scaling_galaxies_list.append(af.Model(al.Galaxy, redshift=redshift_lens, mass=mass)) diff --git a/scripts/cluster/simulator.py b/scripts/cluster/simulator.py index dca279008..3d9c46d8f 100644 --- a/scripts/cluster/simulator.py +++ b/scripts/cluster/simulator.py @@ -67,8 +67,9 @@ relation. Each member is individually weak compared to the main galaxies or the host halo, but the population together perturbs the deflection field non-trivially — exactly the regime in which the scaling-relation tier of the modeling API earns its keep. The number of free parameters does not grow - with the number of scaling members; the shared `scaling_factor` and `scaling_exponent` (2 parameters - total) determine every member's mass from its luminosity. + with the number of scaling members; a single shared normalization `b0_ref` (the lens strength of the + brightest member, with the relation's exponent fixed at the Faber-Jackson value) determines every + member's mass from its luminosity. - `host_halo_galaxy`: A standalone `Galaxy` holding the cluster's `NFWMCRLudlowSph` dark matter halo. It is not tied to any individual member galaxy — the halo is a separate mass component sitting "on top of" @@ -78,8 +79,9 @@ light profile (for visualization of the lensed arcs) and a `Point` model component (used during point-source modeling). -Main lens, host halo, and source centres are saved to JSON files. Scaling-member centres and luminosities -are saved to ``scaling_galaxies.csv`` (the canonical input for the scaling tier). +Main lens, host halo, and source truth parameters (including centres) are saved to the named-galaxy CSVs +(``mass.csv`` / ``light.csv`` / ``point.csv``). Scaling-member centres and luminosities are saved to +``scaling_galaxies.csv`` (the canonical input for the scaling tier). __dPIE Mass Profile__ @@ -96,21 +98,36 @@ __Luminosity-Mass Scaling Relation__ -The 10 scaling members share a single one-parameter relation for the dPIE mass normalization: - - b0 = scaling_factor * luminosity ** scaling_exponent - -Truth values used in this simulator are ``scaling_factor = 0.3`` arcsec / unit luminosity and -``scaling_exponent = 1.0`` (i.e. linear in luminosity). The core radius ``ra`` and truncation radius -``rs`` are held fixed across all scaling members at ``ra = 0.1"`` and ``rs = 10.0"``; only ``b0`` varies -per member. Luminosities are log-spaced across roughly 0.05–0.40, so per-member ``b0`` values run from -~0.015 to ~0.12 arcsec — each member is individually well below the BCG (``b0 = 3.0``) but the 10 of -them sum to a few-tenths of an arcsec of effective mass, perturbing the deflection field by ~10–15%. - -The modeling script promotes ``scaling_factor`` and ``scaling_exponent`` to free parameters; their truth -values above are recovered when the model is fit to the simulated point datasets. Adding more scaling -members in the future amounts to adding rows to ``scaling_galaxies.csv`` — the number of free parameters -in the model stays at 2 for the entire tier. +The 10 scaling members share a reference-anchored relation for the dPIE mass normalization — the +convention used by Lenstool and essentially every published cluster strong-lensing analysis +(Limousin et al. 2005; Eliasdottir et al. 2007; Bergamini et al. 2019): + + b0_i = b0_ref * (L_i / L_ref) ** 0.5 + rs_i = rs_ref * (L_i / L_ref) ** 0.5 + +where ``L_ref`` is the luminosity of the *brightest scaling member* (the reference galaxy) and +``b0_ref`` is that member's lens strength. Anchoring to a reference galaxy makes the normalization +physically interpretable — it is the Einstein-radius-like strength of a galaxy you can point at in the +image — which is what makes a sensible prior range easy to define. The exponent is **fixed at 0.5** +rather than fitted: for the dPIE, ``b0`` is proportional to the velocity dispersion squared, and the +Faber-Jackson relation (L ∝ sigma^4, i.e. sigma ∝ L^(1/4)) then gives b0 ∝ L^(1/2). Lenstool applies +the same fixed-exponent scaling to the truncation radius (r_cut ∝ L^(1/2)), which is why ``rs`` scales +here too; the core radius ``ra`` is held fixed at a small value across the tier (0.1"), again following +standard practice, since strong lensing barely constrains it. + +Truth values used in this simulator are ``b0_ref = 0.12`` arcsec and ``rs_ref = 10.0`` arcsec, anchored +to the brightest member (``L_ref = 0.40``). Luminosities are log-spaced across roughly 0.05–0.40, so +per-member ``b0`` values run from ~0.042 to 0.12 arcsec — each member is individually well below the +BCG (``b0 = 3.0``) but the 10 of them together perturb the deflection field by ~10–15%. + +The modeling script promotes ``b0_ref`` to the tier's single free parameter and recovers the truth value +when fit to the simulated point datasets. Adding more scaling members amounts to adding rows to +``scaling_galaxies.csv`` — the tier's free-parameter count stays at 1. Note that only the luminosity +*ratios* ``L_i / L_ref`` enter the relation, so the units of the luminosity column are irrelevant; +observational catalogues quoting magnitudes convert via ``L_i / L_ref = 10 ** (0.4 * (m_ref - m_i))``. +Kinematic calibrations of the exponent exist for when higher fidelity is needed — Bergamini et al. 2019 +measure sigma ∝ L^0.27-0.28 from MUSE member kinematics (b0 exponent ≈ 0.55) and derive the r_cut +exponent from the fundamental plane — but 0.5 is the standard default. __NFWMCRLudlow Host Halo__ @@ -306,15 +323,16 @@ The 10 cluster members modelled collectively via the luminosity-mass scaling relation (see the ``__Luminosity-Mass Scaling Relation__`` section of the module docstring). The simulator hardcodes the -truth values of ``scaling_factor`` and ``scaling_exponent`` here and derives each member's `b0` from its -luminosity. ``ra`` and ``rs`` are held fixed across all scaling members — only ``b0`` varies. Light -profiles use the per-member luminosity as the central intensity so the rendered image visibly traces the -scaling-tier population. -""" -scaling_factor_truth = 0.3 -scaling_exponent_truth = 1.0 +truth value of ``b0_ref`` (the brightest member's lens strength) and derives each member's ``b0`` and +``rs`` from its luminosity ratio to the reference, with both exponents fixed at the Faber-Jackson value +of 0.5. ``ra`` is held fixed across all scaling members. Light profiles use the per-member luminosity +as the central intensity so the rendered image visibly traces the scaling-tier population. +""" +scaling_b0_ref_truth = 0.12 +scaling_exponent = 0.5 +scaling_luminosity_ref = max(scaling_galaxies_luminosities) scaling_ra = 0.1 -scaling_rs = 10.0 +scaling_rs_ref = 10.0 scaling_galaxies = [] for centre, luminosity in zip(scaling_galaxies_centres, scaling_galaxies_luminosities): @@ -324,8 +342,10 @@ effective_radius=0.8, sersic_index=3.0, ) - b0 = scaling_factor_truth * luminosity**scaling_exponent_truth - mass = al.mp.dPIEMassSph(centre=centre, ra=scaling_ra, rs=scaling_rs, b0=b0) + luminosity_ratio = luminosity / scaling_luminosity_ref + b0 = scaling_b0_ref_truth * luminosity_ratio**scaling_exponent + rs = scaling_rs_ref * luminosity_ratio**scaling_exponent + mass = al.mp.dPIEMassSph(centre=centre, ra=scaling_ra, rs=rs, b0=b0) scaling_galaxies.append(al.Galaxy(redshift=redshift_lens, bulge=bulge, mass=mass)) """ @@ -512,8 +532,9 @@ def jitted_solve(tracer, source_plane_coordinate): Scaling up a real cluster to a larger member population is then a CSV-level edit: add a row per additional member, fill in its centre and luminosity, save. The modeling script picks up the new rows -automatically and the number of free parameters in the scaling tier stays at 2 (`scaling_factor` and -`scaling_exponent`). +automatically and the scaling tier's free-parameter count stays at 1 (``b0_ref``, the reference +member's lens strength; the relation's exponents stay fixed at 0.5). Only luminosity *ratios* enter the +relation, so any consistent luminosity convention works — including converting from magnitudes. """ al.galaxy_table_to_csv( centres=scaling_galaxies_centres, diff --git a/scripts/cluster/start_here.py b/scripts/cluster/start_here.py index ef63093f3..4f6d31a18 100644 --- a/scripts/cluster/start_here.py +++ b/scripts/cluster/start_here.py @@ -8,8 +8,8 @@ their own light and mass profiles. - **Tens to hundreds of lower-mass member galaxies**, whose collective mass perturbs the deflection field non-trivially but whose individual contributions are too weak to constrain on their own. These - are modelled jointly on a luminosity-mass scaling relation, so the entire population shares just two - free parameters regardless of how many members are in the catalogue. + are modelled jointly on a luminosity-mass scaling relation, so the entire population shares a single + free parameter regardless of how many members are in the catalogue. - **One or more cluster-scale dark matter halos** (``10^14 – 10^15`` M_sun), modelled with NFW-like profiles and not tied to any individual galaxy. - **Multiple background sources at different redshifts**, multiply imaged by the cluster — this makes @@ -121,8 +121,8 @@ - ``point_datasets.csv`` — one row per observed multiple image, grouped by source ``name``, with a ``redshift`` column per source. - ``scaling_galaxies.csv`` — one row per scaling-tier member with columns ``y, x, luminosity``. - - ``main_lens_centres.json`` — centres of the 2 individually-modelled main galaxies. - - ``host_halo_centre.json`` — centre of the host halo. + - ``mass.csv`` / ``light.csv`` / ``point.csv`` — named-galaxy CSVs carrying the full truth model, + including the centres of the main galaxies and host halo (see ``csv_api.py``). If the dataset is missing on disk, the corresponding simulator script runs automatically. """ @@ -229,10 +229,13 @@ - **Main lens galaxies (2):** individually-modelled ``dPIEMassSph`` profiles with centre fixed to the observed light centres and free ``ra``, ``rs``, ``b0``. **6 free parameters total.** - - **Scaling-tier members (10):** ``dPIEMassSph`` profiles with centre fixed to the CSV centres, - ``ra`` and ``rs`` fixed at the simulator truth values (0.1" and 10.0"), and ``b0`` derived from the - *shared* relation ``b0 = scaling_factor * luminosity ** scaling_exponent`` plus the per-member - luminosity. **2 free parameters total for the whole tier — independent of the number of members.** + - **Scaling-tier members (10):** ``dPIEMassSph`` profiles with centre fixed to the CSV centres and + ``ra`` fixed (0.1"). ``b0`` and ``rs`` derive from the reference-anchored relation used by Lenstool + and standard in published cluster analyses: ``b0 = b0_ref * (L / L_ref) ** 0.5`` and + ``rs = rs_ref * (L / L_ref) ** 0.5``, where the reference is the *brightest* member. The exponent is + fixed at the Faber-Jackson value (b0 ∝ sigma² and sigma ∝ L^(1/4) give b0 ∝ L^(1/2)) — only the + normalization ``b0_ref``, the reference member's lens strength, is fitted. + **1 free parameter total for the whole tier — independent of the number of members.** - **Host dark matter halo:** a standalone ``Galaxy`` carrying an ``NFWMCRLudlowSph`` halo with centre fixed and a free ``mass_at_200``. **1 free parameter.** @@ -241,8 +244,11 @@ ``GaussianPrior`` centre priors initialised from the mean of each source's observed positions. **4 free parameters total.** -**Total: N = 13 free parameters.** Adding more rows to ``scaling_galaxies.csv`` does not grow N — that's -the defining feature of cluster-scale modeling on a scaling relation. +**Total: N = 12 free parameters.** Adding more rows to ``scaling_galaxies.csv`` does not grow N — that's +the defining feature of cluster-scale modeling on a scaling relation. See +``scripts/cluster/modeling.py`` for the full prose on the scaling-relation convention (why the +normalization anchors to a reference galaxy, why the exponent is fixed, and the kinematic calibrations +that refine it). __Redshifts__ @@ -255,8 +261,8 @@ __Model__ The model is composed below in four blocks: main-tier loop, host halo, source-tier loop, scaling-tier -loop (defining shared ``scaling_factor`` / ``scaling_exponent`` once outside the loop). The four -blocks are then bundled into a single ``af.Collection`` model that the analysis will receive. +loop (defining the shared ``b0_ref`` normalization once outside the loop). The four blocks are then +bundled into a single ``af.Collection`` model that the analysis will receive. """ redshift_lens = 0.5 source_redshifts = [dataset.redshift for dataset in dataset_list] @@ -291,23 +297,28 @@ mean=float(np.mean(positions[:, 1])), sigma=3.0 ) -# Scaling Tier (shared scaling_factor + scaling_exponent; per-member b0 derived). +# Scaling Tier (reference-anchored: b0_ref is the single shared free parameter, the +# lens strength of the brightest member; per-member b0 and rs derive from it with +# the exponents fixed at the Faber-Jackson value 0.5 — the Lenstool convention). -scaling_factor = af.UniformPrior(lower_limit=0.0, upper_limit=1.0) -scaling_exponent = af.UniformPrior(lower_limit=0.0, upper_limit=2.0) +scaling_b0_ref = af.UniformPrior(lower_limit=0.0, upper_limit=1.0) +scaling_exponent = 0.5 +scaling_luminosity_ref = max(scaling_galaxies_luminosity_list) scaling_ra_fixed = 0.1 -scaling_rs_fixed = 10.0 +scaling_rs_ref_fixed = 10.0 scaling_galaxies_list = [] for centre, luminosity in zip( scaling_galaxies_centres, scaling_galaxies_luminosity_list ): + luminosity_ratio = luminosity / scaling_luminosity_ref + mass = af.Model(al.mp.dPIEMassSph) mass.centre = tuple(centre) mass.ra = scaling_ra_fixed - mass.rs = scaling_rs_fixed - mass.b0 = scaling_factor * luminosity**scaling_exponent + mass.rs = scaling_rs_ref_fixed * luminosity_ratio**scaling_exponent + mass.b0 = scaling_b0_ref * luminosity_ratio**scaling_exponent scaling_galaxies_list.append(af.Model(al.Galaxy, redshift=redshift_lens, mass=mass)) @@ -439,7 +450,8 @@ - ``data.fits`` / ``noise_map.fits`` / ``psf.fits`` — your imaging. - ``point_datasets.csv`` — your measured multiple-image positions, with per-source redshifts. - ``scaling_galaxies.csv`` — your scaling-tier members' centres and luminosities. -- ``main_lens_centres.json`` / ``host_halo_centre.json`` — your individually-modelled centres. +- ``mass.csv`` / ``point.csv`` — your individually-modelled galaxies (centres and profiles), in the + named-galaxy CSV schema (see ``csv_api.py``). Update ``dataset_name`` above to point at the new folder, and the rest of the script runs unchanged. """ diff --git a/scripts/group/features/scaling_relation/fit.py b/scripts/group/features/scaling_relation/fit.py index 88836cf51..df4dd3922 100644 --- a/scripts/group/features/scaling_relation/fit.py +++ b/scripts/group/features/scaling_relation/fit.py @@ -12,9 +12,9 @@ `einstein_radius`. Use this tier for the brighter / closer companions that contribute non-trivially to the lensing on their own. - **Scaling galaxies** (`scaling_galaxies_centres.json` + `scaling_galaxies.csv`): the long tail of fainter - companions whose Einstein radii are tied together via a shared two-parameter relation - `einstein_radius = scaling_factor * luminosity ** scaling_exponent`. Adding more galaxies to this tier does - not grow the model. + companions whose Einstein radii are tied together via a shared reference-anchored relation + `einstein_radius = einstein_radius_ref * (luminosity / luminosity_ref) ** 0.5` (exponent fixed at the + Faber-Jackson value; the Lenstool convention). Adding more galaxies to this tier does not grow the model. This script illustrates the API for performing a fit to a group-scale strong lens with all three tiers active, via the standard `Tracer` and `FitImaging` objects, without invoking a non-linear search. @@ -41,7 +41,7 @@ - `autolens_workspace/scripts/group/start_here.py` — the group-scale `lens_dict` API, including how `main_lens_centres.json` is loaded. - `autolens_workspace/scripts/group/features/scaling_relation/modeling.py` — the search-based version of this - script, which composes the same model via `af.Model` with free `scaling_factor` and `scaling_exponent` priors. + script, which composes the same model via `af.Model` with a free `einstein_radius_ref` prior. The group simulator here has only ONE main lens galaxy, so the `lens_dict` has a single entry `lens_0`. The pattern generalises naturally to groups with multiple main lens galaxies. @@ -233,13 +233,16 @@ def build_source_basis(centre): ) ) -scaling_factor = 0.3 -scaling_exponent = 1.0 +einstein_radius_ref = 0.135 +scaling_exponent = 0.5 +luminosity_ref = max(scaling_galaxies_luminosities) scaling_extras = [] scaling_extras_einstein_radii = [] for centre, luminosity in zip(scaling_galaxies_centres, scaling_galaxies_luminosities): - einstein_radius = scaling_factor * luminosity**scaling_exponent + einstein_radius = ( + einstein_radius_ref * (luminosity / luminosity_ref) ** scaling_exponent + ) scaling_extras_einstein_radii.append(einstein_radius) scaling_extras.append( al.Galaxy( @@ -310,7 +313,7 @@ def build_source_basis(centre): ): print( f" scaling galaxy @ {tuple(centre)}: " - f"einstein_radius = {scaling_factor:.2f} * {luminosity:.3f} ** {scaling_exponent:.1f} = {er:.4f}" + f"einstein_radius = {einstein_radius_ref:.3f} * ({luminosity:.3f} / {luminosity_ref:.3f}) ** {scaling_exponent:.1f} = {er:.4f}" ) alpha_total_summed = alpha_main_total + alpha_individual_total + alpha_scaling_total @@ -342,13 +345,13 @@ def build_source_basis(centre): This script demonstrated the group-scale three-tier API and the per-tier deflection composition, without invoking a non-linear search. The scaling relation collapses what would otherwise be N free `einstein_radius` -parameters into 2 shared parameters (`scaling_factor` and `scaling_exponent`), letting the model dimensionality -stay constant as galaxy count grows. +parameters into a single shared normalization (`einstein_radius_ref`, the brightest member's Einstein radius, +with the exponent fixed at 0.5), letting the model dimensionality stay constant as galaxy count grows. In a real modeling workflow: - - `modeling.py` runs the search-based version, where `scaling_factor` and `scaling_exponent` are free `af.Model` - parameters with `UniformPrior`s. + - `modeling.py` runs the search-based version, where `einstein_radius_ref` is a free `af.Model` parameter with + a `UniformPrior`. - `modeling_for_luminosities.py` is the standalone light-only fit that produces the luminosities consumed by the scaling relation. In production this stage is the `source_lp[0]` step of a SLaM pipeline. - `autolens_workspace/scripts/group/slam.py` is the full SLaM pipeline, which already implements scaling diff --git a/scripts/group/features/scaling_relation/likelihood_function.py b/scripts/group/features/scaling_relation/likelihood_function.py index 28928c5ce..850b8ac14 100644 --- a/scripts/group/features/scaling_relation/likelihood_function.py +++ b/scripts/group/features/scaling_relation/likelihood_function.py @@ -4,8 +4,8 @@ This script describes the additional steps required to compute the `log_likelihood` for a group-scale strong lens whose foreground galaxy population is split across three tiers — main lens galaxies (modelled via the group `lens_dict` API), individually-modelled extras (each with its own free `einstein_radius`), and -scaling-tier extras (whose Einstein radii are derived from a shared two-parameter relation -`einstein_radius = scaling_factor * luminosity ** scaling_exponent`). +scaling-tier extras (whose Einstein radii are derived from a shared reference-anchored relation +`einstein_radius = einstein_radius_ref * (luminosity / luminosity_ref) ** 0.5`, the Lenstool convention). This script does NOT repeat the steps shared with single-plane lensing (mask, image-plane grid, PSF convolution, chi-squared, noise normalization, linear-algebra solver for MGE source intensities). It documents only the part @@ -50,9 +50,9 @@ + sum_k alpha_extra_scaling_k(theta) where alpha_extra_scaling_k is the deflection of a mass profile whose - einstein_radius_k = scaling_factor * luminosity_k ** scaling_exponent. + einstein_radius_k = einstein_radius_ref * (luminosity_k / luminosity_ref) ** 0.5. -The model gains exactly 2 free parameters (`scaling_factor`, `scaling_exponent`) regardless of how many galaxies +The model gains exactly 1 free parameter (`einstein_radius_ref`) regardless of how many galaxies sit on the scaling tier. Every other step of the likelihood (PSF convolution, chi-squared, noise normalization, MGE linear-algebra solver) is unchanged. """ @@ -162,12 +162,15 @@ def build_source_basis(centre): for centre, er in zip(extra_galaxies_centres, individual_extras_einstein_radii) ] -scaling_factor = 0.3 -scaling_exponent = 1.0 +einstein_radius_ref = 0.135 +scaling_exponent = 0.5 +luminosity_ref = max(scaling_galaxies_luminosities) scaling_extras = [] for centre, luminosity in zip(scaling_galaxies_centres, scaling_galaxies_luminosities): - einstein_radius = scaling_factor * luminosity**scaling_exponent + einstein_radius = ( + einstein_radius_ref * (luminosity / luminosity_ref) ** scaling_exponent + ) scaling_extras.append( al.Galaxy( redshift=0.5, @@ -217,10 +220,10 @@ def build_source_basis(centre): print(f"alpha_total (across all, first coord): {alpha_total[0]}") for centre, luminosity in zip(scaling_galaxies_centres, scaling_galaxies_luminosities): - er = scaling_factor * luminosity**scaling_exponent + er = einstein_radius_ref * (luminosity / luminosity_ref) ** scaling_exponent print( f" scaling galaxy @ {tuple(centre)}: " - f"einstein_radius = {scaling_factor:.2f} * {luminosity:.3f} ** {scaling_exponent:.1f} = {er:.4f}" + f"einstein_radius = {einstein_radius_ref:.3f} * ({luminosity:.3f} / {luminosity_ref:.3f}) ** {scaling_exponent:.1f} = {er:.4f}" ) """ @@ -253,7 +256,7 @@ def build_source_basis(centre): 1. Computes `alpha_lens(theta) = sum_i alpha_main_lens_i + sum_j alpha_extra_individual_j + sum_k alpha_extra_scaling_k`. Each `alpha_extra_scaling_k` is the deflection of a profile whose `einstein_radius` was derived from - `scaling_factor * luminosity_k ** scaling_exponent`. + `einstein_radius_ref * (luminosity_k / luminosity_ref) ** 0.5`. 2. Ray-traces the image-plane grid to obtain `grid_source = grid - alpha_lens`. 3. Evaluates the source MGE at `grid_source`, producing its image-plane contribution. diff --git a/scripts/group/features/scaling_relation/modeling.py b/scripts/group/features/scaling_relation/modeling.py index 0f17940e3..657db6957 100644 --- a/scripts/group/features/scaling_relation/modeling.py +++ b/scripts/group/features/scaling_relation/modeling.py @@ -19,10 +19,12 @@ - **Scaling galaxies** (`scaling_galaxies_centres.json`): further-out, fainter companions whose Einstein radii are tied together via a shared scaling relation: - einstein_radius = scaling_factor * (luminosity ** scaling_exponent) + einstein_radius = einstein_radius_ref * (luminosity / luminosity_ref) ** 0.5 - The free parameters are `scaling_factor` and `scaling_exponent` only — adding more scaling galaxies does not grow - the model. Use this tier for the long tail of fainter companions. + anchored to a *reference galaxy* (the brightest scaling-tier member), with the exponent fixed at the + Faber-Jackson value of 0.5 — the convention used by Lenstool and standard in published group- and + cluster-scale analyses. The only free parameter is `einstein_radius_ref` — adding more scaling galaxies + does not grow the model. Use this tier for the long tail of fainter companions. Splitting galaxies across these three tiers is the standard pattern in production group fits (see `z_projects/euclid_group/scripts/group.py`). It gives the lensing-significant galaxies the model flexibility they need @@ -240,14 +242,26 @@ """ __Scaling Galaxies__ -The scaling-relation tier. The two relation parameters are defined ONCE outside the loop — every scaling galaxy's -mass is a function of these same two parameters plus its own (fixed) luminosity. +The scaling-relation tier, in the reference-anchored convention used by Lenstool and essentially every published +group- and cluster-scale analysis (Limousin et al. 2005; Eliasdottir et al. 2007; Bergamini et al. 2019). The +normalization ``einstein_radius_ref`` is the Einstein radius of the *brightest* scaling member — a physically +interpretable quantity with an easy-to-motivate prior range — and it is defined ONCE outside the loop: every +scaling galaxy's mass derives from it via its luminosity ratio to the reference. The exponent is *fixed* at the +Faber-Jackson value (einstein_radius ∝ sigma² and sigma ∝ L^(1/4) give einstein_radius ∝ L^(1/2)) rather than +fitted, avoiding the normalization-slope degeneracy. Only luminosity ratios enter, so the luminosity units are +irrelevant; magnitude catalogues convert via ``L / L_ref = 10 ** (0.4 * (m_ref - m))``. + +The dPIE-profile cluster-scale analogue — which also scales the truncation radius (``rs ∝ L^0.5``, mirroring +Lenstool's r_cut scaling) — is ``scripts/cluster/modeling.py``. To free the exponent as a systematics test, +replace the fixed value with e.g. ``af.UniformPrior(lower_limit=0.0, upper_limit=1.0)``. Adding more scaling galaxies (e.g. by lengthening the centres + luminosity lists) does not add any free parameters to the model. """ -scaling_factor = af.UniformPrior(lower_limit=0.0, upper_limit=0.5) -scaling_exponent = af.UniformPrior(lower_limit=0.0, upper_limit=2.0) +einstein_radius_ref = af.UniformPrior(lower_limit=0.0, upper_limit=0.5) +scaling_exponent = 0.5 + +luminosity_ref = max(scaling_galaxies_luminosity_list) scaling_galaxies_list = [] @@ -262,7 +276,8 @@ mass = af.Model(al.mp.Isothermal) mass.centre = tuple(scaling_galaxy_centre) - mass.einstein_radius = scaling_factor * scaling_galaxy_luminosity**scaling_exponent + luminosity_ratio = scaling_galaxy_luminosity / luminosity_ref + mass.einstein_radius = einstein_radius_ref * luminosity_ratio**scaling_exponent scaling_galaxy = af.Model(al.Galaxy, redshift=0.5, bulge=bulge, mass=mass) @@ -342,9 +357,8 @@ """ __Result__ -`result.info` shows all three tiers separately. The recovered `scaling_factor` and `scaling_exponent` should be -close to the truth values used by the simulator (0.3 and 1.0, given the simulator's chosen luminosities and Einstein -radii). +`result.info` shows all three tiers separately. The recovered `einstein_radius_ref` should be close to the truth +value used by the simulator (0.135, the Einstein radius of the brightest scaling member). """ print(result.info) diff --git a/scripts/group/features/scaling_relation/modeling_for_luminosities.py b/scripts/group/features/scaling_relation/modeling_for_luminosities.py index c41c13bc6..41d61fd0a 100644 --- a/scripts/group/features/scaling_relation/modeling_for_luminosities.py +++ b/scripts/group/features/scaling_relation/modeling_for_luminosities.py @@ -6,7 +6,7 @@ `scripts/imaging/features/scaling_relation/modeling.py`) need a measured **luminosity** for every galaxy that sits on the relation: - einstein_radius = scaling_factor * (luminosity ** scaling_exponent) + einstein_radius = einstein_radius_ref * (luminosity / luminosity_ref) ** 0.5 Those tutorials hardcode the luminosity list for readability. In a production fit the luminosities have to be measured from the data itself. This example shows the standard standalone way to do that: diff --git a/scripts/group/features/scaling_relation/simulator.py b/scripts/group/features/scaling_relation/simulator.py index be63c6fdb..74f72ddc4 100644 --- a/scripts/group/features/scaling_relation/simulator.py +++ b/scripts/group/features/scaling_relation/simulator.py @@ -131,10 +131,12 @@ """ __Scaling Galaxies__ -Two further-out, fainter companions whose true Einstein radii are consistent with the relation -``einstein_radius = 0.3 * luminosity ** 1.0`` (luminosities ~0.45 -> Einstein radii ~0.135). Modelling these -individually would add 2 free parameters; on a scaling relation they add zero (the 2 relation parameters are shared -across all scaling galaxies, so adding more does not grow the model). +Two further-out, fainter companions whose true Einstein radii are consistent with the reference-anchored +relation ``einstein_radius = einstein_radius_ref * (luminosity / luminosity_ref) ** 0.5`` with +``einstein_radius_ref = 0.135`` anchored to the brightest scaling member (``luminosity_ref = 0.45``; both members +share that luminosity here, so their radii are equal). Modelling these individually would add 2 free parameters; +on a scaling relation they add zero (the single free normalization is shared across all scaling galaxies, so +adding more does not grow the model). """ scaling_galaxy_0 = al.Galaxy( redshift=0.5, diff --git a/workspace_index.json b/workspace_index.json index 8799312a5..d610697ed 100644 --- a/workspace_index.json +++ b/workspace_index.json @@ -162,7 +162,9 @@ "autolens_workspace/scripts/group/features/scaling_relation/modeling.py", "autolens_workspace/start_here.py", "cluster/modeling.py", + "csv_api.py", "scripts/cluster/csv_api.py", + "scripts/cluster/modeling.py", "scripts/cluster/simulator.py", "scripts/group/features/scaling_relation/modeling_for_luminosities.py", "start_here_imaging.ipynb" @@ -1062,7 +1064,7 @@ ], "notebook": "notebooks/group/features/scaling_relation/likelihood_function.ipynb", "path": "scripts/group/features/scaling_relation/likelihood_function.py", - "summary": "This script describes the additional steps required to compute the `log_likelihood` for a group-scale strong lens whose foreground galaxy population is split across three tiers \u2014 main lens galaxies (modelled via the group `lens_dict` API), individually-modelled extras (each with its own free `einstein_radius`), and scaling-tier extras (whose Einstein radii are derived from a shared two-parameter relation `einstein_radius = scaling_factor * luminosity ** scaling_exponent`).", + "summary": "This script describes the additional steps required to compute the `log_likelihood` for a group-scale strong lens whose foreground galaxy population is split across three tiers \u2014 main lens galaxies (modelled via the group `lens_dict` API), individually-modelled extras (each with its own free `einstein_radius`), and scaling-tier extras (whose Einstein radii are derived from a shared reference-anchored relation `einstein_radius = einstein_radius_ref * (luminosity / luminosity_ref) ** 0.5`, the Lenstool convention).", "title": "__Log Likelihood Function: Group Scaling Relation__" }, { @@ -1084,6 +1086,7 @@ "autolens_workspace/scripts/group/slam.py", "autolens_workspace/scripts/imaging/features/scaling_relation/modeling.py", "group/modeling.py", + "scripts/cluster/modeling.py", "scripts/group/features/pixelization/slam.py", "scripts/group/features/scaling_relation/modeling_for_luminosities.py", "scripts/group/features/scaling_relation/simulator.py",