From aa45abdb7262f623f0e16acaaecfac4fee8ea9c1 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 21 Aug 2026 13:52:09 +0000 Subject: [PATCH] Add interferometer operated_light_profile example package Mirrors scripts/imaging/features/advanced/operated_light_profile for Interferometer datasets (PyAutoArray#459): simulator and modeling scripts fitting compact lens-galaxy nuclear emission with lp_operated / lp_linear_operated profiles, plus generated notebooks, catalogue and the advanced features README entry. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01Lv2LDovvSQydk8biNMSAPL --- llms-full.txt | 4 + .../features/advanced/README.md | 1 + .../advanced/operated_light_profile/README.md | 20 + .../operated_light_profile/modeling.ipynb | 436 ++++++++++++++++++ .../operated_light_profile/simulator.ipynb | 398 ++++++++++++++++ .../features/advanced/README.md | 1 + .../advanced/operated_light_profile/README.md | 20 + .../operated_light_profile/__init__.py | 0 .../operated_light_profile/modeling.py | 247 ++++++++++ .../operated_light_profile/simulator.py | 187 ++++++++ workspace_index.json | 43 ++ 11 files changed, 1357 insertions(+) create mode 100644 notebooks/interferometer/features/advanced/operated_light_profile/README.md create mode 100644 notebooks/interferometer/features/advanced/operated_light_profile/modeling.ipynb create mode 100644 notebooks/interferometer/features/advanced/operated_light_profile/simulator.ipynb create mode 100644 scripts/interferometer/features/advanced/operated_light_profile/README.md create mode 100644 scripts/interferometer/features/advanced/operated_light_profile/__init__.py create mode 100644 scripts/interferometer/features/advanced/operated_light_profile/modeling.py create mode 100644 scripts/interferometer/features/advanced/operated_light_profile/simulator.py diff --git a/llms-full.txt b/llms-full.txt index a6452feb6..fdcda60be 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -294,6 +294,10 @@ AUTO-GENERATED by PyAutoHands — do not edit by hand; regenerate with generate. - Contents: PyAutoLens Requirements, CASA in Five Steps, Step 1 — Split by Field / SPW, Step 2 — Channel Averaging, Step 3 — Continuum Subtraction, Step 4 — Rescale Sigmas, Step 5 — Export to FITS, Combining Spectral Windows, Polarisations, Building the Interferometer Object, Troubleshooting, SLACK - [Interferometer: Data Preparation](scripts/interferometer/data_preparation.py): When an interferometer dataset is analysed, it must conform to certain standards in order for the analysis to be performed correctly. This tutorial describes these standards and links to more detailed scripts which will help you prepare your dataset to adhere to them if it does not already. - Contents: SLACK, Pixel Scale, Visibilities, Noise-Map, UV Wavelengths, Real Space Mask, Data Processing Complete, Positions (Optional), Lens Light Centre (Optional), Extra Galaxies (Optional), Mask Extra Galaxies (Optional), Info (Optional) +- [Modeling Features: Operated Light Profiles (Interferometer)](scripts/interferometer/features/advanced/operated_light_profile/modeling.py): It is common for galaxies to have point-source emission, for example bright emission right at their centre due to an active galactic nuclei or a compact knot of star formation. + - Contents: Mask, Dataset, Dataset Auto-Simulation, Model, Search, Analysis, Run Time, Model-Fit, Result +- [Simulator: Light Operated (Interferometer)](scripts/interferometer/features/advanced/operated_light_profile/simulator.py): It is common for galaxies to have point-source emission, for example bright emission right at their centre due to an active galactic nuclei or a compact knot of star formation. + - Contents: Dataset Paths, Grid, uv-wavelengths, Simulator, Ray Tracing, Output, Visualize, Tracer json - [__Log Likelihood Function: Potential Correction (Interferometer)__](scripts/interferometer/features/advanced/potential_correction/likelihood_function.py): This script provides a step-by-step guide of the **PyAutoLens** visibility-space potential-correction `log_likelihood_function` (the Bayesian evidence of `al.pc.FitDpsiSrcInterferometer`), which jointly inverts `Interferometer` data for a pixelized source and pixelized corrections $\delta\psi$ to the lensing potential. - Contents: Simplifications, Prerequisites, Dataset, Source Inversion Blocks, Dpsi Mesh, Correction Response, Joint Response, Dense Route, Sparse (w-tilde) Route, Solve, Dkappa Map, Evidence Terms, Verification, JAX / xp, Wrap Up - [Feature: Potential Correction (Gravitational Imaging)](scripts/interferometer/features/advanced/potential_correction/start_here.py): This example performs gravitational imaging on interferometer data: pixelized corrections $\delta\psi$ to the lensing potential are reconstructed jointly with the pixelized source, revealing mass structure (e.g. dark subhaloes) the smooth lens model omits, as a convergence-correction map $\delta\kappa = \frac{1}{2}\nabla^2\delta\psi$. diff --git a/notebooks/interferometer/features/advanced/README.md b/notebooks/interferometer/features/advanced/README.md index 504d5c953..73ba7b31c 100644 --- a/notebooks/interferometer/features/advanced/README.md +++ b/notebooks/interferometer/features/advanced/README.md @@ -4,6 +4,7 @@ data using different **PyAutoLens** features. The scripts in this folder are advanced, and generally provide more niche functionality which will only be useful for specific scientific topics.The following example scripts illustrating lens modeling where: +- `operated_light_profile`: Compact point-source emission (e.g. an AGN) fitted with operated light profiles, whose image-plane shape is specified directly. - `shapelets`: The source (or lens) is reconstructed using shapelet basis functions. - `potential_correction`: Gravitational imaging — pixelized corrections to the lensing potential reconstructed jointly with the source. - `subhalo`: Fitting lens models for dark matter subhalo detection and sensitivity mapping. diff --git a/notebooks/interferometer/features/advanced/operated_light_profile/README.md b/notebooks/interferometer/features/advanced/operated_light_profile/README.md new file mode 100644 index 000000000..1e57e6c03 --- /dev/null +++ b/notebooks/interferometer/features/advanced/operated_light_profile/README.md @@ -0,0 +1,20 @@ +The `operated_light_profile` folder contains example scripts showing how to perform analysis of +`Interferometer` data using operated light profiles, which represent compact point-source emission (e.g. an +AGN) whose image-plane shape is specified directly. + +For interferometer data there is no PSF, so operated light profiles are Fourier transformed to the visibility +plane like every other light profile — the PSF-bypass behaviour of the imaging examples applies only where a +PSF exists. Using them keeps a lens model consistent across imaging and interferometer datasets. + +# Files + +The following example scripts illustrating lens modeling where: + +- `modeling`: Lens modeling of an `Interferometer` dataset using operated light profiles. +- `simulator`: Simulating interferometer data of a strong lens using operated light profiles. + +# Results + +These scripts only give a brief overview of how to analyse and interpret the results a lens model fit. + +A full guide to result analysis is given at `autolens_workspace/*/guides/results`. diff --git a/notebooks/interferometer/features/advanced/operated_light_profile/modeling.ipynb b/notebooks/interferometer/features/advanced/operated_light_profile/modeling.ipynb new file mode 100644 index 000000000..6333ff63b --- /dev/null +++ b/notebooks/interferometer/features/advanced/operated_light_profile/modeling.ipynb @@ -0,0 +1,436 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Modeling Features: Operated Light Profiles (Interferometer)\n", + "===========================================================\n", + "\n", + "It is common for galaxies to have point-source emission, for example bright emission right at their centre due\n", + "to an active galactic nuclei or a compact knot of star formation.\n", + "\n", + "For CCD imaging data this emission is blurred by the telescope's Point Spread Function, and the\n", + "`imaging/features/advanced/operated_light_profile` example explains how operated light profiles fit it by\n", + "assuming the profile has already been convolved with the PSF.\n", + "\n", + "Interferometer data has no PSF: the visibilities are the Fourier transform of the sky emission, and the\n", + "synthesized beam only enters when a dirty image is formed. An operated light profile therefore takes on a\n", + "simpler meaning \u2014 it is a light profile whose image-plane shape directly represents the compact emission,\n", + "with no convolution step to bypass. Its image is Fourier transformed to the visibility plane like every other\n", + "light profile.\n", + "\n", + "Using operated light profiles for this compact emission keeps a lens model consistent across datasets: the\n", + "same `lp_operated` / `lp_linear_operated` component fitted to imaging data can be fitted to interferometer\n", + "data of the same lens, with the PSF-bypass behaviour applying only where a PSF exists.\n", + "\n", + "__Advanced: Visibility-Space Overrides__\n", + "\n", + "Internally, linear operated light profiles use the inversion's `operated_mapping_matrix_override` API to\n", + "bypass PSF convolution for imaging data. Interferometer inversions also support this override for custom\n", + "linear objects: an override supplied to an interferometer inversion bypasses the NUFFT entirely and must\n", + "therefore be a complex matrix in visibility space, of shape [total_visibilities, params] (e.g. computed via\n", + "an analytic Fourier transform). See the `LinearObj.operated_mapping_matrix_override` docstring in PyAutoArray\n", + "for the full contract. The linear operated light profiles fitted in this example do not use an override for\n", + "interferometer data \u2014 their images are NUFFT'd like any other profile.\n", + "\n", + "__Model__\n", + "\n", + "This script fits an `Interferometer` dataset of a 'galaxy-scale' strong lens with a model where:\n", + "\n", + " - The lens galaxy's light is a linear `Sersic` bulge.\n", + " - The lens galaxy includes a linear operated `Gaussian` representing its compact nuclear emission.\n", + " - The lens galaxy's total mass distribution is an `Isothermal` and `ExternalShear`.\n", + " - The source galaxy's light is a linear `SersicCore`.\n", + "\n", + "__Fit__\n", + "\n", + "For operated light profiles, there is no `fit.py` example found for standard light profiles, linear light\n", + "profiles and other examples.\n", + "\n", + "This is done purely to keep the number of examples in the workspace manageable. To perform a fit with operated\n", + "light profiles, simply follow one of the other `interferometer/fit.py` examples and replace the light profiles\n", + "with operated light profiles using the API described below.\n", + "\n", + "__Start Here Notebook__\n", + "\n", + "If any code in this script is unclear, refer to the `interferometer/start_here.ipynb` notebook.\n", + "\n", + "__Imaging Equivalent__\n", + "\n", + "For the CCD-imaging version of this script, see\n", + "`autolens_workspace/*/imaging/features/advanced/operated_light_profile/modeling.py`.\n", + "\n", + "__Contents__\n", + "\n", + "- **Mask:** Define the `real_space_mask` which sets the grid the strong lens is evaluated on.\n", + "- **Dataset:** Loading the operated light profile interferometer dataset from FITS files.\n", + "- **Dataset Auto-Simulation:** Automatically simulating the dataset if it does not already exist.\n", + "- **Model:** Composing the lens model with a linear Sersic bulge and operated Gaussian point source.\n", + "- **Search:** Configuring the Nautilus nested sampling non-linear search.\n", + "- **Analysis:** Creating the AnalysisInterferometer object for likelihood evaluation.\n", + "- **Run Time:** Discussion of computational run times for operated light profiles.\n", + "- **Model-Fit:** Running the model-fit and monitoring output.\n", + "- **Result:** Inspecting the result object and best-fit model." + ] + }, + { + "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 autolens 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(\"autolens\")" + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "\n", + "from autolens import jax_wrapper # Sets JAX environment before other imports\n", + "\n", + "from autolens import setup_notebook; setup_notebook()\n", + "\n", + "from pathlib import Path\n", + "import autofit as af\n", + "import autolens as al\n", + "import autolens.plot as aplt" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Mask__\n", + "\n", + "We define the `real_space_mask` which defines the grid the image of the strong lens is evaluated on." + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "mask_radius = 3.0\n", + "\n", + "real_space_mask = al.Mask2D.circular(\n", + " shape_native=(256, 256),\n", + " pixel_scales=0.1,\n", + " radius=mask_radius,\n", + ")" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Dataset__\n", + "\n", + "Load and plot the strong lens `Interferometer` dataset `light_operated` from .fits files, using\n", + "`TransformerNUFFT` backed by `nufftax`." + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "dataset_name = \"light_operated\"\n", + "dataset_path = Path(\"dataset\") / \"interferometer\" / dataset_name" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Dataset Auto-Simulation__\n", + "\n", + "If the dataset does not already exist on your system, it will be created by running the corresponding\n", + "simulator script. This ensures that all example scripts can be run without manually simulating data first." + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "if al.util.dataset.should_simulate(str(dataset_path)):\n", + " import subprocess\n", + " import sys\n", + "\n", + " subprocess.run(\n", + " [\n", + " sys.executable,\n", + " \"scripts/interferometer/features/advanced/operated_light_profile/simulator.py\",\n", + " ],\n", + " check=True,\n", + " )\n", + "\n", + "dataset = al.Interferometer.from_fits(\n", + " data_path=dataset_path / \"data.fits\",\n", + " noise_map_path=dataset_path / \"noise_map.fits\",\n", + " uv_wavelengths_path=dataset_path / \"uv_wavelengths.fits\",\n", + " real_space_mask=real_space_mask,\n", + " transformer_class=al.TransformerNUFFT,\n", + ")\n", + "\n", + "aplt.subplot_interferometer_dirty_images(dataset=dataset)" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Model__\n", + "\n", + "We compose a lens model where:\n", + "\n", + " - The lens galaxy's light is a linear `Sersic` bulge [6 parameters].\n", + "\n", + " - The lens galaxy's point source emission is a linear operated `Gaussian` centred on the bulge [3 parameters].\n", + "\n", + " - The lens galaxy's total mass distribution is an `Isothermal` and `ExternalShear` [7 parameters].\n", + "\n", + " - The source galaxy's light is a linear `SersicCore` [6 parameters].\n", + "\n", + "The number of free parameters and therefore the dimensionality of non-linear parameter space is N=22.\n", + "\n", + "The prior on the operated `Gaussian`'s `sigma` value is very important, as it is often the case that this is a\n", + "very small value (e.g. ~0.1).\n", + "\n", + "By default, **PyAutoLens** assumes a `UniformPrior` from 0.0 to 5.0, but the scale of this value depends on\n", + "the resolution of the data. I therefore recommend you set it manually below, using your knowledge of the\n", + "compact emission's angular size.\n", + "\n", + "__Model Cookbook__\n", + "\n", + "A full description of model composition is provided by the model cookbook:\n", + "\n", + "https://pyautolens.readthedocs.io/en/latest/general/model_cookbook.html" + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "bulge = af.Model(al.lp_linear.Sersic)\n", + "psf = af.Model(al.lp_linear_operated.Gaussian)\n", + "\n", + "psf.sigma = af.UniformPrior(lower_limit=0.0, upper_limit=5.0)\n", + "\n", + "bulge.centre = psf.centre\n", + "\n", + "lens = af.Model(\n", + " al.Galaxy,\n", + " redshift=0.5,\n", + " bulge=bulge,\n", + " psf=psf,\n", + " mass=al.mp.Isothermal,\n", + " shear=al.mp.ExternalShear,\n", + ")\n", + "source = af.Model(al.Galaxy, redshift=1.0, bulge=al.lp_linear.SersicCore)\n", + "\n", + "model = af.Collection(galaxies=af.Collection(lens=lens, source=source))" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `info` attribute shows the model in a readable format." + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "print(model.info)" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Search__\n", + "\n", + "The model is fitted to the data using a non-linear search. In this example, we use the nested sampling\n", + "algorithm Nautilus (https://nautilus.readthedocs.io/en/latest/).\n", + "\n", + "A full description of the settings below is given in the beginner modeling scripts, if anything is unclear." + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "search = af.Nautilus(\n", + " path_prefix=Path(\"interferometer\") / \"features\",\n", + " name=\"operated_light_profiles\",\n", + " unique_tag=dataset_name,\n", + " n_live=100,\n", + " n_batch=20, # GPU lens model fits are batched and run simultaneously.\n", + " live_visual_update=False, # Set True to open a live matplotlib window (script) or refresh a Jupyter cell (notebook).\n", + ")" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Analysis__\n", + "\n", + "Create the `AnalysisInterferometer` object defining how the via Nautilus the model is fitted to the data." + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "analysis = al.AnalysisInterferometer(dataset=dataset, use_jax=True)" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Run Time__\n", + "\n", + "For interferometer data the likelihood evaluation time of an operated light profile is the same as that of an\n", + "ordinary light profile \u2014 both are evaluated in real space and NUFFT'd to the visibility plane (the PSF\n", + "convolution that operated profiles bypass for imaging data does not exist here).\n", + "\n", + "The overall run-time may be a little slower than a model without the point source component though, because\n", + "the `psf` component adds a few extra parameters.\n", + "\n", + "__Model-Fit__\n", + "\n", + "We begin the model-fit by passing the model and analysis object to the non-linear search (checkout the output\n", + "folder for on-the-fly visualization and results)." + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "result = search.fit(model=model, analysis=analysis)" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Result__\n", + "\n", + "The search returns a result object, which whose `info` attribute shows the result in a readable format:" + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "print(result.info)" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We plot the maximum likelihood fit, tracer images and posteriors inferred via Nautilus.\n", + "\n", + "The lens galaxy's bulge and compact nuclear emission appear similar to those in the data, confirming that the\n", + "`intensity` values inferred by the inversion process are accurate." + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "print(result.max_log_likelihood_instance)\n", + "\n", + "aplt.subplot_tracer(\n", + " tracer=result.max_log_likelihood_tracer, grid=result.grids.lp\n", + ")\n", + "\n", + "aplt.subplot_fit_interferometer(fit=result.max_log_likelihood_fit)" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Checkout `autolens_workspace/*/guides/results` for a full description of analysing results." + ] + } + ], + "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/notebooks/interferometer/features/advanced/operated_light_profile/simulator.ipynb b/notebooks/interferometer/features/advanced/operated_light_profile/simulator.ipynb new file mode 100644 index 000000000..bcde85365 --- /dev/null +++ b/notebooks/interferometer/features/advanced/operated_light_profile/simulator.ipynb @@ -0,0 +1,398 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Simulator: Light Operated (Interferometer)\n", + "==========================================\n", + "\n", + "It is common for galaxies to have point-source emission, for example bright emission right at their centre due\n", + "to an active galactic nuclei or a compact knot of star formation.\n", + "\n", + "For interferometer data there is no Point Spread Function: the visibilities are the Fourier transform of the\n", + "sky emission, and the synthesized beam only enters when a dirty image is formed. The operated `Gaussian` in\n", + "this script therefore represents compact nuclear emission whose image-plane shape is specified directly, and\n", + "it is Fourier transformed to the visibility plane like every other light profile.\n", + "\n", + "This script simulates an `Interferometer` dataset of a 'galaxy-scale' strong lens which has this point-source\n", + "emission in the centre of its lens galaxy.\n", + "\n", + "This dataset is used in `interferometer/features/advanced/operated_light_profile/modeling.py` to demonstrate\n", + "how to fit this point-source emission using an operated light profile.\n", + "\n", + "__Contents__\n", + "\n", + "- **Dataset Paths:** The `dataset_type` describes the type of data being simulated and `dataset_name` gives it a name.\n", + "- **Grid:** Real-space grid the strong lens image is evaluated on.\n", + "- **uv-wavelengths:** Load the uv baselines used to NUFFT the image to the visibility plane.\n", + "- **Simulator:** `SimulatorInterferometer` (no PSF; uv-plane noise instead of image-plane Poisson noise).\n", + "- **Ray Tracing:** Setup the lens galaxy's light, mass and source galaxy light for this simulated lens.\n", + "- **Output:** Output the simulated dataset to the dataset path as .fits files.\n", + "- **Visualize:** Output a subplot of the simulated dataset and the tracer's quantities to the dataset path.\n", + "- **Tracer json:** Save the `Tracer` in the dataset folder as a .json file.\n", + "\n", + "__Model__\n", + "\n", + "This script simulates `Interferometer` data of a 'galaxy-scale' strong lens where:\n", + "\n", + " - The lens galaxy's light profile is an `Sersic` bulge.\n", + " - The lens galaxy's total mass distribution is an `Isothermal` and `ExternalShear`.\n", + " - The lens galaxy has a point source of emission at its centre which is modeled as a operated `Gaussian`.\n", + " - The source galaxy's light is an `SersicCore`.\n", + "\n", + "__Start Here Notebook__\n", + "\n", + "If any code in this script is unclear, refer to the `interferometer/simulator.ipynb` notebook." + ] + }, + { + "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 autolens 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(\"autolens\")" + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "\n", + "from autolens import setup_notebook; setup_notebook()\n", + "\n", + "from pathlib import Path\n", + "import autolens as al\n", + "import autolens.plot as aplt" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Dataset Paths__\n", + "\n", + "The `dataset_type` describes the type of data being simulated and `dataset_name` gives it a descriptive name." + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "dataset_type = \"interferometer\"\n", + "dataset_name = \"light_operated\"\n", + "dataset_path = Path(\"dataset\", dataset_type, dataset_name)" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Grid__\n", + "\n", + "Simulate the image using a (y,x) grid. Over-sampling is an imaging-only technique and is not used for\n", + "interferometer data." + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "grid = al.Grid2D.uniform(shape_native=(256, 256), pixel_scales=0.1)" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__uv-wavelengths__\n", + "\n", + "To perform the Fourier transform we need the wavelengths of the baselines." + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "uv_wavelengths_path = Path(\"dataset\", dataset_type, \"uv_wavelengths\")\n", + "uv_wavelengths = al.ndarray_via_fits_from(\n", + " file_path=Path(uv_wavelengths_path, \"sma.fits\"), hdu=0\n", + ")" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Simulator__\n", + "\n", + "Create the simulator for the interferometer data, which defines the exposure time, visibility-plane\n", + "noise sigma, and transformer." + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "simulator = al.SimulatorInterferometer(\n", + " uv_wavelengths=uv_wavelengths,\n", + " exposure_time=300.0,\n", + " noise_sigma=1000.0,\n", + " transformer_class=al.TransformerDFT,\n", + ")" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Ray Tracing__\n", + "\n", + "Setup the lens galaxy's light (elliptical Sersic bulge + operated Gaussian point source), mass (Isothermal\n", + "and ExternalShear) and source galaxy light (cored elliptical Sersic) for this simulated lens." + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "lens_galaxy = al.Galaxy(\n", + " redshift=0.5,\n", + " bulge=al.lp.Sersic(\n", + " centre=(0.0, 0.0),\n", + " ell_comps=al.convert.ell_comps_from(axis_ratio=0.9, angle=45.0),\n", + " intensity=1.0,\n", + " effective_radius=0.6,\n", + " sersic_index=3.0,\n", + " ),\n", + " psf=al.lp_operated.Gaussian(\n", + " centre=(0.0, 0.0), ell_comps=(0.0, 0.0), intensity=100.0, sigma=0.1\n", + " ),\n", + " mass=al.mp.Isothermal(\n", + " centre=(0.0, 0.0),\n", + " einstein_radius=1.6,\n", + " ell_comps=al.convert.ell_comps_from(axis_ratio=0.9, angle=45.0),\n", + " ),\n", + " shear=al.mp.ExternalShear(gamma_1=0.05, gamma_2=0.05),\n", + ")\n", + "\n", + "source_galaxy = al.Galaxy(\n", + " redshift=1.0,\n", + " bulge=al.lp.SersicCore(\n", + " centre=(0.0, 0.0),\n", + " ell_comps=al.convert.ell_comps_from(axis_ratio=0.8, angle=60.0),\n", + " intensity=10.0,\n", + " effective_radius=1.0,\n", + " sersic_index=2.5,\n", + " ),\n", + ")" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Use these galaxies to setup a tracer, which will generate the image for the simulated interferometer dataset." + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "tracer = al.Tracer(galaxies=[lens_galaxy, source_galaxy])" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Lets look at the tracer`s image, this is the image we'll be simulating." + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "aplt.plot_array(array=tracer.image_2d_from(grid=grid), title=\"Image\")" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Pass the simulator the tracer, which creates the ray-traced image and NUFFTs it to visibilities." + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "dataset = simulator.via_tracer_from(tracer=tracer, grid=grid)" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Plot the simulated `Interferometer` dataset before outputting it to fits." + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "aplt.subplot_interferometer_dirty_images(dataset=dataset)" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Output__\n", + "\n", + "Output the simulated dataset to the dataset path as .fits files." + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "aplt.fits_interferometer(\n", + " dataset=dataset,\n", + " data_path=dataset_path / \"data.fits\",\n", + " noise_map_path=dataset_path / \"noise_map.fits\",\n", + " uv_wavelengths_path=dataset_path / \"uv_wavelengths.fits\",\n", + " overwrite=True,\n", + ")" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Visualize__\n", + "\n", + "Output a subplot of the simulated dataset and the tracer's quantities to the dataset path as .png files." + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "aplt.subplot_interferometer_dirty_images(\n", + " dataset=dataset, output_path=dataset_path, output_format=\"png\"\n", + ")\n", + "aplt.subplot_tracer(\n", + " tracer=tracer, grid=grid, output_path=dataset_path, output_format=\"png\"\n", + ")" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Tracer json__\n", + "\n", + "Save the `Tracer` in the dataset folder as a .json file, ensuring the true light profiles, mass profiles and\n", + "galaxies are safely stored and available to check how the dataset was simulated in the future.\n", + "\n", + "This can be loaded via the method `tracer = al.from_json()`." + ] + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "al.output_to_json(\n", + " obj=tracer,\n", + " file_path=Path(dataset_path, \"tracer.json\"),\n", + ")" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The dataset can be viewed in the folder `autolens_workspace/dataset/interferometer/light_operated`." + ] + } + ], + "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/interferometer/features/advanced/README.md b/scripts/interferometer/features/advanced/README.md index 504d5c953..73ba7b31c 100644 --- a/scripts/interferometer/features/advanced/README.md +++ b/scripts/interferometer/features/advanced/README.md @@ -4,6 +4,7 @@ data using different **PyAutoLens** features. The scripts in this folder are advanced, and generally provide more niche functionality which will only be useful for specific scientific topics.The following example scripts illustrating lens modeling where: +- `operated_light_profile`: Compact point-source emission (e.g. an AGN) fitted with operated light profiles, whose image-plane shape is specified directly. - `shapelets`: The source (or lens) is reconstructed using shapelet basis functions. - `potential_correction`: Gravitational imaging — pixelized corrections to the lensing potential reconstructed jointly with the source. - `subhalo`: Fitting lens models for dark matter subhalo detection and sensitivity mapping. diff --git a/scripts/interferometer/features/advanced/operated_light_profile/README.md b/scripts/interferometer/features/advanced/operated_light_profile/README.md new file mode 100644 index 000000000..1e57e6c03 --- /dev/null +++ b/scripts/interferometer/features/advanced/operated_light_profile/README.md @@ -0,0 +1,20 @@ +The `operated_light_profile` folder contains example scripts showing how to perform analysis of +`Interferometer` data using operated light profiles, which represent compact point-source emission (e.g. an +AGN) whose image-plane shape is specified directly. + +For interferometer data there is no PSF, so operated light profiles are Fourier transformed to the visibility +plane like every other light profile — the PSF-bypass behaviour of the imaging examples applies only where a +PSF exists. Using them keeps a lens model consistent across imaging and interferometer datasets. + +# Files + +The following example scripts illustrating lens modeling where: + +- `modeling`: Lens modeling of an `Interferometer` dataset using operated light profiles. +- `simulator`: Simulating interferometer data of a strong lens using operated light profiles. + +# Results + +These scripts only give a brief overview of how to analyse and interpret the results a lens model fit. + +A full guide to result analysis is given at `autolens_workspace/*/guides/results`. diff --git a/scripts/interferometer/features/advanced/operated_light_profile/__init__.py b/scripts/interferometer/features/advanced/operated_light_profile/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/scripts/interferometer/features/advanced/operated_light_profile/modeling.py b/scripts/interferometer/features/advanced/operated_light_profile/modeling.py new file mode 100644 index 000000000..600b40a3c --- /dev/null +++ b/scripts/interferometer/features/advanced/operated_light_profile/modeling.py @@ -0,0 +1,247 @@ +""" +Modeling Features: Operated Light Profiles (Interferometer) +=========================================================== + +It is common for galaxies to have point-source emission, for example bright emission right at their centre due +to an active galactic nuclei or a compact knot of star formation. + +For CCD imaging data this emission is blurred by the telescope's Point Spread Function, and the +`imaging/features/advanced/operated_light_profile` example explains how operated light profiles fit it by +assuming the profile has already been convolved with the PSF. + +Interferometer data has no PSF: the visibilities are the Fourier transform of the sky emission, and the +synthesized beam only enters when a dirty image is formed. An operated light profile therefore takes on a +simpler meaning — it is a light profile whose image-plane shape directly represents the compact emission, +with no convolution step to bypass. Its image is Fourier transformed to the visibility plane like every other +light profile. + +Using operated light profiles for this compact emission keeps a lens model consistent across datasets: the +same `lp_operated` / `lp_linear_operated` component fitted to imaging data can be fitted to interferometer +data of the same lens, with the PSF-bypass behaviour applying only where a PSF exists. + +__Advanced: Visibility-Space Overrides__ + +Internally, linear operated light profiles use the inversion's `operated_mapping_matrix_override` API to +bypass PSF convolution for imaging data. Interferometer inversions also support this override for custom +linear objects: an override supplied to an interferometer inversion bypasses the NUFFT entirely and must +therefore be a complex matrix in visibility space, of shape [total_visibilities, params] (e.g. computed via +an analytic Fourier transform). See the `LinearObj.operated_mapping_matrix_override` docstring in PyAutoArray +for the full contract. The linear operated light profiles fitted in this example do not use an override for +interferometer data — their images are NUFFT'd like any other profile. + +__Model__ + +This script fits an `Interferometer` dataset of a 'galaxy-scale' strong lens with a model where: + + - The lens galaxy's light is a linear `Sersic` bulge. + - The lens galaxy includes a linear operated `Gaussian` representing its compact nuclear emission. + - The lens galaxy's total mass distribution is an `Isothermal` and `ExternalShear`. + - The source galaxy's light is a linear `SersicCore`. + +__Fit__ + +For operated light profiles, there is no `fit.py` example found for standard light profiles, linear light +profiles and other examples. + +This is done purely to keep the number of examples in the workspace manageable. To perform a fit with operated +light profiles, simply follow one of the other `interferometer/fit.py` examples and replace the light profiles +with operated light profiles using the API described below. + +__Start Here Notebook__ + +If any code in this script is unclear, refer to the `interferometer/start_here.ipynb` notebook. + +__Imaging Equivalent__ + +For the CCD-imaging version of this script, see +`autolens_workspace/*/imaging/features/advanced/operated_light_profile/modeling.py`. + +__Contents__ + +- **Mask:** Define the `real_space_mask` which sets the grid the strong lens is evaluated on. +- **Dataset:** Loading the operated light profile interferometer dataset from FITS files. +- **Dataset Auto-Simulation:** Automatically simulating the dataset if it does not already exist. +- **Model:** Composing the lens model with a linear Sersic bulge and operated Gaussian point source. +- **Search:** Configuring the Nautilus nested sampling non-linear search. +- **Analysis:** Creating the AnalysisInterferometer object for likelihood evaluation. +- **Run Time:** Discussion of computational run times for operated light profiles. +- **Model-Fit:** Running the model-fit and monitoring output. +- **Result:** Inspecting the result object and best-fit model. +""" + +from autolens import jax_wrapper # Sets JAX environment before other imports + +# from autolens import setup_notebook; setup_notebook() + +from pathlib import Path +import autofit as af +import autolens as al +import autolens.plot as aplt + +""" +__Mask__ + +We define the `real_space_mask` which defines the grid the image of the strong lens is evaluated on. +""" +mask_radius = 3.0 + +real_space_mask = al.Mask2D.circular( + shape_native=(256, 256), + pixel_scales=0.1, + radius=mask_radius, +) + +""" +__Dataset__ + +Load and plot the strong lens `Interferometer` dataset `light_operated` from .fits files, using +`TransformerNUFFT` backed by `nufftax`. +""" +dataset_name = "light_operated" +dataset_path = Path("dataset") / "interferometer" / dataset_name + +""" +__Dataset Auto-Simulation__ + +If the dataset does not already exist on your system, it will be created by running the corresponding +simulator script. This ensures that all example scripts can be run without manually simulating data first. +""" +if al.util.dataset.should_simulate(str(dataset_path)): + import subprocess + import sys + + subprocess.run( + [ + sys.executable, + "scripts/interferometer/features/advanced/operated_light_profile/simulator.py", + ], + check=True, + ) + +dataset = al.Interferometer.from_fits( + data_path=dataset_path / "data.fits", + noise_map_path=dataset_path / "noise_map.fits", + uv_wavelengths_path=dataset_path / "uv_wavelengths.fits", + real_space_mask=real_space_mask, + transformer_class=al.TransformerNUFFT, +) + +aplt.subplot_interferometer_dirty_images(dataset=dataset) + +""" +__Model__ + +We compose a lens model where: + + - The lens galaxy's light is a linear `Sersic` bulge [6 parameters]. + + - The lens galaxy's point source emission is a linear operated `Gaussian` centred on the bulge [3 parameters]. + + - The lens galaxy's total mass distribution is an `Isothermal` and `ExternalShear` [7 parameters]. + + - The source galaxy's light is a linear `SersicCore` [6 parameters]. + +The number of free parameters and therefore the dimensionality of non-linear parameter space is N=22. + +The prior on the operated `Gaussian`'s `sigma` value is very important, as it is often the case that this is a +very small value (e.g. ~0.1). + +By default, **PyAutoLens** assumes a `UniformPrior` from 0.0 to 5.0, but the scale of this value depends on +the resolution of the data. I therefore recommend you set it manually below, using your knowledge of the +compact emission's angular size. + +__Model Cookbook__ + +A full description of model composition is provided by the model cookbook: + +https://pyautolens.readthedocs.io/en/latest/general/model_cookbook.html +""" +bulge = af.Model(al.lp_linear.Sersic) +psf = af.Model(al.lp_linear_operated.Gaussian) + +psf.sigma = af.UniformPrior(lower_limit=0.0, upper_limit=5.0) + +bulge.centre = psf.centre + +lens = af.Model( + al.Galaxy, + redshift=0.5, + bulge=bulge, + psf=psf, + mass=al.mp.Isothermal, + shear=al.mp.ExternalShear, +) +source = af.Model(al.Galaxy, redshift=1.0, bulge=al.lp_linear.SersicCore) + +model = af.Collection(galaxies=af.Collection(lens=lens, source=source)) + +""" +The `info` attribute shows the model in a readable format. +""" +print(model.info) + +""" +__Search__ + +The model is fitted to the data using a non-linear search. In this example, we use the nested sampling +algorithm Nautilus (https://nautilus.readthedocs.io/en/latest/). + +A full description of the settings below is given in the beginner modeling scripts, if anything is unclear. +""" +search = af.Nautilus( + path_prefix=Path("interferometer") / "features", + name="operated_light_profiles", + unique_tag=dataset_name, + n_live=100, + n_batch=20, # GPU lens model fits are batched and run simultaneously. + live_visual_update=False, # Set True to open a live matplotlib window (script) or refresh a Jupyter cell (notebook). +) + +""" +__Analysis__ + +Create the `AnalysisInterferometer` object defining how the via Nautilus the model is fitted to the data. +""" +analysis = al.AnalysisInterferometer(dataset=dataset, use_jax=True) + +""" +__Run Time__ + +For interferometer data the likelihood evaluation time of an operated light profile is the same as that of an +ordinary light profile — both are evaluated in real space and NUFFT'd to the visibility plane (the PSF +convolution that operated profiles bypass for imaging data does not exist here). + +The overall run-time may be a little slower than a model without the point source component though, because +the `psf` component adds a few extra parameters. + +__Model-Fit__ + +We begin the model-fit by passing the model and analysis object to the non-linear search (checkout the output +folder for on-the-fly visualization and results). +""" +result = search.fit(model=model, analysis=analysis) + +""" +__Result__ + +The search returns a result object, which whose `info` attribute shows the result in a readable format: +""" +print(result.info) + +""" +We plot the maximum likelihood fit, tracer images and posteriors inferred via Nautilus. + +The lens galaxy's bulge and compact nuclear emission appear similar to those in the data, confirming that the +`intensity` values inferred by the inversion process are accurate. +""" +print(result.max_log_likelihood_instance) + +aplt.subplot_tracer( + tracer=result.max_log_likelihood_tracer, grid=result.grids.lp +) + +aplt.subplot_fit_interferometer(fit=result.max_log_likelihood_fit) + +""" +Checkout `autolens_workspace/*/guides/results` for a full description of analysing results. +""" diff --git a/scripts/interferometer/features/advanced/operated_light_profile/simulator.py b/scripts/interferometer/features/advanced/operated_light_profile/simulator.py new file mode 100644 index 000000000..03064498c --- /dev/null +++ b/scripts/interferometer/features/advanced/operated_light_profile/simulator.py @@ -0,0 +1,187 @@ +""" +Simulator: Light Operated (Interferometer) +========================================== + +It is common for galaxies to have point-source emission, for example bright emission right at their centre due +to an active galactic nuclei or a compact knot of star formation. + +For interferometer data there is no Point Spread Function: the visibilities are the Fourier transform of the +sky emission, and the synthesized beam only enters when a dirty image is formed. The operated `Gaussian` in +this script therefore represents compact nuclear emission whose image-plane shape is specified directly, and +it is Fourier transformed to the visibility plane like every other light profile. + +This script simulates an `Interferometer` dataset of a 'galaxy-scale' strong lens which has this point-source +emission in the centre of its lens galaxy. + +This dataset is used in `interferometer/features/advanced/operated_light_profile/modeling.py` to demonstrate +how to fit this point-source emission using an operated light profile. + +__Contents__ + +- **Dataset Paths:** The `dataset_type` describes the type of data being simulated and `dataset_name` gives it a name. +- **Grid:** Real-space grid the strong lens image is evaluated on. +- **uv-wavelengths:** Load the uv baselines used to NUFFT the image to the visibility plane. +- **Simulator:** `SimulatorInterferometer` (no PSF; uv-plane noise instead of image-plane Poisson noise). +- **Ray Tracing:** Setup the lens galaxy's light, mass and source galaxy light for this simulated lens. +- **Output:** Output the simulated dataset to the dataset path as .fits files. +- **Visualize:** Output a subplot of the simulated dataset and the tracer's quantities to the dataset path. +- **Tracer json:** Save the `Tracer` in the dataset folder as a .json file. + +__Model__ + +This script simulates `Interferometer` data of a 'galaxy-scale' strong lens where: + + - The lens galaxy's light profile is an `Sersic` bulge. + - The lens galaxy's total mass distribution is an `Isothermal` and `ExternalShear`. + - The lens galaxy has a point source of emission at its centre which is modeled as a operated `Gaussian`. + - The source galaxy's light is an `SersicCore`. + +__Start Here Notebook__ + +If any code in this script is unclear, refer to the `interferometer/simulator.ipynb` notebook. +""" + +# from autolens import setup_notebook; setup_notebook() + +from pathlib import Path +import autolens as al +import autolens.plot as aplt + +""" +__Dataset Paths__ + +The `dataset_type` describes the type of data being simulated and `dataset_name` gives it a descriptive name. +""" +dataset_type = "interferometer" +dataset_name = "light_operated" +dataset_path = Path("dataset", dataset_type, dataset_name) + +""" +__Grid__ + +Simulate the image using a (y,x) grid. Over-sampling is an imaging-only technique and is not used for +interferometer data. +""" +grid = al.Grid2D.uniform(shape_native=(256, 256), pixel_scales=0.1) + +""" +__uv-wavelengths__ + +To perform the Fourier transform we need the wavelengths of the baselines. +""" +uv_wavelengths_path = Path("dataset", dataset_type, "uv_wavelengths") +uv_wavelengths = al.ndarray_via_fits_from( + file_path=Path(uv_wavelengths_path, "sma.fits"), hdu=0 +) + +""" +__Simulator__ + +Create the simulator for the interferometer data, which defines the exposure time, visibility-plane +noise sigma, and transformer. +""" +simulator = al.SimulatorInterferometer( + uv_wavelengths=uv_wavelengths, + exposure_time=300.0, + noise_sigma=1000.0, + transformer_class=al.TransformerDFT, +) + +""" +__Ray Tracing__ + +Setup the lens galaxy's light (elliptical Sersic bulge + operated Gaussian point source), mass (Isothermal +and ExternalShear) and source galaxy light (cored elliptical Sersic) for this simulated lens. +""" +lens_galaxy = al.Galaxy( + redshift=0.5, + bulge=al.lp.Sersic( + centre=(0.0, 0.0), + ell_comps=al.convert.ell_comps_from(axis_ratio=0.9, angle=45.0), + intensity=1.0, + effective_radius=0.6, + sersic_index=3.0, + ), + psf=al.lp_operated.Gaussian( + centre=(0.0, 0.0), ell_comps=(0.0, 0.0), intensity=100.0, sigma=0.1 + ), + mass=al.mp.Isothermal( + centre=(0.0, 0.0), + einstein_radius=1.6, + ell_comps=al.convert.ell_comps_from(axis_ratio=0.9, angle=45.0), + ), + shear=al.mp.ExternalShear(gamma_1=0.05, gamma_2=0.05), +) + +source_galaxy = al.Galaxy( + redshift=1.0, + bulge=al.lp.SersicCore( + centre=(0.0, 0.0), + ell_comps=al.convert.ell_comps_from(axis_ratio=0.8, angle=60.0), + intensity=10.0, + effective_radius=1.0, + sersic_index=2.5, + ), +) + +""" +Use these galaxies to setup a tracer, which will generate the image for the simulated interferometer dataset. +""" +tracer = al.Tracer(galaxies=[lens_galaxy, source_galaxy]) + +""" +Lets look at the tracer`s image, this is the image we'll be simulating. +""" +aplt.plot_array(array=tracer.image_2d_from(grid=grid), title="Image") + +""" +Pass the simulator the tracer, which creates the ray-traced image and NUFFTs it to visibilities. +""" +dataset = simulator.via_tracer_from(tracer=tracer, grid=grid) + +""" +Plot the simulated `Interferometer` dataset before outputting it to fits. +""" +aplt.subplot_interferometer_dirty_images(dataset=dataset) + +""" +__Output__ + +Output the simulated dataset to the dataset path as .fits files. +""" +aplt.fits_interferometer( + dataset=dataset, + data_path=dataset_path / "data.fits", + noise_map_path=dataset_path / "noise_map.fits", + uv_wavelengths_path=dataset_path / "uv_wavelengths.fits", + overwrite=True, +) + +""" +__Visualize__ + +Output a subplot of the simulated dataset and the tracer's quantities to the dataset path as .png files. +""" +aplt.subplot_interferometer_dirty_images( + dataset=dataset, output_path=dataset_path, output_format="png" +) +aplt.subplot_tracer( + tracer=tracer, grid=grid, output_path=dataset_path, output_format="png" +) + +""" +__Tracer json__ + +Save the `Tracer` in the dataset folder as a .json file, ensuring the true light profiles, mass profiles and +galaxies are safely stored and available to check how the dataset was simulated in the future. + +This can be loaded via the method `tracer = al.from_json()`. +""" +al.output_to_json( + obj=tracer, + file_path=Path(dataset_path, "tracer.json"), +) + +""" +The dataset can be viewed in the folder `autolens_workspace/dataset/interferometer/light_operated`. +""" diff --git a/workspace_index.json b/workspace_index.json index 42d6dc099..82650c8e7 100644 --- a/workspace_index.json +++ b/workspace_index.json @@ -4514,6 +4514,49 @@ "summary": "When an interferometer dataset is analysed, it must conform to certain standards in order for the analysis to be performed correctly. This tutorial describes these standards and links to more detailed scripts which will help you prepare your dataset to adhere to them if it does not already.", "title": "Interferometer: Data Preparation" }, + { + "contents": [ + "Mask", + "Dataset", + "Dataset Auto-Simulation", + "Model", + "Search", + "Analysis", + "Run Time", + "Model-Fit", + "Result" + ], + "cross_refs": [ + "/imaging/features/advanced/operated_light_profile/modeling.py", + "fit.py", + "interferometer/fit.py", + "interferometer/start_here.ipynb" + ], + "notebook": "notebooks/interferometer/features/advanced/operated_light_profile/modeling.ipynb", + "path": "scripts/interferometer/features/advanced/operated_light_profile/modeling.py", + "summary": "It is common for galaxies to have point-source emission, for example bright emission right at their centre due to an active galactic nuclei or a compact knot of star formation.", + "title": "Modeling Features: Operated Light Profiles (Interferometer)" + }, + { + "contents": [ + "Dataset Paths", + "Grid", + "uv-wavelengths", + "Simulator", + "Ray Tracing", + "Output", + "Visualize", + "Tracer json" + ], + "cross_refs": [ + "interferometer/features/advanced/operated_light_profile/modeling.py", + "interferometer/simulator.ipynb" + ], + "notebook": "notebooks/interferometer/features/advanced/operated_light_profile/simulator.ipynb", + "path": "scripts/interferometer/features/advanced/operated_light_profile/simulator.py", + "summary": "It is common for galaxies to have point-source emission, for example bright emission right at their centre due to an active galactic nuclei or a compact knot of star formation.", + "title": "Simulator: Light Operated (Interferometer)" + }, { "contents": [ "Simplifications",