Skip to content

Commit

Permalink
Merge branch 'dev' into pre-commit-ci-update-config
Browse files Browse the repository at this point in the history
  • Loading branch information
ConnorStoneAstro committed Jun 26, 2024
2 parents 7fc8a75 + 6d2d31b commit d66b9fc
Show file tree
Hide file tree
Showing 13 changed files with 1,119 additions and 298 deletions.
1 change: 1 addition & 0 deletions .codespell-whitelist
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ sie
childs
dout
din
tht
4 changes: 2 additions & 2 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ jobs:
ls -ltrh
ls -ltrh dist
- name: Publish to Test PyPI
uses: pypa/gh-action-pypi-publish@v1.8.14
uses: pypa/gh-action-pypi-publish@v1.9.0
with:
repository-url: https://test.pypi.org/legacy/
verbose: true
Expand Down Expand Up @@ -95,5 +95,5 @@ jobs:
name: artifact
path: dist

- uses: pypa/gh-action-pypi-publish@v1.8.14
- uses: pypa/gh-action-pypi-publish@v1.9.0
if: startsWith(github.ref, 'refs/tags')
7 changes: 6 additions & 1 deletion docs/source/_toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ chapters:
- file: websim
- file: tutorials/index
sections:
- file: tutorials/BasicIntroduction
- file: basicindex
sections:
- file: tutorials/BasicIntroduction_yaml
- file: tutorials/BasicIntroduction_oop
- file: tutorials/BasicIntroduction_func
- file: tutorials/LensZoo
- file: tutorials/VisualizeCaustics
- file: tutorials/MultiplaneDemo
Expand All @@ -23,6 +27,7 @@ chapters:
- file: examples/Example_ImageFit_NUTS
- file: examples/Example_QSOLensFit
- file: contributing
- file: frequently_asked_questions
- file: citation
- file: license
- file: modules
Expand Down
10 changes: 10 additions & 0 deletions docs/source/basicindex.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Basic Introduction
==================

There are three user interfaces to Caustics: configuration file, object-oriented, and functional.
Here we have built the same tutorial three times over so you can see how the three interfaces compare.
They provide progressively more freedom and power, but also require more knowledge of the underlying system.

