{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Tutorial 2: Profiles\n", "====================\n", "\n", "This tutorial introduces `Profile` objects, specifically:\n", "\n", " - `LightProfile`'s: which represent analytic forms for the light distribution of galaxies.\n", " - `MassProfile`'s: which represent analytic forms for the mass distributions of galaxies.\n", "\n", "By passing these objects 2D grids of $(y,x)$ coordinates we can create images from a light profile and deflection\n", "angle maps from a mass profile, the latter of which will ultimately describe how light is ray-traced throughout the\n", "Universe by a strong gravitational lens!" ] }, { "cell_type": "code", "metadata": {}, "source": [ "%matplotlib inline\n", "from pyprojroot import here\n", "workspace_path = str(here())\n", "%cd $workspace_path\n", "print(f\"Working Directory has been set to `{workspace_path}`\")\n", "\n", "import autolens as al\n", "import autolens.plot as aplt" ], "outputs": [], "execution_count": null }, { "cell_type": "markdown", "metadata": {}, "source": [ "__Initial Setup__\n", "\n", "We first setup a `Grid2D`, which uses the same resolution and arc-second to pixel conversion as the previous tutorial." ] }, { "cell_type": "code", "metadata": {}, "source": [ "grid = al.Grid2D.uniform(shape_native=(100, 100), pixel_scales=0.05)" ], "outputs": [], "execution_count": null }, { "cell_type": "markdown", "metadata": {}, "source": [ "__Light Profiles__\n", "\n", "We now create a `LightProfile` using the `light_profile` module, which is access via `lp` for conciseness. \n", "\n", "We'll use the elliptical Sersic light profile (using the `Sersic` object), which is an analytic function used \n", "throughout studies of galaxy morphology to represent their light. \n", "\n", "You'll note that we use `Ell` to concisely describe that this profile is elliptical. If you are unsure what \n", "the `ell_comps` are, I'll give a description of them at the end of the tutorial." ] }, { "cell_type": "code", "metadata": {}, "source": [ "sersic_light_profile = al.lp.Sersic(\n", " centre=(0.0, 0.0),\n", " ell_comps=(0.0, 0.111111),\n", " intensity=1.0,\n", " effective_radius=1.0,\n", " sersic_index=2.5,\n", ")" ], "outputs": [], "execution_count": null }, { "cell_type": "markdown", "metadata": {}, "source": [ "By printing a `LightProfile` we can display its parameters." ] }, { "cell_type": "code", "metadata": {}, "source": [ "print(sersic_light_profile)" ], "outputs": [], "execution_count": null }, { "cell_type": "markdown", "metadata": {}, "source": [ "__Images__\n", "\n", "We next pass the `Grid2D` to the `sersic_light_profile`, to compute the intensity of the Sersic at every (y,x) \n", "coordinate on our two dimension grid. This uses the `image_2d_from` method, and you'll see throughout this tutorial \n", "that **PyAutoLens** has numerous `_from` methods for computing quantities from a grid." ] }, { "cell_type": "code", "metadata": {}, "source": [ "image = sersic_light_profile.image_2d_from(grid=grid)" ], "outputs": [], "execution_count": null }, { "cell_type": "markdown", "metadata": {}, "source": [ "Much like the `Grid2D` objects discussed in the previous tutorial, this returns an `Array2D` object:" ] }, { "cell_type": "code", "metadata": {}, "source": [ "print(type(image))" ], "outputs": [], "execution_count": null }, { "cell_type": "markdown", "metadata": {}, "source": [ "Just like a grid, the `Array2D` object has both `native` and `slim` attributes:" ] }, { "cell_type": "code", "metadata": {}, "source": [ "print(\"Intensity of pixel 0:\")\n", "print(image.native[0, 0])\n", "print(\"Intensity of pixel 1:\")\n", "print(image.slim[1])" ], "outputs": [], "execution_count": null }, { "cell_type": "markdown", "metadata": {}, "source": [ "For an `Array2D`, the dimensions of these attributes are as follows:\n", "\n", " - `native`: an ndarray of shape [total_y_image_pixels, total_x_image_pixels].\n", "\n", " - `slim`: an ndarray of shape [total_y_image_pixels*total_x_image_pixels].\n", "\n", "The `native` and `slim` dimensions are therefore analogous to those of the `Grid2D` object, but without the final \n", "dimension of 2." ] }, { "cell_type": "code", "metadata": {}, "source": [ "print(image.shape_native)\n", "print(image.shape_slim)" ], "outputs": [], "execution_count": null }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can use a `LightProfilePlotter` to plot the image of a light profile. We pass this plotter the light profile and\n", "a grid, which are used to create the image that is plotted." ] }, { "cell_type": "code", "metadata": {}, "source": [ "light_profile_plotter = aplt.LightProfilePlotter(\n", " light_profile=sersic_light_profile, grid=grid\n", ")\n", "light_profile_plotter.figures_2d(image=True)" ], "outputs": [], "execution_count": null }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also compute and plot 1D quantities of the light profile, which show how the image intensity varies as a \n", "function of radius.\n", "\n", "1D plots use a radial grid which is aligned with the profile centre and major-axis." ] }, { "cell_type": "code", "metadata": {}, "source": [ "print(sersic_light_profile.image_1d_from(grid=grid))\n", "\n", "light_profile_plotter.figures_1d(image=True)" ], "outputs": [], "execution_count": null }, { "cell_type": "markdown", "metadata": {}, "source": [ "__Mass Profiles__\n", "\n", "To perform ray-tracing, we require `MassProfile`'s, which are created via the `mass_profile_list` module and which is \n", "accessed via `mp` for conciseness. \n", "\n", "A `MassProfile` is an analytic function that describes the distribution of mass in a galaxy, and therefore \n", "can be used to derive its surface-density, gravitational potential and, most importantly, its deflection angles. In\n", "gravitational lensing, the deflection angles describe how light is deflected by the `MassProfile` due to the curvature \n", "of space-time.\n", "\n", "You'll note that we use `Sph` to concisely describe that this profile is spherical." ] }, { "cell_type": "code", "metadata": {}, "source": [ "sis_mass_profile = al.mp.IsothermalSph(centre=(0.0, 0.0), einstein_radius=1.6)\n", "\n", "print(sis_mass_profile)" ], "outputs": [], "execution_count": null }, { "cell_type": "markdown", "metadata": {}, "source": [ "__Deflection Angles__\n", "\n", "We can again use a `from_grid_` method to compute the deflection angles of a mass profile from a grid. \n", "\n", "The deflection angles are returned as the arc-second deflections of the grid's $(y,x)$ Cartesian components. This again \n", "uses the `Grid2D``s object meaning that we can access the deflection angles via the `native` and `slim` attributes. \n", "\n", "(If you are still unclear what exactly a deflection angle means or how it will help us with gravitational lensing,\n", "things should become a lot clearer in tutorial 4 of this chapter. For now, just look at the pretty pictures they make!)." ] }, { "cell_type": "code", "metadata": {}, "source": [ "mass_profile_deflections_yx_2d = sis_mass_profile.deflections_yx_2d_from(grid=grid)\n", "\n", "print(\"deflection-angles of `Grid2D` pixel 0:\")\n", "print(mass_profile_deflections_yx_2d.native[0, 0])\n", "print(\"deflection-angles of `Grid2D` pixel 1:\")\n", "print(mass_profile_deflections_yx_2d.slim[1])\n", "print()" ], "outputs": [], "execution_count": null }, { "cell_type": "markdown", "metadata": {}, "source": [ "A `MassProfilePlotter` can plot the deflection angles.\n", "\n", "(The black and white lines are called the `critical curve` and `caustic`. we'll cover what these are in a later tutorial.)" ] }, { "cell_type": "code", "metadata": {}, "source": [ "mass_profile_plottter = aplt.MassProfilePlotter(\n", " mass_profile=sis_mass_profile, grid=grid\n", ")\n", "mass_profile_plottter.figures_2d(deflections_y=True, deflections_x=True)" ], "outputs": [], "execution_count": null }, { "cell_type": "markdown", "metadata": {}, "source": [ "__Other Properties__\n", "\n", "`MassProfile`'s have a range of other properties that are used for lensing calculations, a couple of which we've plotted \n", "images of below:\n", "\n", " - `convergence`: The surface mass density of the mass profile in dimensionless units.\n", " - `potential`: The gravitational of the mass profile again in convenient dimensionless units.\n", " - `agnification`: Describes how much brighter each image-pixel appears due to focusing of light rays.\n", "\n", "Extracting `Array2D`'s of these quantities from **PyAutoLens** is exactly the same as for the image and deflection \n", "angles above." ] }, { "cell_type": "code", "metadata": {}, "source": [ "convergence_2d = sis_mass_profile.convergence_2d_from(grid=grid)\n", "potential_2d = sis_mass_profile.potential_2d_from(grid=grid)\n", "magnification_2d = sis_mass_profile.magnification_2d_from(grid=grid)" ], "outputs": [], "execution_count": null }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are also one dimensional versions of these quantities, which are computed on an annular radial grid." ] }, { "cell_type": "code", "metadata": {}, "source": [ "convergence_1d = sis_mass_profile.convergence_1d_from(grid=grid)\n", "potential_1d = sis_mass_profile.potential_1d_from(grid=grid)" ], "outputs": [], "execution_count": null }, { "cell_type": "markdown", "metadata": {}, "source": [ "Plotting them is equally straight forward, in both 1D and 2D." ] }, { "cell_type": "code", "metadata": {}, "source": [ "mass_profile_plottter.figures_2d(convergence=True, potential=True, magnification=True)\n", "mass_profile_plottter.figures_1d(convergence=True, potential=True)" ], "outputs": [], "execution_count": null }, { "cell_type": "markdown", "metadata": {}, "source": [ "__Wrap Up__\n", "\n", "Congratulations, you`ve completed your second **PyAutoLens** tutorial! \n", "\n", "Before moving on to the next one, experiment by doing the following:\n", "\n", "1) Change the `LightProfile`'s effective radius and Sersic index - how does the image's appearance change?\n", "2) Change the `MassProfile`'s einstein radius - what happens to the deflection angles, potential and convergence?\n", "3) Experiment with different `LightProfile`'s and `MassProfile`'s in the `light_profile` and `mass_profile` modules. \n", "In particular, try the `Isothermal` `Profile`, which introduces ellipticity into the mass distribution\n", "\n", "___Elliptical Components___\n", "\n", "The `ell_comps` describe the ellipticity of light and mass distributions. \n", "\n", "We can define a coordinate system where an ellipse is defined in terms of:\n", "\n", " - axis_ratio = semi-major axis / semi-minor axis = b/a\n", " - position angle, where angle is in degrees.\n", "\n", "See https://en.wikipedia.org/wiki/Ellipse for a full description of elliptical coordinates.\n", "\n", "The elliptical components are related to the axis-ratio and position angle as follows:\n", "\n", " fac = (1 - axis_ratio) / (1 + axis_ratio)\n", " \n", " elliptical_comp[0] = elliptical_comp_y = fac * np.sin(2 * angle)\n", " elliptical_comp[1] = elliptical_comp_x = fac * np.cos(2 * angle)\n", "\n", "We can use the **PyAutoLens** `convert` module to determine the elliptical components from an `axis_ratio` and `angle`,\n", "noting that the position angle is defined counter-clockwise from the positive x-axis." ] }, { "cell_type": "code", "metadata": {}, "source": [ "ell_comps = al.convert.ell_comps_from(axis_ratio=0.5, angle=45.0)\n", "\n", "print(ell_comps)" ], "outputs": [], "execution_count": null }, { "cell_type": "markdown", "metadata": {}, "source": [ "The reason light profiles and mass profiles use the elliptical components instead of an axis-ratio and position angle is\n", "because it improves the lens modeling process. What is lens modeling? You'll find out in chapter 2!" ] }, { "cell_type": "code", "metadata": {}, "source": [], "outputs": [], "execution_count": null } ], "metadata": { "anaconda-cloud": {}, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.1" } }, "nbformat": 4, "nbformat_minor": 4 }