diff --git a/llms-full.txt b/llms-full.txt index 0257007a..02342769 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -69,6 +69,8 @@ AUTO-GENERATED by PyAutoHands — do not edit by hand; regenerate with generate. - Contents: Setup, Fit Imaging, Reconstruction, Inversion Plots, Mapper, Mesh Grids - [Simulator: Clumpy Galaxy](scripts/imaging/features/pixelization/simulator.py): This script simulates `Imaging` of a galaxy whose light has two distinct components: - Contents: Dataset Paths, Grid, Over Sampling, Galaxies, Output, Visualize, Mask Extra Galaxies, Plane Output +- [Simulator: Point Source](scripts/imaging/features/point_source/simulator.py): This script simulates an `Imaging` dataset containing a galaxy with: + - Contents: Dataset Paths, Grid, PSF, Galaxies, Simulation, Output - [Modeling Features: Shapelets](scripts/imaging/features/shapelets/fit.py): A shapelet is a basis function that is appropriate for capturing the exponential / disk-like features of a galaxy. It has been employed in galaxy structure studies to model the light of the galaxy, because it can represent features of disky star forming galaxies that a single Sersic function cannot. - Contents: Advantages & Disadvantages, Dataset & Mask, Basis, Coefficients, Linear Light Profiles, Fit, Intensities, Model, Search & Analysis, Run Time, Model-Fit, Result, Cartesian Shapelets - [Modeling Features: Shapelets](scripts/imaging/features/shapelets/modeling.py): A shapelet is a basis function that is appropriate for capturing the exponential / disk-like features of a galaxy. It has been employed in galaxy structure studies to model the light of the galaxy, because it can represent features of disky star forming galaxies that a single Sersic function cannot. diff --git a/notebooks/imaging/features/point_source/simulator.ipynb b/notebooks/imaging/features/point_source/simulator.ipynb new file mode 100644 index 00000000..28bf079c --- /dev/null +++ b/notebooks/imaging/features/point_source/simulator.ipynb @@ -0,0 +1,331 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Simulator: Point Source\n", + "=======================\n", + "\n", + "This script simulates an `Imaging` dataset containing a galaxy with:\n", + "\n", + " - An extended `Sersic` host galaxy.\n", + " - An unresolved point source represented by `ag.lp.PointSource`.\n", + "\n", + "A point source is a delta function in the image plane. Its `intensity` is the\n", + "total flux of the source, not a surface-brightness value evaluated independently\n", + "at every grid coordinate.\n", + "\n", + "Accurate point-source imaging requires an over-sampled PSF. The source is placed\n", + "on the nearest sub-pixel, convolved with the PSF at that finer resolution and\n", + "then binned back to the detector pixels while conserving total flux. Setting\n", + "`convolve_over_sample_size=1` remains a valid pixel-centred approximation, but\n", + "does not retain the source's sub-pixel position.\n", + "\n", + "For model fitting, `ag.lp_linear.PointSource` provides the same spatial model\n", + "with its total flux solved by the linear inversion. The source centre remains a\n", + "non-linear parameter.\n", + "\n", + "__Contents__\n", + "\n", + "- **Dataset Paths:** Define where the simulated dataset is written.\n", + "- **Grid:** Create a detector grid with sampling compatible with the PSF.\n", + "- **PSF:** Define a Gaussian PSF sampled at twice the detector resolution.\n", + "- **Galaxies:** Combine an extended host and unresolved point source.\n", + "- **Simulation:** Simulate noisy imaging using fine-grid PSF convolution.\n", + "- **Output:** Write the dataset, plots and input galaxies to disk." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Google Colab Setup__\n", + "\n", + "This cell sets up the environment when the notebook is run on Google Colab: it installs the\n", + "required PyAuto packages, clones the workspace (configuration files and example datasets) and\n", + "points the configuration at it. If you are running the notebook elsewhere (e.g. locally via\n", + "your own installation) it does nothing, and you can run it safely.\n", + "\n", + "Colab tip: model-fits run much faster on a GPU \u2014 enable one via \"Runtime\" -> \"Change runtime\n", + "type\" -> \"Hardware accelerator\" before running the notebook." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " import google.colab\n", + "except ImportError:\n", + " from autogalaxy import setup_colab as _setup_colab\n", + "else:\n", + " import importlib\n", + " import subprocess\n", + " import sys\n", + "\n", + " subprocess.check_call(\n", + " [sys.executable, \"-m\", \"pip\", \"install\", \"autonerves\", \"--no-deps\"]\n", + " )\n", + " _setup_colab = importlib.import_module(\"autonerves.setup_colab\")\n", + "\n", + "_setup_colab.setup(\"autogalaxy\")" + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "\n", + "from autogalaxy import setup_notebook; setup_notebook()\n", + "\n", + "from pathlib import Path\n", + "\n", + "import autogalaxy as ag\n", + "import autogalaxy.plot as aplt" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Dataset Paths__\n", + "\n", + "The simulated data are written to `dataset/imaging/point_source`." + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "dataset_path = Path(\"dataset\", \"imaging\", \"point_source\")" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Grid__\n", + "\n", + "The detector pixels are 0.1 arcseconds across. A uniform over-sample size of 2\n", + "gives four sub-pixels per detector pixel and is compatible with the PSF\n", + "convolution factor defined below." + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "over_sample_size = 2\n", + "\n", + "grid = ag.Grid2D.uniform(\n", + " shape_native=(101, 101),\n", + " pixel_scales=0.1,\n", + " over_sample_size=over_sample_size,\n", + ")" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__PSF__\n", + "\n", + "The PSF kernel is sampled on pixels that are twice as fine as the detector\n", + "pixels. Therefore its `pixel_scales` are `0.1 / 2 = 0.05` arcseconds and its\n", + "`convolve_over_sample_size` is 2.\n", + "\n", + "During simulation PyAutoGalaxy evaluates the point source on this fine grid,\n", + "convolves it with the fine PSF and bins the result back to the 0.1 arcsecond\n", + "detector grid." + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "psf = ag.Convolver.from_gaussian(\n", + " shape_native=(21, 21),\n", + " sigma=0.12,\n", + " pixel_scales=grid.pixel_scales[0] / over_sample_size,\n", + " normalize=True,\n", + " convolve_over_sample_size=over_sample_size,\n", + ")\n", + "\n", + "simulator = ag.SimulatorImaging(\n", + " exposure_time=300.0,\n", + " psf=psf,\n", + " background_sky_level=0.1,\n", + " add_poisson_noise_to_data=True,\n", + " noise_seed=1,\n", + ")" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Galaxies__\n", + "\n", + "The point source is offset by a quarter of a detector pixel in each direction.\n", + "At over-sample size 2 this selects one of the central pixel's four sub-pixels,\n", + "so the blurred image retains the offset instead of forcing the source onto the\n", + "detector-pixel centre.\n", + "\n", + "The point source `intensity=25.0` is its total flux before noise and background\n", + "sky are added. It is conserved when the fine-grid image is binned back to the\n", + "detector resolution." + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "point_source_centre = (0.025, -0.025)\n", + "\n", + "galaxy = ag.Galaxy(\n", + " redshift=0.5,\n", + " host=ag.lp.Sersic(\n", + " centre=(0.0, 0.0),\n", + " ell_comps=ag.convert.ell_comps_from(axis_ratio=0.8, angle=45.0),\n", + " intensity=0.5,\n", + " effective_radius=0.8,\n", + " sersic_index=2.0,\n", + " ),\n", + " point_source=ag.lp.PointSource(\n", + " centre=point_source_centre,\n", + " intensity=25.0,\n", + " ),\n", + ")\n", + "\n", + "galaxies = ag.Galaxies(galaxies=[galaxy])" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Simulation__\n", + "\n", + "Simulate the dataset. The simulator automatically uses the over-sampled PSF\n", + "path because `convolve_over_sample_size=2`." + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "dataset = simulator.via_galaxies_from(galaxies=galaxies, grid=grid)\n", + "\n", + "aplt.subplot_imaging_dataset(dataset=dataset)" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Output__\n", + "\n", + "Write the image, PSF and noise map as FITS files." + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "aplt.fits_imaging(\n", + " dataset=dataset,\n", + " data_path=dataset_path / \"data.fits\",\n", + " psf_path=dataset_path / \"psf.fits\",\n", + " noise_map_path=dataset_path / \"noise_map.fits\",\n", + " overwrite=True,\n", + ")" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Write PNG visualizations of the simulated dataset and its input galaxy." + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "aplt.subplot_imaging_dataset(\n", + " dataset=dataset,\n", + " output_path=dataset_path,\n", + " output_format=\"png\",\n", + ")\n", + "aplt.subplot_galaxies(\n", + " galaxies=galaxies,\n", + " grid=grid,\n", + " output_path=dataset_path,\n", + " output_format=\"png\",\n", + ")" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Save the exact input galaxy as JSON for reproducibility." + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "ag.output_to_json(\n", + " obj=galaxies,\n", + " file_path=dataset_path / \"galaxies.json\",\n", + ")\n", + "\n", + "print(f\"Point-source dataset written to {dataset_path}\")\n" + ], + "outputs": [], + "execution_count": null + } + ], + "metadata": { + "anaconda-cloud": {}, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.1" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} \ No newline at end of file diff --git a/scripts/imaging/features/point_source/simulator.py b/scripts/imaging/features/point_source/simulator.py new file mode 100644 index 00000000..9ae2013c --- /dev/null +++ b/scripts/imaging/features/point_source/simulator.py @@ -0,0 +1,167 @@ +""" +Simulator: Point Source +======================= + +This script simulates an `Imaging` dataset containing a galaxy with: + + - An extended `Sersic` host galaxy. + - An unresolved point source represented by `ag.lp.PointSource`. + +A point source is a delta function in the image plane. Its `intensity` is the +total flux of the source, not a surface-brightness value evaluated independently +at every grid coordinate. + +Accurate point-source imaging requires an over-sampled PSF. The source is placed +on the nearest sub-pixel, convolved with the PSF at that finer resolution and +then binned back to the detector pixels while conserving total flux. Setting +`convolve_over_sample_size=1` remains a valid pixel-centred approximation, but +does not retain the source's sub-pixel position. + +For model fitting, `ag.lp_linear.PointSource` provides the same spatial model +with its total flux solved by the linear inversion. The source centre remains a +non-linear parameter. + +__Contents__ + +- **Dataset Paths:** Define where the simulated dataset is written. +- **Grid:** Create a detector grid with sampling compatible with the PSF. +- **PSF:** Define a Gaussian PSF sampled at twice the detector resolution. +- **Galaxies:** Combine an extended host and unresolved point source. +- **Simulation:** Simulate noisy imaging using fine-grid PSF convolution. +- **Output:** Write the dataset, plots and input galaxies to disk. +""" + +# from autogalaxy import setup_notebook; setup_notebook() + +from pathlib import Path + +import autogalaxy as ag +import autogalaxy.plot as aplt + +""" +__Dataset Paths__ + +The simulated data are written to `dataset/imaging/point_source`. +""" +dataset_path = Path("dataset", "imaging", "point_source") + +""" +__Grid__ + +The detector pixels are 0.1 arcseconds across. A uniform over-sample size of 2 +gives four sub-pixels per detector pixel and is compatible with the PSF +convolution factor defined below. +""" +over_sample_size = 2 + +grid = ag.Grid2D.uniform( + shape_native=(101, 101), + pixel_scales=0.1, + over_sample_size=over_sample_size, +) + +""" +__PSF__ + +The PSF kernel is sampled on pixels that are twice as fine as the detector +pixels. Therefore its `pixel_scales` are `0.1 / 2 = 0.05` arcseconds and its +`convolve_over_sample_size` is 2. + +During simulation PyAutoGalaxy evaluates the point source on this fine grid, +convolves it with the fine PSF and bins the result back to the 0.1 arcsecond +detector grid. +""" +psf = ag.Convolver.from_gaussian( + shape_native=(21, 21), + sigma=0.12, + pixel_scales=grid.pixel_scales[0] / over_sample_size, + normalize=True, + convolve_over_sample_size=over_sample_size, +) + +simulator = ag.SimulatorImaging( + exposure_time=300.0, + psf=psf, + background_sky_level=0.1, + add_poisson_noise_to_data=True, + noise_seed=1, +) + +""" +__Galaxies__ + +The point source is offset by a quarter of a detector pixel in each direction. +At over-sample size 2 this selects one of the central pixel's four sub-pixels, +so the blurred image retains the offset instead of forcing the source onto the +detector-pixel centre. + +The point source `intensity=25.0` is its total flux before noise and background +sky are added. It is conserved when the fine-grid image is binned back to the +detector resolution. +""" +point_source_centre = (0.025, -0.025) + +galaxy = ag.Galaxy( + redshift=0.5, + host=ag.lp.Sersic( + centre=(0.0, 0.0), + ell_comps=ag.convert.ell_comps_from(axis_ratio=0.8, angle=45.0), + intensity=0.5, + effective_radius=0.8, + sersic_index=2.0, + ), + point_source=ag.lp.PointSource( + centre=point_source_centre, + intensity=25.0, + ), +) + +galaxies = ag.Galaxies(galaxies=[galaxy]) + +""" +__Simulation__ + +Simulate the dataset. The simulator automatically uses the over-sampled PSF +path because `convolve_over_sample_size=2`. +""" +dataset = simulator.via_galaxies_from(galaxies=galaxies, grid=grid) + +aplt.subplot_imaging_dataset(dataset=dataset) + +""" +__Output__ + +Write the image, PSF and noise map as FITS files. +""" +aplt.fits_imaging( + dataset=dataset, + data_path=dataset_path / "data.fits", + psf_path=dataset_path / "psf.fits", + noise_map_path=dataset_path / "noise_map.fits", + overwrite=True, +) + +""" +Write PNG visualizations of the simulated dataset and its input galaxy. +""" +aplt.subplot_imaging_dataset( + dataset=dataset, + output_path=dataset_path, + output_format="png", +) +aplt.subplot_galaxies( + galaxies=galaxies, + grid=grid, + output_path=dataset_path, + output_format="png", +) + +""" +Save the exact input galaxy as JSON for reproducibility. +""" +ag.output_to_json( + obj=galaxies, + file_path=dataset_path / "galaxies.json", +) + +print(f"Point-source dataset written to {dataset_path}") diff --git a/smoke_tests.txt b/smoke_tests.txt index f8481de0..2dcc00a4 100644 --- a/smoke_tests.txt +++ b/smoke_tests.txt @@ -13,3 +13,4 @@ guides/galaxies.py guides/using_jax.py guides/modeling/cookbook.py imaging/simulator.py +imaging/features/point_source/simulator.py diff --git a/workspace_index.json b/workspace_index.json index 7731b8b7..3b704653 100644 --- a/workspace_index.json +++ b/workspace_index.json @@ -1564,6 +1564,21 @@ "summary": "This script simulates `Imaging` of a galaxy whose light has two distinct components:", "title": "Simulator: Clumpy Galaxy" }, + { + "contents": [ + "Dataset Paths", + "Grid", + "PSF", + "Galaxies", + "Simulation", + "Output" + ], + "cross_refs": [], + "notebook": "notebooks/imaging/features/point_source/simulator.ipynb", + "path": "scripts/imaging/features/point_source/simulator.py", + "summary": "This script simulates an `Imaging` dataset containing a galaxy with:", + "title": "Simulator: Point Source" + }, { "contents": [ "Advantages & Disadvantages",