1. Configuration File: :doc:`tutorials/BasicIntroduction_yaml`
2. Object-Oriented: :doc:`tutorials/BasicIntroduction_oop`
3. Functional: :doc:`tutorials/BasicIntroduction_func`
10 changes: 10 additions & 0 deletions docs/source/frequently_asked_questions.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FAQs - Frequently asked questions
=================================
| **Q:** How do I know what order to put the parameter values in the pytorch tensor which gets passed to the simulator?
| **A:** If you are using a simulator, then you can get the parameters using ``your_simulator.state_dict()``. The parameters whose values are set dynamically will say "None", while the static parameters will have their values shown. The order of the dynamical parameters corresponds to the order you should use in your parameter value tensor.
|
| **Q:** Why can I put the lens redshift at higher values than the source redshift or to negative values for some parametric models?
| **A:** We can calculate everything for those profiles with reduced deflection angles where the redshifts do not actually play into the calculation. If you use a profile defined by the lens mass, like a NFW lens, or a Multiplane lens then it does matter that the redshifts make sense and you will very likely get errors for those. Similarly, if you call the ``lens.physical_deflection_angle`` you will encounter errors.
|
| **Q:** I do (multiplane-)lensing with pixelated convergence using the pixelated kappa map of a parametric profile. The lensing effect differs from directly using the parametric lens. Why is the lensing effect different?
| **A:** Since you do pixelated convergence your mass is binned in pixels in a finite field of view (FOV) so you are missing some mass. At the limit of infinite resolution and infinite FOV the pixelated profile gives you the parametric profile. If the difference is above your error tolerance then you have to increase the resolution and/or FOV of your pixelated convergence map. Especially for SIE or EPL profiles (which go to infinity density in the center, and have infinite mass outside any FOV) you will miss infinite mass when pixelating.
278 changes: 278 additions & 0 deletions docs/source/tutorials/BasicIntroduction_func.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Welcome to Caustics! Functional\n",
"\n",
"Caustics is a powerful gravitational lensing simulator that can support users from beginner to highly advanced. In this tutorial we will cover the basics of the caustics functional interface. This one is a bit different from the other two since we are building from the ground up."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Simulating an SIE lens\n",
"\n",
"Here we will demo the very basics of lensing with a classic `SIE` lens model. We will see what it takes to make an `SIE` model, lens a background `Sersic` source, and sample the resulting image using a `Simulator`. Caustics simulators can generalize to very complex scenarios, here we will use a built-in simulator which handles a common use case (lensing a background source). To start, we of course need to import some modules. For the minimal example, this is just `matplotlib` a common package used for plotting, `torch` which is a numerical package for GPU/autodiff (much like `numpy`), and `caustics` the reason you are here.\n",
"\n",
"In this tutorial, we will guide you through the process of simulating an SIE lens the functional method, which is a bit different than the other two. This tutorial is mirrored in two other tutorials so you can see the `yaml`, object oriented, and functional interfaces.\n",
"\n",
"First, let's import the necessary packages:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Import the Necessary Packages\n",
"> **Note:** These packages need to be imported for both methods"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt\n",
"import torch\n",
"import caustics"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Define sampling grid\n",
"\n",
"To replicate the other mirrored tutorials, here we will sample on the same 100x100 grid. In those examples we do gaussian quadrature sub pixel integration so we will show how to do that here as well. The coordinates specified here are what we will eventually sample on in the simulator."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# First lets define the sampling grid\n",
"npix = 100\n",
"pixelscale = 0.05\n",
"thx, thy = caustics.utils.get_meshgrid(pixelscale, npix, npix)\n",
"gqx, gqy, gqW = caustics.utils.gaussian_quadrature_grid(\n",
" pixelscale, thx, thy, quad_level=3\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Simulation parameters\n",
"\n",
"Here we use the same parameters as the other tutorials. Note that `z_s` and `z_l` are not actually needed, but we keep them anyway for consistency. In the caustics object oriented framework, some lenses need to know their redshift so to keep a uniform interface we have all lenses require a redshift."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"x = torch.tensor([\n",
"# z_s z_l x0 y0 q phi b x0 y0 q phi n Re\n",
" 1.5, 0.5, -0.2, 0.0, 0.4, 1.5708, 1.7, 0.0, 0.0, 0.5, -0.985, 1.3, 1.0, \n",
"# Ie x0 y0 q phi n Re Ie\n",
" 5.0, -0.2, 0.0, 0.8, 0.0, 1., 1.0, 10.0\n",
"]) # fmt: skip"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Build a simulator\n",
"\n",
"Here we manually construct our own simulator using `caustics.func`. This will perform exactly the same operations as the other introduction tutorials, using the same functions. The only difference is that this version is more opaque and harder to debug since we have done everything manually. Note that it is more fragile too, internally we have specified the indices of `x` that are needed for the lens and light distributions, if we swapped out the `sie` with and `nfw` then all the indices would need to change. This is why using the yaml or oop caustics interfaces is easier to play around with different profiles and configurations without everything breaking. Also note that we aren't doing PSF convolution, that would add another layer of complexity and would be tedious to do here, though it is very easy in the other interfaces.\n",
"\n",
"Still, this is such a simple function it represents the optimal use of compute resources, it will be faster than the other methods. Further, you have complete freedom to change whatever you like! This makes it ideal for exploring new ideas."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def sim(x):\n",
" # Compute deflection angles\n",
" ax, ay = caustics.func.reduced_deflection_angle_sie(*x[2:7], gqx, gqy)\n",
" # Raytrace with lens equation\n",
" bx, by = gqx - ax, gqy - ay\n",
" # Lens background source light\n",
" k = caustics.func.k_sersic(x[11])\n",
" mu_fine = caustics.func.brightness_sersic(*x[7:14], bx, by, k)\n",
" mu = caustics.utils.gaussian_quadrature_integrator(mu_fine, gqW)\n",
" # Add lens light\n",
" k = caustics.func.k_sersic(x[18])\n",
" mu_fine = caustics.func.brightness_sersic(*x[14:], gqx, gqy, k)\n",
" mu += caustics.utils.gaussian_quadrature_integrator(mu_fine, gqW)\n",
" return mu"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Plot the Results!\n",
"\n",
"This section is mostly self explanatory. We evaluate the simulator configuration by calling it like a function. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Substitute sim with sim for yaml method\n",
"image = sim(x).detach().cpu().numpy()\n",
"\n",
"plt.imshow(image, origin=\"lower\")\n",
"plt.axis(\"off\")\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### The Simulator Graph\n",
"\n",
"There is no simulator graph for our manually constructed simulator function, not unless we make it!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Sampling with a Simulator\n",
"\n",
"Now let's see how to use some of the powerful features of the simulator we have created. Note that it is a function, allowing us to take advantage of many PyTorch features. To start, lets see how we can run batches of lens simulations using `vmap`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"newx = x.repeat(20, 1)\n",
"newx += torch.normal(mean=0, std=0.1 * torch.ones_like(newx))\n",
"\n",
"images = torch.vmap(sim)(newx) # Substitute minisim with sim for the yaml method\n",
"\n",
"fig, axarr = plt.subplots(4, 5, figsize=(20, 16))\n",
"for ax, im in zip(axarr.flatten(), images):\n",
" ax.imshow(im, origin=\"lower\")\n",
" ax.axis(\"off\")\n",
"plt.tight_layout()\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Gradients with autodiff\n",
"\n",
"Batching is useful for fully paramellizing code and maximally using computational resources, but autodiff gradients allow whole new algorithms and techniques to be used in gravitational lensing! Let's try computing the Jacobian for the lensing configuration that we have been using so far. The result is a grid of images that show how the lensing simulation image would change if we adjusted each parameter individually. Thanks to autodiff, these derivatives have no finite differences approximation error, they are exact up to the machine precision."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"J = torch.func.jacfwd(sim)(x) # Substitute minisim with sim for the yaml method\n",
"\n",
"fig, axarr = plt.subplots(3, 7, figsize=(20, 9))\n",
"labels = [\n",
" \"z_s\",\n",
" \"z_l\",\n",
" \"lens x0\",\n",
" \"lens y0\",\n",
" \"lens q\",\n",
" \"lens phi\",\n",
" \"lens b\",\n",
" \"source x0\",\n",
" \"source y0\",\n",
" \"source q\",\n",
" \"source phi\",\n",
" \"source n\",\n",
" \"source Re\",\n",
" \"source Ie\",\n",
" \"lenslight x0\",\n",
" \"lenslight y0\",\n",
" \"lenslight q\",\n",
" \"lenslight phi\",\n",
" \"lenslight n\",\n",
" \"lenslight Re\",\n",
" \"lenslight Ie\",\n",
"]\n",
"for i, ax in enumerate(axarr.flatten()):\n",
" ax.imshow(J[..., i], origin=\"lower\")\n",
" ax.set_title(labels[i])\n",
" ax.axis(\"off\")\n",
"plt.tight_layout()\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Model Parameters\n",
"\n",
"In order, here is an explanation of the parameters. \n",
"- `z_s` is the redshift of the source. \n",
"- `z_l` is the lens redshift which tells the lens how far away it is from the observer (us). \n",
"- The next two parameters `x0` and `y0` indicate where the lens is relative to the main optical axis, which is the coordinates `(0, 0)`. \n",
"- The `q` parameter gives the axis ratio for the `SIE`, so it knows how elongated it is. \n",
"- `phi` indicates the position angle (where the ellipse is pointing). \n",
"- `b` gives the Einstein radius (in arcsec) of the lens. \n",
"- The next `x0` and `y0` provide the position relative to the main optical axis of the Sersic source, here we offset the source slightly to make for an interesting figure. \n",
"- The `q` parameter defines the axis ratio of the Sersic ellipse. \n",
"- `phi` defines the position angle of the ellipse. \n",
"- `n` is the Sersic index which determines how concentrated the light is; `n=0.5` is a Gaussian distribution, `n=1` is an exponential, `n=4` is a De Vaucouleurs profile. \n",
"- `Re` is the radius (in arcsec) within which half the total light of the profile is enclosed. \n",
"- `Ie` is the brightness at `Re`. \n",
"- The next set of parameters are also Sersic parameters, but this time they are for the lens light model."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
Loading

0 comments on commit d66b9fc

Please sign in to comment.