diff --git a/.github/workflows/navigator_check.yml b/.github/workflows/navigator_check.yml new file mode 100644 index 0000000..f8a28d4 --- /dev/null +++ b/.github/workflows/navigator_check.yml @@ -0,0 +1,17 @@ +name: Navigator Check + +# Thin caller for PyAutoBuild's reusable navigator-catalogue check. The check +# logic (path/banner lint + catalogue staleness) and its entrypoints live in +# PyAutoBuild/autobuild; this repo only declares which generator project to run. +# See PyAutoLabs/PyAutoBuild/.github/workflows/navigator_check.yml. + +on: [push, pull_request] + +permissions: + contents: read + +jobs: + navigator: + uses: PyAutoLabs/PyAutoBuild/.github/workflows/navigator_check.yml@main + with: + project: howtolens diff --git a/.navigator_check_ignore b/.navigator_check_ignore new file mode 100644 index 0000000..5130bb7 --- /dev/null +++ b/.navigator_check_ignore @@ -0,0 +1,9 @@ +# Paths exempt from the navigator path/banner checks (see +# PyAutoBuild/autobuild/check_navigator.py). One path or glob per line. +# +# CLAUDE.md's "Relationship to autolens_workspace" section mentions sibling +# autolens_workspace scripts in prose (as the next destination after a concept). +# Those paths live in the separate autolens_workspace repo, not here, so the +# path-existence check would flag them as missing. Exempt them. +scripts/imaging/modeling.py +scripts/guides/ diff --git a/llms-full.txt b/llms-full.txt new file mode 100644 index 0000000..c4f1d29 --- /dev/null +++ b/llms-full.txt @@ -0,0 +1,104 @@ +AUTO-GENERATED by PyAutoBuild — do not edit by hand; regenerate with generate.py. + +# HowToLens Lectures — Full Catalogue + +> Complete, generated listing of every script (and its matching notebook) in this +> workspace, grouped by top-level `scripts/` folder. This is the expanded companion to +> the curated `llms.txt` routing layer. Each entry links the script's title to its path +> and gives the first line of its docstring; `Contents:` lists the sections within. + +## chapter_1_introduction + +- [Tutorial 0: Visualization](scripts/chapter_1_introduction/tutorial_0_visualization.py): In this tutorial, we quickly cover visualization in **PyAutoLens** and make sure images display clearly in your Jupyter notebook and on your computer screen. + - Contents: Directories, Dataset, Subplots, Plot Customization, Overlays, Wrap Up +- [HowToLens: Introduction](scripts/chapter_1_introduction/tutorial_1_grids_and_galaxies.py): A strong gravitational lens is a system where two (or more) galaxies align perfectly down our line of sight from Earth such that the foreground galaxy's mass curves space-time in on itself, such that the light of a background source galaxy is deflected and magnified. This means we can see the background source galaxy multiple times, as multiple arcs or rings, because multiple paths through the foreground galaxy's mass are taken by the source's light. + - Contents: Grids, Geometry, Light Profiles, One Dimension Projection +- [Tutorial 2: Ray Tracing](scripts/chapter_1_introduction/tutorial_2_ray_tracing.py): Strong gravitational lensing occurs when the mass of a foreground galaxy (or galaxies) curves space-time around it, causing light rays from a background source to appear deflected. + - Contents: Grid, Mass Profiles +- [Tutorial 5: More Ray Tracing](scripts/chapter_1_introduction/tutorial_3_more_ray_tracing.py): We'll now reinforce the ideas that we learnt about ray-tracing in the previous tutorial and introduce the following new concepts: + - Contents: Initial Setup, Concise Code, Critical Curves, Caustics, Units, More Complexity, Multi Galaxy Ray Tracing, Wrap Up +- [Tutorial 4: Point Sources](scripts/chapter_1_introduction/tutorial_4_point_sources.py): This tutorial is not wrriten yet, but will explain how point source lensing works. + - Contents: Wrap Up +- [Tutorial 5: Lensing Formalism](scripts/chapter_1_introduction/tutorial_5_lensing_formalism.py): This tutorial is not wrriten yet, but will explain what all the different lens quantities are and give a more formal description of them. + - Contents: Wrap Up +- [Tutorial 6: Data](scripts/chapter_1_introduction/tutorial_6_data.py): In the last tutorials, we use tracers to create images of strong lenses. However, those images don't accurately represent what we would observe through a telescope. + - Contents: Initial Setup, Optics Blurring, Poisson Noise, Background Sky, Simulator, Output, Wrap Up +- [Tutorial 7: Fitting](scripts/chapter_1_introduction/tutorial_7_fitting.py): In previous tutorials, we used light profiles to create simulated images of tracer and visualized how these images would appear when captured by a CCD detector on a telescope like the Hubble Space Telescope. + - Contents: Dataset & Mask, Masked Grid, Fitting, Incorrect Fit, Model Fitting, Wrap Up +- [Tutorial 9: Summary](scripts/chapter_1_introduction/tutorial_8_summary.py): In this chapter, we have learnt that: + - Contents: Start, Object Composition, Visualization, Code Design, Source Code, Wrap Up + +## chapter_2_lens_modeling + +- [Tutorial 1: Non-linear Search](scripts/chapter_2_lens_modeling/tutorial_1_non_linear_search.py): The starting point for most scientific analysis conducted by an Astronomer is that they have observations of a strong lens using a telescope like the Hubble Space Telescope, and seek to learn about the lens galaxy, source galaxy and the Universe from these observations. With **PyAutoLen**, we seek to learn about the lenses may and ray-tracing, asking questions like how big is the lens galaxy and what does the unlensed source galaxy look like? + - Contents: Overview, Parameter Space, Search Types, Deeper Background, PyAutoFit, Initial Setup, Mask, Model, Priors, Analysis, Searches, Nested Sampling, Wrap Up +- [Tutorial 2: Practicalities](scripts/chapter_2_lens_modeling/tutorial_2_practicalities.py): In the last tutorial, we introduced foundational statistical concepts essential for model-fitting, such as parameter spaces, likelihoods, priors, and non-linear searches. Understanding these statistical concepts is crucial for performing model fits effectively. + - Contents: PyAutoFit, Initial Setup, Mask, Model, Search, Search Settings, Iterations Per Update, Analysis, VRAM Use, Run Times, Result Info, Output Folder, Unique Identifier, Output Folder Contents, Result, Other Practicalities, Wrap Up +- [Tutorial 3: Realism and Complexity](scripts/chapter_2_lens_modeling/tutorial_3_realism_and_complexity.py): In the previous two tutorials, we fitted a fairly crude and unrealistic model: the lens's mass was spherical, as was the source's light. Given most lens galaxies are literally called 'elliptical galaxies' we should probably model their mass as elliptical! Furthermore, we have completely omitted the lens galaxy's light, which in real observations outshines the source's light and therefore must be included in the lens model. + - Contents: Initial Setup, Mask, Model, Run Time, Result, Global and Local Maxima, Wrap Up +- [Tutorial 4: Dealing With Failure](scripts/chapter_2_lens_modeling/tutorial_4_dealing_with_failure.py): In the previous tutorial we intentionally made our non-linear search infer a local maxima solution and therefore return a physically incorrect lens model. In this tutorial, we will pretend that we have modeled our lens and inferred a local maxima. We introduce three approaches one can take that changes how we fit the model, all of which have the aim of ensuring we infer the global maxima: + - Contents: Initial Setup, Mask, Prior Tuning, Run Time, Result, Discussion +- [Tutorial 5: Linear Profiles](scripts/chapter_2_lens_modeling/tutorial_5_linear_profiles.py): In the previous tutorial we learned how to balance model complexity with our non-linear search in order to infer accurate lens model solutions and avoid failure. We saw how in order to fit a model accurately one may have to parameterize and fit a simpler model with fewer non-linear parameters, at the expense of fitting the data less accurately. + - Contents: Initial Setup, Mask, Linear Light Profiles, Run Time, Result, Intensities, Visualization, Basis, Model Fit, Source MGE, Multi Gaussian Expansion Benefits, Disadvantage of Basis Functions, Positive Only Solver, Other Basis Functions, Wrap Up +- [Tutorial 6: Masking and Positions](scripts/chapter_2_lens_modeling/tutorial_6_masking_and_positions.py): We have learnt everything we need to know about non-linear searches to model a strong lens and infer a good lens model solution. Now, lets consider masking in more detail, something we have not given much consideration previously. We'll also learn a neat trick to improve the speed and accuracy of a non-linear search. + - Contents: Initial Setup, Mask, Run Time, Search, Discussion, Positions Thresholding, Wrap Up +- [Tutorial 7: Results](scripts/chapter_2_lens_modeling/tutorial_7_results.py): In the previous tutorials, each search returned a `Result` object, which we used to plot the maximum log likelihood fit each model-fit. In this tutorial, we'll take a look at the result object in a little more detail. + - Contents: Initial Setup, Tracer & Fit, Samples, Workspace, Database, Wrap Up +- [Tutorial 8: Need For Speed](scripts/chapter_2_lens_modeling/tutorial_8_need_for_speed.py): In this chapter, we have learnt how to model strong lenses and how to balance complexity and realism to ensure that we infer a good lens model. + - Contents: Algorithmic Optimization, Data Quantity, Wrap Up + +## chapter_3_search_chaining + +- [Tutorial 1: Search Chaining](scripts/chapter_3_search_chaining/tutorial_1_search_chaining.py): In chapter 2, we learnt how to perform lens modeling using a non-linear search. In all of the tutorials, we fitted the data using just one non-linear search. In this chapter, we introduce a technique called 'non-linear search chaining', fits a lens model using a sequence of non-linear searches. The initial searches fit simpler lens models whose parameter spaces can be more accurately and efficiently sampled. The results of this search are then passed to later searches which fit lens models of gradually increasing complexity. + - Contents: Initial Setup, Model, Result, Prior Passing, Run Time, Model Fit, Wrap Up +- [Tutorial 2: Prior Passing](scripts/chapter_3_search_chaining/tutorial_2_prior_passing.py): In the previous tutorial, we used non-linear search chaining to break the model-fitting procedure down into two non-linear searches. This used an initial search to fit a simple lens model, whose results were used to tune and initialize the priors of a more complex lens model that was fitted by the second search. + - Contents: Initial Setup, Model, Search, Prior Passing, Result, Wrap Up, Detailed Explanation Of Prior Passing, EXAMPLE +- [Tutorial 3: Lens and Source](scripts/chapter_3_search_chaining/tutorial_3_lens_and_source.py): In this tutorial, we demonstrate search chaining using three searches to fit strong lens `Imaging` which includes the lens galaxy's light. + - Contents: Dated Tutorial, Initial Setup, Paths, Notes, Wrap Up +- [Tutorial 4: Two Lens galaxies](scripts/chapter_3_search_chaining/tutorial_4_x2_lens_galaxies.py): Up to now, all the images we've fitted had one lens galaxy. However, we saw in chapter 1 that our lens plane can consist of multiple galaxies which each contribute to the strong lensing. Multi-galaxy systems are challenging to model, because they add an extra 5-10 parameters to the non-linear search and, more problematically, the degeneracies between the parameters of the mass profiles of the two galaxies can be severe. + - Contents: Initial Setup, Mask, Paths, Search Chaining Approach, Wrap Up +- [Tutorial 5: Complex Source](scripts/chapter_3_search_chaining/tutorial_5_complex_source.py): Up to now, we've not paid much attention to the source galaxy's morphology. We've assumed its a single-component exponential profile, which is a fairly crude assumption. A quick look at any image of a real galaxy reveals a wealth of different structures that could be present: bulges, disks, bars, star-forming knots and so on. Furthermore, there could be more than one source-galaxy! + - Contents: Initial Setup, Paths, Search Chaining Approach, Run Times, Wrap Up +- [Tutorial 6: SLaM](scripts/chapter_3_search_chaining/tutorial_6_slam.py): You are now familiar with pipelines, in particular how we use them to break-down the lens modeling procedure to provide more efficient and reliable model-fits. In the previous tutorials, you learnt how to write your own pipelines, which can fit whatever lens model is of particular interest to your scientific study. + +## chapter_4_pixelizations + +- [Tutorial 10: Brightness Adaption](scripts/chapter_4_pixelizations/tutorial_10_brightness_adaption.py): In the previous tutorial we motivated our need to adapt the pixelization to the source's morphology, such that source pixels congregates in the source's brightest regions regardless of where the source is located in the source-plane. + - Contents: Initial Setup, Adapt Image, Adaption, Hilbert, Weight Map, Wrap Up +- [Tutorial 11: Adaptive Regularization](scripts/chapter_4_pixelizations/tutorial_11_adaptive_regularization.py): In tutorial 7, we discussed why the `Constant` regularization scheme was sub-optimal. Different regions of the source demand different levels of regularization, motivating a regularization scheme which adapts to the reconstructed source's surface brightness. + - Contents: Initial Setup, Convenience Function, Adaptive Regularization, Wrap Up +- [Tutorial 1: pixelizations](scripts/chapter_4_pixelizations/tutorial_1_pixelizations.py): In the previous chapters, we used light profiles to model the light of a strong lens's source galaxy, where the light profile was an analytic description of how the luminosity varies as a function of radius. In this chapter, we are instead going to reconstruct the source's light on a pixel-grid, and in this tutorial we will learn how to create a source-plane pixelization. + - Contents: Initial Setup, Mesh, Wrap Up +- [Tutorial 2: Mappers](scripts/chapter_4_pixelizations/tutorial_2_mappers.py): In the previous tutorial, we used a pixelization to create made a `Mapper`. However, it was not clear what a `Mapper` does, why it was called a mapper and whether it was mapping anything at all! + - Contents: Initial Setup, Mappers, Mask, Wrap Up +- [Tutorial 3: Inversions](scripts/chapter_4_pixelizations/tutorial_3_inversions.py): In the previous two tutorials, we introduced: + - Contents: Initial Setup, Pixelization, Positive Only Solver, Wrap Up, Detailed Explanation +- [Tutorial 4: Bayesian Regularization](scripts/chapter_4_pixelizations/tutorial_4_bayesian_regularization.py): So far, we have: + - Contents: Initial Setup, Convenience Function, Pixelization, Regularization, Bayesian Evidence, Detailed Description +- [Tutorial 5: Borders](scripts/chapter_4_pixelizations/tutorial_5_borders.py): In the previous tutorials, the source-plane pixel grid perfectly mapped over the traced image-pixel $(y,x)$ coordinates in the source plane. If these pixels mapped to a larger area in the source plane, its pixel-grid would automatically increase its size so as to cover every source-plane coordinate. + - Contents: Initial Setup, Borders, Wrap Up +- [Tutorial 6: Lens Modeling](scripts/chapter_4_pixelizations/tutorial_6_lens_modeling.py): When modeling complex source's with parametric profiles, we quickly entered a regime where our non-linear search was faced with a parameter space of dimensionality N=20+ parameters. This made the model-fitting inefficient and likely to infer a local maxima. + - Contents: Initial Setup, Unphysical Solutions, Brief Description, Light Profiles, Wrap Up +- [Tutorial 7: Adaptive Pixelization](scripts/chapter_4_pixelizations/tutorial_7_adaptive_pixelization.py): In this tutorial we will introduce a new `Pixelization` object, which uses an `Overlay` image-mesh and a `Delaunay` mesh. + - Contents: Initial Setup, Advantages and Disadvatanges, Image Mesh, Regularization, Wrap Up +- [Tutorial 8: Model-Fit](scripts/chapter_4_pixelizations/tutorial_8_model_fit.py): You should now perform lens modeling using a pixelization, which is described fully in the example: +- [Tutorial 9: Fit Problems](scripts/chapter_4_pixelizations/tutorial_9_fit_problems.py): To begin, make sure you have read the `introduction` file carefully, as a clear understanding of how the Bayesian evidence works is key to understanding this chapter! + - Contents: Initial Setup, Mask, Simulator, Fitting, Fit Problems, Discussion, Wrap Up + +## chapter_optional + +- [Tutorial: Alternative Searches](scripts/chapter_optional/tutorial_searches.py): Up to now, we've always used the non-linear search Nautilus and not considered the input parameters that control its sampling. In this tutorial, we'll consider how we can change these setting to balance finding the global maxima solution with fast run time. + - Contents: Nested Sampling, Optimizers, MCMC + +## simulator + +- [Simulator: Simple Sersic Lens](scripts/simulator/lens_sersic.py): This script simulates `Imaging` of a 'galaxy-scale' which is identical to the `simple` simulated in the `start_here.py` script, but where the lens galaxy's light is an `Sersic` profile. + - Contents: Model, Dataset Paths, Simulate, Ray Tracing, Output, Visualize, Tracer json +- [Simulator: Lens x2](scripts/simulator/lens_x2.py): This script simulates `Imaging` of a 'galaxy-scale' lens where there are two lens galaxies, each with their own light and mass profiles. + - Contents: Model, Dataset Paths, Simulate, Ray Tracing, Output, Visualize, Tracer json +- [Simulator: No Lens Light](scripts/simulator/no_lens_light.py): This script simulates `Imaging` of a 'galaxy-scale' which is identical to the `simple` simulated in the `start_here.py` script, but where the lens galaxy's light is omitted. + - Contents: Model, Dataset Paths, Simulate, Ray Tracing, Output, Visualize, Mask Extra Galaxies, Tracer json +- [Simulator: SIS](scripts/simulator/no_lens_light__mass_sis.py): This script simulates `Imaging` of a 'galaxy-scale' which is identical to the `simple` simulated in the `start_here.py` script, but where the lens galaxy's light is omitted and the lens's mass distribution is a Singular Isothermal Sphere. + - Contents: Model, Dataset Paths, Simulate, Ray Tracing, Output, Visualize, Tracer json +- [Simulator: Source Complex](scripts/simulator/source_complex.py): This script simulates `Imaging` of a 'galaxy-scale' strong lens where the source galaxy's light is more complex than other examples, being composed of 4 Sersics. + - Contents: Model, Dataset Paths, Simulate, Ray Tracing, Output, Visualize, Tracer json diff --git a/llms.txt b/llms.txt new file mode 100644 index 0000000..465321d --- /dev/null +++ b/llms.txt @@ -0,0 +1,81 @@ +If your AI can't browse GitHub, paste this entire file into the chat as context. + +# HowToLens + +> The **HowToLens** lecture series: tutorial scripts (and generated notebooks) that teach +> strong gravitational lensing and the **PyAutoLens** API from first principles. This file +> is a routing layer: given a learner's question, point them to the RIGHT existing tutorial +> in this repository instead of inventing an explanation. Every path below is a real file +> in this repo; scripts run from the repo root (e.g. `python scripts/chapter_1_introduction/tutorial_1_grids_and_galaxies.py`), +> and each has a matching notebook under `notebooks/`. + +## Who this is for + +HowToLens is the **on-ramp for beginners** — students new to lensing, or new to PyAutoLens. +It teaches the concepts and the API step by step. Once a learner wants to run a real +analysis on their own data, route them *out* to the production examples in +[autolens_workspace](https://github.com/PyAutoLabs/autolens_workspace) (see "Ready for real +analysis?" below). + +## Start here + +- [start_here.ipynb](start_here.ipynb): One-page orientation — what the lectures cover and how to run them (locally or in Colab). +- [scripts/chapter_1_introduction/tutorial_1_grids_and_galaxies.py](scripts/chapter_1_introduction/tutorial_1_grids_and_galaxies.py): The first real lecture — what a strong lens is, grids, geometry and light profiles. Begin here if the learner is new to lensing. + +## The learning path (chapters in order) + +- **Chapter 1 — Introduction** → [scripts/chapter_1_introduction/](scripts/chapter_1_introduction/): Lensing from first principles: grids & galaxies, ray tracing, simulating data, and fitting data. The conceptual foundation everything else builds on. +- **Chapter 2 — Lens Modeling** → [scripts/chapter_2_lens_modeling/](scripts/chapter_2_lens_modeling/): How to fit a lens model to data with a non-linear search — parameter spaces, priors, dealing with failure, linear light profiles, masking, and reading results. +- **Chapter 3 — Search Chaining** → [scripts/chapter_3_search_chaining/](scripts/chapter_3_search_chaining/): Breaking a hard fit into a sequence of searches (prior passing), multi-galaxy lenses, complex sources, and an introduction to the SLaM pipelines. +- **Chapter 4 — Pixelizations** → [scripts/chapter_4_pixelizations/](scripts/chapter_4_pixelizations/): Reconstructing the source on a pixel-grid instead of with light profiles — mappers, inversions, Bayesian regularization, borders, and adaptive meshes. + +## I want to understand… + +- **What is a strong lens / how does ray tracing work?** → [scripts/chapter_1_introduction/tutorial_2_ray_tracing.py](scripts/chapter_1_introduction/tutorial_2_ray_tracing.py): Mass profiles deflecting light; image-plane vs source-plane. +- **How does a telescope image (and noise/PSF) get simulated?** → [scripts/chapter_1_introduction/tutorial_6_data.py](scripts/chapter_1_introduction/tutorial_6_data.py): Optics blurring, Poisson noise, background sky, the simulator API. +- **What does "fitting" a lens actually mean?** → [scripts/chapter_1_introduction/tutorial_7_fitting.py](scripts/chapter_1_introduction/tutorial_7_fitting.py): Masking, model images, residuals and the log likelihood. +- **How does a non-linear search find a model?** → [scripts/chapter_2_lens_modeling/tutorial_1_non_linear_search.py](scripts/chapter_2_lens_modeling/tutorial_1_non_linear_search.py): Parameter space, priors, nested sampling with Nautilus. +- **My fit found a wrong (local-maxima) solution — what do I do?** → [scripts/chapter_2_lens_modeling/tutorial_4_dealing_with_failure.py](scripts/chapter_2_lens_modeling/tutorial_4_dealing_with_failure.py): Prior tuning, simplifying the model, and other escapes from local maxima. +- **What are linear light profiles / MGE?** → [scripts/chapter_2_lens_modeling/tutorial_5_linear_profiles.py](scripts/chapter_2_lens_modeling/tutorial_5_linear_profiles.py): Solving intensities linearly; the Multi-Gaussian Expansion basis. +- **How do I chain searches / what is SLaM?** → [scripts/chapter_3_search_chaining/tutorial_1_search_chaining.py](scripts/chapter_3_search_chaining/tutorial_1_search_chaining.py) then [scripts/chapter_3_search_chaining/tutorial_6_slam.py](scripts/chapter_3_search_chaining/tutorial_6_slam.py). +- **How do I reconstruct a complex source on a pixel grid?** → [scripts/chapter_4_pixelizations/tutorial_1_pixelizations.py](scripts/chapter_4_pixelizations/tutorial_1_pixelizations.py) through [tutorial_4_bayesian_regularization.py](scripts/chapter_4_pixelizations/tutorial_4_bayesian_regularization.py). + +## Ready for real analysis? + +When the learner wants to model their *own* lens (not a teaching dataset), route them to the +production workspace — it has science-case entry points and a data-preparation guide: + +- [autolens_workspace](https://github.com/PyAutoLabs/autolens_workspace) — see its own `llms.txt` routing layer; e.g. `imaging/start_here.py` for CCD data and `imaging/data_preparation/start_here.py` for getting their FITS files analysis-ready. + +## How to answer (for the assistant) + +When routing a learner, reply in this shape (the same shape the workspace navigator uses, so +the two agree): + +- **Start here** — the single best tutorial for their question (from the lists above). +- **Then see** — the next tutorial in the chapter, or the concept it builds toward. +- **Related guide** — a relevant later chapter, or the matching `autolens_workspace` example for production use. +- **Why this is the right example** — one line tying the tutorial to their question. +- **What to modify** — the few things they'd change to explore (model components, dataset, search settings). +- **What needs local execution** — flag any step that requires actually running the fit on their machine. + +### Code style (match the lectures, not banner comments) + +Drafted code must match this repo's style: triple-quoted `"""__Section__"""` docstrings with +a line of prose explaining each step, and a module docstring whose title is underlined with +`=`. Do NOT use `# -----` banner comments to mark sections; reserve inline `#` for short +line-level notes only. + +## Capability boundary (chat without local execution) + +A chat assistant can route to the right tutorial, explain lensing concepts, review pasted +scripts / tracebacks / plots, and draft code. It CANNOT run fits, inspect the learner's local +files or `output/` folder, or guarantee code against their installed PyAutoLens version. To +run a tutorial, the learner executes it locally from the repo root, or in Google Colab (the +notebooks ship Colab setup cells). For sustained editing, execution and project state, point +them to a local coding agent (Claude Code / Codex) or the +[autolens_assistant](https://github.com/PyAutoLabs/autolens_assistant) science-assistant workspace. + +--- + +For the full per-tutorial listing, see the generated companion catalogue `llms-full.txt`. diff --git a/workspace_index.json b/workspace_index.json new file mode 100644 index 0000000..8f8ff0c --- /dev/null +++ b/workspace_index.json @@ -0,0 +1,624 @@ +[ + { + "contents": [ + "Directories", + "Dataset", + "Subplots", + "Plot Customization", + "Overlays", + "Wrap Up" + ], + "cross_refs": [], + "notebook": "notebooks/chapter_1_introduction/tutorial_0_visualization.ipynb", + "path": "scripts/chapter_1_introduction/tutorial_0_visualization.py", + "summary": "In this tutorial, we quickly cover visualization in **PyAutoLens** and make sure images display clearly in your Jupyter notebook and on your computer screen.", + "title": "Tutorial 0: Visualization" + }, + { + "contents": [ + "Grids", + "Geometry", + "Light Profiles", + "One Dimension Projection" + ], + "cross_refs": [], + "notebook": "notebooks/chapter_1_introduction/tutorial_1_grids_and_galaxies.ipynb", + "path": "scripts/chapter_1_introduction/tutorial_1_grids_and_galaxies.py", + "summary": "A strong gravitational lens is a system where two (or more) galaxies align perfectly down our line of sight from Earth such that the foreground galaxy's mass curves space-time in on itself, such that the light of a background source galaxy is deflected and magnified. This means we can see the background source galaxy multiple times, as multiple arcs or rings, because multiple paths through the foreground galaxy's mass are taken by the source's light.", + "title": "HowToLens: Introduction" + }, + { + "contents": [ + "Grid", + "Mass Profiles" + ], + "cross_refs": [], + "notebook": "notebooks/chapter_1_introduction/tutorial_2_ray_tracing.ipynb", + "path": "scripts/chapter_1_introduction/tutorial_2_ray_tracing.py", + "summary": "Strong gravitational lensing occurs when the mass of a foreground galaxy (or galaxies) curves space-time around it, causing light rays from a background source to appear deflected.", + "title": "Tutorial 2: Ray Tracing" + }, + { + "contents": [ + "Initial Setup", + "Concise Code", + "Critical Curves", + "Caustics", + "Units", + "More Complexity", + "Multi Galaxy Ray Tracing", + "Wrap Up" + ], + "cross_refs": [], + "notebook": "notebooks/chapter_1_introduction/tutorial_3_more_ray_tracing.ipynb", + "path": "scripts/chapter_1_introduction/tutorial_3_more_ray_tracing.py", + "summary": "We'll now reinforce the ideas that we learnt about ray-tracing in the previous tutorial and introduce the following new concepts:", + "title": "Tutorial 5: More Ray Tracing" + }, + { + "contents": [ + "Wrap Up" + ], + "cross_refs": [], + "notebook": "notebooks/chapter_1_introduction/tutorial_4_point_sources.ipynb", + "path": "scripts/chapter_1_introduction/tutorial_4_point_sources.py", + "summary": "This tutorial is not wrriten yet, but will explain how point source lensing works.", + "title": "Tutorial 4: Point Sources" + }, + { + "contents": [ + "Wrap Up" + ], + "cross_refs": [], + "notebook": "notebooks/chapter_1_introduction/tutorial_5_lensing_formalism.ipynb", + "path": "scripts/chapter_1_introduction/tutorial_5_lensing_formalism.py", + "summary": "This tutorial is not wrriten yet, but will explain what all the different lens quantities are and give a more formal description of them.", + "title": "Tutorial 5: Lensing Formalism" + }, + { + "contents": [ + "Initial Setup", + "Optics Blurring", + "Poisson Noise", + "Background Sky", + "Simulator", + "Output", + "Wrap Up" + ], + "cross_refs": [], + "notebook": "notebooks/chapter_1_introduction/tutorial_6_data.ipynb", + "path": "scripts/chapter_1_introduction/tutorial_6_data.py", + "summary": "In the last tutorials, we use tracers to create images of strong lenses. However, those images don't accurately represent what we would observe through a telescope.", + "title": "Tutorial 6: Data" + }, + { + "contents": [ + "Dataset & Mask", + "Masked Grid", + "Fitting", + "Incorrect Fit", + "Model Fitting", + "Wrap Up" + ], + "cross_refs": [], + "notebook": "notebooks/chapter_1_introduction/tutorial_7_fitting.ipynb", + "path": "scripts/chapter_1_introduction/tutorial_7_fitting.py", + "summary": "In previous tutorials, we used light profiles to create simulated images of tracer and visualized how these images would appear when captured by a CCD detector on a telescope like the Hubble Space Telescope.", + "title": "Tutorial 7: Fitting" + }, + { + "contents": [ + "Start", + "Object Composition", + "Visualization", + "Code Design", + "Source Code", + "Wrap Up" + ], + "cross_refs": [], + "notebook": "notebooks/chapter_1_introduction/tutorial_8_summary.ipynb", + "path": "scripts/chapter_1_introduction/tutorial_8_summary.py", + "summary": "In this chapter, we have learnt that:", + "title": "Tutorial 9: Summary" + }, + { + "contents": [ + "Overview", + "Parameter Space", + "Search Types", + "Deeper Background", + "PyAutoFit", + "Initial Setup", + "Mask", + "Model", + "Priors", + "Analysis", + "Searches", + "Nested Sampling", + "Wrap Up" + ], + "cross_refs": [ + "/imaging/log_likelihood_function/parametric.ipynb" + ], + "notebook": "notebooks/chapter_2_lens_modeling/tutorial_1_non_linear_search.ipynb", + "path": "scripts/chapter_2_lens_modeling/tutorial_1_non_linear_search.py", + "summary": "The starting point for most scientific analysis conducted by an Astronomer is that they have observations of a strong lens using a telescope like the Hubble Space Telescope, and seek to learn about the lens galaxy, source galaxy and the Universe from these observations. With **PyAutoLen**, we seek to learn about the lenses may and ray-tracing, asking questions like how big is the lens galaxy and what does the unlensed source galaxy look like?", + "title": "Tutorial 1: Non-linear Search" + }, + { + "contents": [ + "PyAutoFit", + "Initial Setup", + "Mask", + "Model", + "Search", + "Search Settings", + "Iterations Per Update", + "Analysis", + "VRAM Use", + "Run Times", + "Result Info", + "Output Folder", + "Unique Identifier", + "Output Folder Contents", + "Result", + "Other Practicalities", + "Wrap Up" + ], + "cross_refs": [ + "scripts/howtogalaxy/chapter_2_modeling/tutorial_2_practicalities.py" + ], + "notebook": "notebooks/chapter_2_lens_modeling/tutorial_2_practicalities.ipynb", + "path": "scripts/chapter_2_lens_modeling/tutorial_2_practicalities.py", + "summary": "In the last tutorial, we introduced foundational statistical concepts essential for model-fitting, such as parameter spaces, likelihoods, priors, and non-linear searches. Understanding these statistical concepts is crucial for performing model fits effectively.", + "title": "Tutorial 2: Practicalities" + }, + { + "contents": [ + "Initial Setup", + "Mask", + "Model", + "Run Time", + "Result", + "Global and Local Maxima", + "Wrap Up" + ], + "cross_refs": [], + "notebook": "notebooks/chapter_2_lens_modeling/tutorial_3_realism_and_complexity.ipynb", + "path": "scripts/chapter_2_lens_modeling/tutorial_3_realism_and_complexity.py", + "summary": "In the previous two tutorials, we fitted a fairly crude and unrealistic model: the lens's mass was spherical, as was the source's light. Given most lens galaxies are literally called 'elliptical galaxies' we should probably model their mass as elliptical! Furthermore, we have completely omitted the lens galaxy's light, which in real observations outshines the source's light and therefore must be included in the lens model.", + "title": "Tutorial 3: Realism and Complexity" + }, + { + "contents": [ + "Initial Setup", + "Mask", + "Prior Tuning", + "Run Time", + "Result", + "Discussion" + ], + "cross_refs": [ + "howtolens/chapter_optional/tutorial_searches.ipynb" + ], + "notebook": "notebooks/chapter_2_lens_modeling/tutorial_4_dealing_with_failure.ipynb", + "path": "scripts/chapter_2_lens_modeling/tutorial_4_dealing_with_failure.py", + "summary": "In the previous tutorial we intentionally made our non-linear search infer a local maxima solution and therefore return a physically incorrect lens model. In this tutorial, we will pretend that we have modeled our lens and inferred a local maxima. We introduce three approaches one can take that changes how we fit the model, all of which have the aim of ensuring we infer the global maxima:", + "title": "Tutorial 4: Dealing With Failure" + }, + { + "contents": [ + "Initial Setup", + "Mask", + "Linear Light Profiles", + "Run Time", + "Result", + "Intensities", + "Visualization", + "Basis", + "Model Fit", + "Source MGE", + "Multi Gaussian Expansion Benefits", + "Disadvantage of Basis Functions", + "Positive Only Solver", + "Other Basis Functions", + "Wrap Up" + ], + "cross_refs": [ + "/modeling/features/multi_gaussian_expansion.py", + "autolens_workspace/scripts/modeling/features/shapelets.py" + ], + "notebook": "notebooks/chapter_2_lens_modeling/tutorial_5_linear_profiles.ipynb", + "path": "scripts/chapter_2_lens_modeling/tutorial_5_linear_profiles.py", + "summary": "In the previous tutorial we learned how to balance model complexity with our non-linear search in order to infer accurate lens model solutions and avoid failure. We saw how in order to fit a model accurately one may have to parameterize and fit a simpler model with fewer non-linear parameters, at the expense of fitting the data less accurately.", + "title": "Tutorial 5: Linear Profiles" + }, + { + "contents": [ + "Initial Setup", + "Mask", + "Run Time", + "Search", + "Discussion", + "Positions Thresholding", + "Wrap Up" + ], + "cross_refs": [], + "notebook": "notebooks/chapter_2_lens_modeling/tutorial_6_masking_and_positions.ipynb", + "path": "scripts/chapter_2_lens_modeling/tutorial_6_masking_and_positions.py", + "summary": "We have learnt everything we need to know about non-linear searches to model a strong lens and infer a good lens model solution. Now, lets consider masking in more detail, something we have not given much consideration previously. We'll also learn a neat trick to improve the speed and accuracy of a non-linear search.", + "title": "Tutorial 6: Masking and Positions" + }, + { + "contents": [ + "Initial Setup", + "Tracer & Fit", + "Samples", + "Workspace", + "Database", + "Wrap Up" + ], + "cross_refs": [], + "notebook": "notebooks/chapter_2_lens_modeling/tutorial_7_results.ipynb", + "path": "scripts/chapter_2_lens_modeling/tutorial_7_results.py", + "summary": "In the previous tutorials, each search returned a `Result` object, which we used to plot the maximum log likelihood fit each model-fit. In this tutorial, we'll take a look at the result object in a little more detail.", + "title": "Tutorial 7: Results" + }, + { + "contents": [ + "Algorithmic Optimization", + "Data Quantity", + "Wrap Up" + ], + "cross_refs": [ + "//numba.py", + "chapter_optional/tutorial_searches.ipynb" + ], + "notebook": "notebooks/chapter_2_lens_modeling/tutorial_8_need_for_speed.ipynb", + "path": "scripts/chapter_2_lens_modeling/tutorial_8_need_for_speed.py", + "summary": "In this chapter, we have learnt how to model strong lenses and how to balance complexity and realism to ensure that we infer a good lens model.", + "title": "Tutorial 8: Need For Speed" + }, + { + "contents": [ + "Initial Setup", + "Model", + "Result", + "Prior Passing", + "Run Time", + "Model Fit", + "Wrap Up" + ], + "cross_refs": [], + "notebook": "notebooks/chapter_3_search_chaining/tutorial_1_search_chaining.ipynb", + "path": "scripts/chapter_3_search_chaining/tutorial_1_search_chaining.py", + "summary": "In chapter 2, we learnt how to perform lens modeling using a non-linear search. In all of the tutorials, we fitted the data using just one non-linear search. In this chapter, we introduce a technique called 'non-linear search chaining', fits a lens model using a sequence of non-linear searches. The initial searches fit simpler lens models whose parameter spaces can be more accurately and efficiently sampled. The results of this search are then passed to later searches which fit lens models of gradually increasing complexity.", + "title": "Tutorial 1: Search Chaining" + }, + { + "contents": [ + "Initial Setup", + "Model", + "Search", + "Prior Passing", + "Result", + "Wrap Up", + "Detailed Explanation Of Prior Passing", + "EXAMPLE" + ], + "cross_refs": [], + "notebook": "notebooks/chapter_3_search_chaining/tutorial_2_prior_passing.ipynb", + "path": "scripts/chapter_3_search_chaining/tutorial_2_prior_passing.py", + "summary": "In the previous tutorial, we used non-linear search chaining to break the model-fitting procedure down into two non-linear searches. This used an initial search to fit a simple lens model, whose results were used to tune and initialize the priors of a more complex lens model that was fitted by the second search.", + "title": "Tutorial 2: Prior Passing" + }, + { + "contents": [ + "Dated Tutorial", + "Initial Setup", + "Paths", + "Notes", + "Wrap Up" + ], + "cross_refs": [], + "notebook": "notebooks/chapter_3_search_chaining/tutorial_3_lens_and_source.ipynb", + "path": "scripts/chapter_3_search_chaining/tutorial_3_lens_and_source.py", + "summary": "In this tutorial, we demonstrate search chaining using three searches to fit strong lens `Imaging` which includes the lens galaxy's light.", + "title": "Tutorial 3: Lens and Source" + }, + { + "contents": [ + "Initial Setup", + "Mask", + "Paths", + "Search Chaining Approach", + "Wrap Up" + ], + "cross_refs": [], + "notebook": "notebooks/chapter_3_search_chaining/tutorial_4_x2_lens_galaxies.ipynb", + "path": "scripts/chapter_3_search_chaining/tutorial_4_x2_lens_galaxies.py", + "summary": "Up to now, all the images we've fitted had one lens galaxy. However, we saw in chapter 1 that our lens plane can consist of multiple galaxies which each contribute to the strong lensing. Multi-galaxy systems are challenging to model, because they add an extra 5-10 parameters to the non-linear search and, more problematically, the degeneracies between the parameters of the mass profiles of the two galaxies can be severe.", + "title": "Tutorial 4: Two Lens galaxies" + }, + { + "contents": [ + "Initial Setup", + "Paths", + "Search Chaining Approach", + "Run Times", + "Wrap Up" + ], + "cross_refs": [], + "notebook": "notebooks/chapter_3_search_chaining/tutorial_5_complex_source.ipynb", + "path": "scripts/chapter_3_search_chaining/tutorial_5_complex_source.py", + "summary": "Up to now, we've not paid much attention to the source galaxy's morphology. We've assumed its a single-component exponential profile, which is a fairly crude assumption. A quick look at any image of a real galaxy reveals a wealth of different structures that could be present: bulges, disks, bars, star-forming knots and so on. Furthermore, there could be more than one source-galaxy!", + "title": "Tutorial 5: Complex Source" + }, + { + "contents": [], + "cross_refs": [], + "notebook": "notebooks/chapter_3_search_chaining/tutorial_6_slam.ipynb", + "path": "scripts/chapter_3_search_chaining/tutorial_6_slam.py", + "summary": "You are now familiar with pipelines, in particular how we use them to break-down the lens modeling procedure to provide more efficient and reliable model-fits. In the previous tutorials, you learnt how to write your own pipelines, which can fit whatever lens model is of particular interest to your scientific study.", + "title": "Tutorial 6: SLaM" + }, + { + "contents": [ + "Initial Setup", + "Adapt Image", + "Adaption", + "Hilbert", + "Weight Map", + "Wrap Up" + ], + "cross_refs": [], + "notebook": "notebooks/chapter_4_pixelizations/tutorial_10_brightness_adaption.ipynb", + "path": "scripts/chapter_4_pixelizations/tutorial_10_brightness_adaption.py", + "summary": "In the previous tutorial we motivated our need to adapt the pixelization to the source's morphology, such that source pixels congregates in the source's brightest regions regardless of where the source is located in the source-plane.", + "title": "Tutorial 10: Brightness Adaption" + }, + { + "contents": [ + "Initial Setup", + "Convenience Function", + "Adaptive Regularization", + "Wrap Up" + ], + "cross_refs": [], + "notebook": "notebooks/chapter_4_pixelizations/tutorial_11_adaptive_regularization.ipynb", + "path": "scripts/chapter_4_pixelizations/tutorial_11_adaptive_regularization.py", + "summary": "In tutorial 7, we discussed why the `Constant` regularization scheme was sub-optimal. Different regions of the source demand different levels of regularization, motivating a regularization scheme which adapts to the reconstructed source's surface brightness.", + "title": "Tutorial 11: Adaptive Regularization" + }, + { + "contents": [ + "Initial Setup", + "Mesh", + "Wrap Up" + ], + "cross_refs": [], + "notebook": "notebooks/chapter_4_pixelizations/tutorial_1_pixelizations.ipynb", + "path": "scripts/chapter_4_pixelizations/tutorial_1_pixelizations.py", + "summary": "In the previous chapters, we used light profiles to model the light of a strong lens's source galaxy, where the light profile was an analytic description of how the luminosity varies as a function of radius. In this chapter, we are instead going to reconstruct the source's light on a pixel-grid, and in this tutorial we will learn how to create a source-plane pixelization.", + "title": "Tutorial 1: pixelizations" + }, + { + "contents": [ + "Initial Setup", + "Mappers", + "Mask", + "Wrap Up" + ], + "cross_refs": [], + "notebook": "notebooks/chapter_4_pixelizations/tutorial_2_mappers.ipynb", + "path": "scripts/chapter_4_pixelizations/tutorial_2_mappers.py", + "summary": "In the previous tutorial, we used a pixelization to create made a `Mapper`. However, it was not clear what a `Mapper` does, why it was called a mapper and whether it was mapping anything at all!", + "title": "Tutorial 2: Mappers" + }, + { + "contents": [ + "Initial Setup", + "Pixelization", + "Positive Only Solver", + "Wrap Up", + "Detailed Explanation" + ], + "cross_refs": [ + "/imaging/log_likelihood_function/inversion.ipynb" + ], + "notebook": "notebooks/chapter_4_pixelizations/tutorial_3_inversions.ipynb", + "path": "scripts/chapter_4_pixelizations/tutorial_3_inversions.py", + "summary": "In the previous two tutorials, we introduced:", + "title": "Tutorial 3: Inversions" + }, + { + "contents": [ + "Initial Setup", + "Convenience Function", + "Pixelization", + "Regularization", + "Bayesian Evidence", + "Detailed Description" + ], + "cross_refs": [], + "notebook": "notebooks/chapter_4_pixelizations/tutorial_4_bayesian_regularization.ipynb", + "path": "scripts/chapter_4_pixelizations/tutorial_4_bayesian_regularization.py", + "summary": "So far, we have:", + "title": "Tutorial 4: Bayesian Regularization" + }, + { + "contents": [ + "Initial Setup", + "Borders", + "Wrap Up" + ], + "cross_refs": [], + "notebook": "notebooks/chapter_4_pixelizations/tutorial_5_borders.ipynb", + "path": "scripts/chapter_4_pixelizations/tutorial_5_borders.py", + "summary": "In the previous tutorials, the source-plane pixel grid perfectly mapped over the traced image-pixel $(y,x)$ coordinates in the source plane. If these pixels mapped to a larger area in the source plane, its pixel-grid would automatically increase its size so as to cover every source-plane coordinate.", + "title": "Tutorial 5: Borders" + }, + { + "contents": [ + "Initial Setup", + "Unphysical Solutions", + "Brief Description", + "Light Profiles", + "Wrap Up" + ], + "cross_refs": [], + "notebook": "notebooks/chapter_4_pixelizations/tutorial_6_lens_modeling.ipynb", + "path": "scripts/chapter_4_pixelizations/tutorial_6_lens_modeling.py", + "summary": "When modeling complex source's with parametric profiles, we quickly entered a regime where our non-linear search was faced with a parameter space of dimensionality N=20+ parameters. This made the model-fitting inefficient and likely to infer a local maxima.", + "title": "Tutorial 6: Lens Modeling" + }, + { + "contents": [ + "Initial Setup", + "Advantages and Disadvatanges", + "Image Mesh", + "Regularization", + "Wrap Up" + ], + "cross_refs": [], + "notebook": "notebooks/chapter_4_pixelizations/tutorial_7_adaptive_pixelization.ipynb", + "path": "scripts/chapter_4_pixelizations/tutorial_7_adaptive_pixelization.py", + "summary": "In this tutorial we will introduce a new `Pixelization` object, which uses an `Overlay` image-mesh and a `Delaunay` mesh.", + "title": "Tutorial 7: Adaptive Pixelization" + }, + { + "contents": [], + "cross_refs": [ + "/imaging/features/pixelizations/modeling.py" + ], + "notebook": "notebooks/chapter_4_pixelizations/tutorial_8_model_fit.ipynb", + "path": "scripts/chapter_4_pixelizations/tutorial_8_model_fit.py", + "summary": "You should now perform lens modeling using a pixelization, which is described fully in the example:", + "title": "Tutorial 8: Model-Fit" + }, + { + "contents": [ + "Initial Setup", + "Mask", + "Simulator", + "Fitting", + "Fit Problems", + "Discussion", + "Wrap Up" + ], + "cross_refs": [], + "notebook": "notebooks/chapter_4_pixelizations/tutorial_9_fit_problems.ipynb", + "path": "scripts/chapter_4_pixelizations/tutorial_9_fit_problems.py", + "summary": "To begin, make sure you have read the `introduction` file carefully, as a clear understanding of how the Bayesian evidence works is key to understanding this chapter!", + "title": "Tutorial 9: Fit Problems" + }, + { + "contents": [ + "Nested Sampling", + "Optimizers", + "MCMC" + ], + "cross_refs": [], + "notebook": "notebooks/chapter_optional/tutorial_searches.ipynb", + "path": "scripts/chapter_optional/tutorial_searches.py", + "summary": "Up to now, we've always used the non-linear search Nautilus and not considered the input parameters that control its sampling. In this tutorial, we'll consider how we can change these setting to balance finding the global maxima solution with fast run time.", + "title": "Tutorial: Alternative Searches" + }, + { + "contents": [ + "Model", + "Dataset Paths", + "Simulate", + "Ray Tracing", + "Output", + "Visualize", + "Tracer json" + ], + "cross_refs": [ + "simulators/start_here.ipynb", + "start_here.py" + ], + "notebook": "notebooks/simulator/lens_sersic.ipynb", + "path": "scripts/simulator/lens_sersic.py", + "summary": "This script simulates `Imaging` of a 'galaxy-scale' which is identical to the `simple` simulated in the `start_here.py` script, but where the lens galaxy's light is an `Sersic` profile.", + "title": "Simulator: Simple Sersic Lens" + }, + { + "contents": [ + "Model", + "Dataset Paths", + "Simulate", + "Ray Tracing", + "Output", + "Visualize", + "Tracer json" + ], + "cross_refs": [ + "simulators/start_here.ipynb" + ], + "notebook": "notebooks/simulator/lens_x2.ipynb", + "path": "scripts/simulator/lens_x2.py", + "summary": "This script simulates `Imaging` of a 'galaxy-scale' lens where there are two lens galaxies, each with their own light and mass profiles.", + "title": "Simulator: Lens x2" + }, + { + "contents": [ + "Model", + "Dataset Paths", + "Simulate", + "Ray Tracing", + "Output", + "Visualize", + "Mask Extra Galaxies", + "Tracer json" + ], + "cross_refs": [ + "autolens_workspace/notebooks/modeling/features/no_lens_light.ipynb", + "imaging/features/pixelization/fit.py", + "imaging/features/pixelization/modeling.py", + "simulators/start_here.ipynb", + "start_here.py" + ], + "notebook": "notebooks/simulator/no_lens_light.ipynb", + "path": "scripts/simulator/no_lens_light.py", + "summary": "This script simulates `Imaging` of a 'galaxy-scale' which is identical to the `simple` simulated in the `start_here.py` script, but where the lens galaxy's light is omitted.", + "title": "Simulator: No Lens Light" + }, + { + "contents": [ + "Model", + "Dataset Paths", + "Simulate", + "Ray Tracing", + "Output", + "Visualize", + "Tracer json" + ], + "cross_refs": [ + "simulators/start_here.ipynb", + "start_here.py" + ], + "notebook": "notebooks/simulator/no_lens_light__mass_sis.ipynb", + "path": "scripts/simulator/no_lens_light__mass_sis.py", + "summary": "This script simulates `Imaging` of a 'galaxy-scale' which is identical to the `simple` simulated in the `start_here.py` script, but where the lens galaxy's light is omitted and the lens's mass distribution is a Singular Isothermal Sphere.", + "title": "Simulator: SIS" + }, + { + "contents": [ + "Model", + "Dataset Paths", + "Simulate", + "Ray Tracing", + "Output", + "Visualize", + "Tracer json" + ], + "cross_refs": [ + "simulators/start_here.ipynb" + ], + "notebook": "notebooks/simulator/source_complex.ipynb", + "path": "scripts/simulator/source_complex.py", + "summary": "This script simulates `Imaging` of a 'galaxy-scale' strong lens where the source galaxy's light is more complex than other examples, being composed of 4 Sersics.", + "title": "Simulator: Source Complex" + } +]