From 4f95d4ef410bfdd2ada21ac7e7fca0b47bbac43f Mon Sep 17 00:00:00 2001 From: Francois Date: Mon, 19 Oct 2020 12:50:29 +0200 Subject: [PATCH 01/18] Adding link to develop branch --- .gitmodules | 3 +++ dev | 1 + 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 dev diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..ff56e5e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "Shear-and-PSF-Reading-Group"] + path = dev + url = https://github.com/CosmoStat/Shear-and-PSF-Reading-Group.git diff --git a/dev b/dev new file mode 160000 index 0000000..c507830 --- /dev/null +++ b/dev @@ -0,0 +1 @@ +Subproject commit c5078301d1afa4a8d480f036ccff8e62320afb1b From a6dfb0954699fd53279fe0d2cc0980ebb52ece99 Mon Sep 17 00:00:00 2001 From: Samuel Farrens Date: Fri, 27 Nov 2020 15:40:59 +0100 Subject: [PATCH 02/18] removed unused submodule --- .gitignore | 1 + .gitmodules | 3 --- dev | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) delete mode 100644 .gitmodules delete mode 160000 dev diff --git a/.gitignore b/.gitignore index b6e4761..2386a80 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ __pycache__/ # Distribution / packaging .Python build/ +*_build* develop-eggs/ dist/ downloads/ diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index ff56e5e..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "Shear-and-PSF-Reading-Group"] - path = dev - url = https://github.com/CosmoStat/Shear-and-PSF-Reading-Group.git diff --git a/dev b/dev deleted file mode 160000 index c507830..0000000 --- a/dev +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c5078301d1afa4a8d480f036ccff8e62320afb1b From 600f362702427f5199fac0d5744274837bc31818 Mon Sep 17 00:00:00 2001 From: mkilbing Date: Sun, 29 Nov 2020 13:05:44 +0100 Subject: [PATCH 03/18] created branch martin from develop --- .github/workflows/deploy_dev.yml | 49 ++ .github/workflows/deploy_pages.yml | 34 ++ .github/workflows/test_build.yml | 29 + README.md | 114 +++- environment.yml | 11 + notebooks/moments.ipynb | 462 ++++++++++++++++ requirements.txt | 4 + setup.py | 11 + shearbook/_bibliography/z_bias.bib | 37 ++ shearbook/_bibliography/z_calibration.bib | 0 shearbook/_bibliography/z_model.bib | 0 shearbook/_bibliography/z_moments.bib | 20 + shearbook/_bibliography/z_moments_methods.bib | 20 + shearbook/_config.yml | 69 +++ shearbook/_static/download.png | Bin 0 -> 5033 bytes shearbook/_static/kacprazak_bias.jpg | Bin 0 -> 79779 bytes shearbook/_static/logo.png | Bin 0 -> 9854 bytes shearbook/_static/rocket.png | Bin 0 -> 5271 bytes shearbook/_toc.yml | 42 ++ shearbook/bias/bias-intro.md | 8 + shearbook/bias/bias-ref.md | 5 + shearbook/bias/model-bias.ipynb | 95 ++++ shearbook/bias/noise-bias.ipynb | 83 +++ shearbook/calibration/calibration-intro.md | 9 + shearbook/calibration/calibration-ref.md | 5 + shearbook/contrib/contributors.md | 26 + shearbook/contrib/guidelines.md | 119 ++++ shearbook/contrib/style.md | 5 + shearbook/home.md | 3 + shearbook/intro/about.md | 3 + shearbook/intro/run_code.md | 29 + shearbook/methods/ksb-variants.ipynb | 48 ++ shearbook/methods/ksb.ipynb | 48 ++ shearbook/methods/moments-methods-ref.md | 5 + shearbook/methods/moments-methods.md | 8 + shearbook/methods/viola2011.md | 73 +++ shearbook/model/model-intro.md | 9 + shearbook/model/model-ref.md | 5 + shearbook/moments/moments-append.md | 108 ++++ shearbook/moments/moments-basic.ipynb | 509 ++++++++++++++++++ shearbook/moments/moments-intro.md | 8 + shearbook/moments/moments-ref.md | 5 + shearbook/moments/moments-weighted.ipynb | 49 ++ shrbk/__init__.py | 0 shrbk/data.py | 35 ++ shrbk/interact.py | 59 ++ shrbk/plot.py | 51 ++ 47 files changed, 2310 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/deploy_dev.yml create mode 100644 .github/workflows/deploy_pages.yml create mode 100644 .github/workflows/test_build.yml create mode 100644 environment.yml create mode 100644 notebooks/moments.ipynb create mode 100644 requirements.txt create mode 100644 setup.py create mode 100644 shearbook/_bibliography/z_bias.bib create mode 100644 shearbook/_bibliography/z_calibration.bib create mode 100644 shearbook/_bibliography/z_model.bib create mode 100644 shearbook/_bibliography/z_moments.bib create mode 100644 shearbook/_bibliography/z_moments_methods.bib create mode 100644 shearbook/_config.yml create mode 100644 shearbook/_static/download.png create mode 100644 shearbook/_static/kacprazak_bias.jpg create mode 100644 shearbook/_static/logo.png create mode 100644 shearbook/_static/rocket.png create mode 100644 shearbook/_toc.yml create mode 100644 shearbook/bias/bias-intro.md create mode 100644 shearbook/bias/bias-ref.md create mode 100644 shearbook/bias/model-bias.ipynb create mode 100644 shearbook/bias/noise-bias.ipynb create mode 100644 shearbook/calibration/calibration-intro.md create mode 100644 shearbook/calibration/calibration-ref.md create mode 100644 shearbook/contrib/contributors.md create mode 100644 shearbook/contrib/guidelines.md create mode 100644 shearbook/contrib/style.md create mode 100644 shearbook/home.md create mode 100644 shearbook/intro/about.md create mode 100644 shearbook/intro/run_code.md create mode 100644 shearbook/methods/ksb-variants.ipynb create mode 100644 shearbook/methods/ksb.ipynb create mode 100644 shearbook/methods/moments-methods-ref.md create mode 100644 shearbook/methods/moments-methods.md create mode 100644 shearbook/methods/viola2011.md create mode 100644 shearbook/model/model-intro.md create mode 100644 shearbook/model/model-ref.md create mode 100644 shearbook/moments/moments-append.md create mode 100644 shearbook/moments/moments-basic.ipynb create mode 100644 shearbook/moments/moments-intro.md create mode 100644 shearbook/moments/moments-ref.md create mode 100644 shearbook/moments/moments-weighted.ipynb create mode 100644 shrbk/__init__.py create mode 100644 shrbk/data.py create mode 100644 shrbk/interact.py create mode 100644 shrbk/plot.py diff --git a/.github/workflows/deploy_dev.yml b/.github/workflows/deploy_dev.yml new file mode 100644 index 0000000..c58815e --- /dev/null +++ b/.github/workflows/deploy_dev.yml @@ -0,0 +1,49 @@ +name: CI + +on: + push: + branches: + - develop + +jobs: + deploy-dev: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python 3.8 + uses: actions/setup-python@v1 + with: + python-version: 3.8 + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install -r requirements.txt + python -m pip install . + + - name: Build Book + run: | + jupyter-book build shearbook + + - name: Deploy Development Pages + uses: peaceiris/actions-gh-pages@v3.5.9 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: shearbook/_build/html + publish_branch: dev-pages + + - name: Update Submodule on GH-Pages + uses: actions/checkout@v2 + with: + ref: gh-pages + - run: | + git submodule init + git submodule update --remote --merge + git submodule status + git config user.name github-actions + git config user.email github-actions@github.com + git add develop + git commit -m "updated dev-pages submodule" + git push origin gh-pages diff --git a/.github/workflows/deploy_pages.yml b/.github/workflows/deploy_pages.yml new file mode 100644 index 0000000..b0264a3 --- /dev/null +++ b/.github/workflows/deploy_pages.yml @@ -0,0 +1,34 @@ +name: CI + +on: + push: + branches: + - master + +jobs: + deploy: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python 3.8 + uses: actions/setup-python@v1 + with: + python-version: 3.8 + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install -r requirements.txt + python -m pip install . + + - name: Build Book + run: | + jupyter-book build shearbook + + - name: Deploy Pages + uses: peaceiris/actions-gh-pages@v3.5.9 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: shearbook/_build/html diff --git a/.github/workflows/test_build.yml b/.github/workflows/test_build.yml new file mode 100644 index 0000000..edf30fb --- /dev/null +++ b/.github/workflows/test_build.yml @@ -0,0 +1,29 @@ +name: CI + +on: + pull_request: + branches: + - develop + - master + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python 3.8 + uses: actions/setup-python@v1 + with: + python-version: 3.8 + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install -r requirements.txt + python -m pip install . + + - name: Build Book + run: | + jupyter-book build shearbook diff --git a/README.md b/README.md index 8d8cbdc..677d7f5 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,113 @@ -# Shear & PSF Reading Group -Notes and tools derived from reading groups sessions +# Shear Book +Welcome to the Shear and PSF reading group repository! + +The following steps will help you get set up to contribute to the book. + +## Requirements + +To build the book you will need the following packages. + +- [numpy](https://numpy.org/) +- [scipy](https://www.scipy.org/) +- [matplotlib](https://matplotlib.org/) +- [Jupyter](https://jupyter.org/) +- [Jupyter Book](https://jupyterbook.org/intro.html) + +You can install them as follows + +```bash +$ python -m pip install -r requirements.txt +``` + +alternatively you can build the `shear` conda environment. + +```bash +$ conda env create -f environment.yml +$ conda activate shear +``` + +## Install the `shrbk` library + +`shrbk` is the library made for this book. +Install it ither in development mode + +```bash +$ python -m pip install -e . +``` + +or plainly + +```bash +$ python -m pip install . +``` + +## Building + +To build the book locally run + +```bash +$ jupyter-book build shearbook +``` + +in the root of the directory. This will build the HTML files and provide a link to the `index.html` so that you can view the book in your browser. + +## Cleaning + +Similarly, to clean the locally builded files run + +```bash +$ jupyter-book clean shearbook +``` + +in the root directory. This will remove the `_build` directory. + + +## Adding Content + +### New Pages + +To create a new page simply write a new markdown file or Jupyter notebook and the file name to the `_toc.yml` file to specify where the page should be included in the book. + +*e.g.* For a file called `new_content.md` you would add + +```yaml +- file: new_content +``` + +to the appropriate section of `_toc.yml`. + +See the [Jupyter Book documentation](https://jupyterbook.org/content/content-blocks.html) for tips on including special content blocks, equations and references. + +### Bibliography + +To add a bibliography page: + +1. Create a new `.bib` file with standard BibTeX content. +> Be sure to prefix the file name with `z_` as this will ensure that the file is sourced after the content files. This makes it possible to cite papers across multiple pages. + +2. Create a markdown file ending with `-ref.md` (for the sake of consistency) and include the following. + +````markdown +# References + +```{bibliography} _bibliography/z_.bib +:all: +``` +```` + +where `` is the name of your `.bib` file. + +### Interactive Notebooks + +If you prepare an additional interactive version (*i.e.* with widgets) of a given notebook, place it in the `notebooks` directory and reference it in the text as follows + +```python +from IPython.display import Markdown +from shrbk.interact import get_url, make_html_binder_button + +# Provide binder badge +Markdown(make_html_binder_button(get_url('.ipynb')) +``` + +This will embed a Binder badge that links to the interactive notebook. diff --git a/environment.yml b/environment.yml new file mode 100644 index 0000000..7b3fd35 --- /dev/null +++ b/environment.yml @@ -0,0 +1,11 @@ +name: shear +channels: + - conda-forge + - defaults +dependencies: + - jupyter + - matplotlib + - numpy + - scipy + - pip: + - jupyter-book diff --git a/notebooks/moments.ipynb b/notebooks/moments.ipynb new file mode 100644 index 0000000..457c1bd --- /dev/null +++ b/notebooks/moments.ipynb @@ -0,0 +1,462 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Moments Basics\n", + "\n", + "In this section we will take a look at the basics of moment based methods for measuring galaxy shapes.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from shrbk.plot import *\n", + "from shrbk.data import * " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Data\n", + "\n", + "Let's begin by looking at some dummy images with properties we can easily discern by eye." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "165bac5deeb34ce5a433081709bec6bf", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "interactive(children=(Dropdown(description='data_name', options=('Horizontal Line', 'Vertical Line', 'Diagonal…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "@interact(data_name=data_dict.keys())\n", + "def show_data(data_name):\n", + " \n", + " print(data_dict[data_name])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Image Flux\n", + "\n", + "Now we can look at the flux density or surface brightness per pixel, $I(x, y)$ , and then dermine the total flux of the image, $F$." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "15f5b4a24acd41bba5290a42ed9a19ba", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "interactive(children=(Dropdown(description='data_name', options=('Horizontal Line', 'Vertical Line', 'Diagonal…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "@interact(data_name=data_dict.keys())\n", + "def show_flux(data_name):\n", + " \n", + " show_image(data_dict[data_name], show_flux=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\\begin{equation}\n", + " \\large F = \\int I(x,y) dx dy\n", + "\\end{equation}\n", + "\n", + "\n", + "\\begin{equation}\n", + " \\large F = \\sum_{x,y} I(x,y)\n", + "\\end{equation}" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "ed0592b3bd634883918e135b6e33b300", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "interactive(children=(Dropdown(description='data_name', options=('Horizontal Line', 'Vertical Line', 'Diagonal…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "@interact(data_name=data_dict.keys())\n", + "def print_total_flux(data_name):\n", + " \n", + " print('F = {}'.format(np.sum(data_dict[data_name])))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Centroid" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "$$\n", + " \\large \\bar{x} = \\frac{\\sum_{x,y} xI(x,y)}{\\sum_{x,y} I(x,y)}\n", + "$$ (eq:centroid_x)\n", + "\n", + "$$\n", + " \\large \\bar{y} = \\frac{\\sum_{x,y} yI(x,y)}{\\sum_{x,y} I(x,y)}\n", + "$$ (eq:centroid_x)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "def get_centroid(data):\n", + " \n", + " # Sum flux over x and y individually\n", + " sum_i = np.array([np.sum(data, axis=i) for i in (1, 0)])\n", + " \n", + " # Get range of x and y values\n", + " ranges = np.array([np.arange(i) for i in data.shape])\n", + " \n", + " # Calculate centroids\n", + " cents = np.sum(sum_i * ranges, axis=1) / np.sum(data)\n", + " \n", + " return cents.astype(int)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "22ef5fe2625947409c2d44912fbfd89b", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "interactive(children=(Dropdown(description='data_name', options=('Horizontal Line', 'Vertical Line', 'Diagonal…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "@interact(data_name=data_dict.keys())\n", + "def show_centroid(data_name):\n", + " \n", + " data = data_dict[data_name]\n", + " centroid = get_centroid(data)\n", + " \n", + " show_image(data, centroid=centroid)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Moments\n", + "\n", + "\\begin{equation}\n", + " \\large Q_{xy} = \\frac{\\sum_{x,y} I(x,y)(x - \\bar{x})(y - \\bar{y})}{\\sum_{x,y} I(x,y)}\n", + "\\end{equation}" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "def get_moments(data):\n", + " \n", + " centroid = get_centroid(data)\n", + " ranges = np.array([np.arange(i) for i in data.shape])\n", + " \n", + " x = np.outer(ranges[0] - centroid[0], np.ones(data.shape[1]))\n", + " y = np.outer(np.ones(data.shape[0]), ranges[1] - centroid[1])\n", + " \n", + " q = np.array([np.sum(data * xi * xj) for xi in (x, y) for xj in (x, y)])\n", + " q = (q / np.sum(data)).reshape(2, 2).astype('complex')\n", + " \n", + " return q" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "57aa3723362d49809c7061866ed909ab", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "interactive(children=(Dropdown(description='data_name', options=('Horizontal Line', 'Vertical Line', 'Diagonal…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "@interact(data_name=data_dict.keys())\n", + "def print_moments(data_name):\n", + " \n", + " print(get_moments(data_dict[data_name]))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Ellipticity\n", + "\n", + "\\begin{equation}\n", + " \\large \\chi = \\frac{Q_{00}-Q_{11}+2iQ_{01}}{Q_{00}+Q_{11}}\n", + "\\end{equation}\n", + "\n", + "\\begin{equation}\n", + " \\large \\epsilon = \\frac{Q_{00}-Q_{11}+2iQ_{01}}{Q_{00}+Q_{11}+2\\sqrt{Q_{00}Q_{11}-Q_{01}^2}}\n", + "\\end{equation}" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "def get_ellipticity(data, method='chi'):\n", + " \n", + " # Calculate moments\n", + " q = get_moments(data)\n", + " \n", + " # Calculate the size.\n", + " r2 = q[0, 0] + q[1, 1]\n", + "\n", + " # Calculate the numerator\n", + " num = (q[0, 0] - q[1, 1] + 2 * np.complex(0, q[0, 1]))\n", + " \n", + " # Calculate the denominator\n", + " den = r2\n", + " \n", + " if method == 'epsilon':\n", + " den += 2 * np.sqrt(q[0, 0] * q[1, 1] - q[0, 1] ** 2)\n", + " \n", + " # Calculate the ellipticity\n", + " ellip = num / den\n", + "\n", + " return np.around([ellip.real, ellip.imag], 3)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "6656d4dfc609470d9a734ab789ef266a", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "interactive(children=(Dropdown(description='data_name', options=('Horizontal Line', 'Vertical Line', 'Diagonal…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "@interact(data_name=data_dict.keys())\n", + "def show_centroid(data_name):\n", + " \n", + " data = data_dict[data_name]\n", + " centroid = get_centroid(data)\n", + " chi = get_ellipticity(data)\n", + " epsilon = get_ellipticity(data, method='epsilon')\n", + " \n", + " show_image(data, centroid=centroid, chi=chi, epsilon=epsilon)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\\begin{equation}\n", + " \\large a^2 = \\frac{Q_{00}+Q_{11}+\\sqrt{(Q_{00}-Q_{11})^2 + 4Q_{01}^2}}{2}\n", + "\\end{equation}\n", + "\n", + "\\begin{equation}\n", + " \\large b^2 = \\frac{Q_{00}+Q_{11}-\\sqrt{(Q_{00}-Q_{11})^2 + 4Q_{01}^2}}{2}\n", + "\\end{equation}\n", + "\n", + "\\begin{equation}\n", + " \\large \\tan{2\\theta} = \\frac{2Q_{01}}{Q_{00}-Q_{11}}\n", + "\\end{equation}" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "def get_abt(data):\n", + " \n", + " q = get_moments(data)\n", + " \n", + " qq_plus = q[0, 0] + q[1, 1]\n", + " qq_minus = q[0, 0] - q[1, 1]\n", + " root = np.sqrt(qq_minus ** 2 + 4 * q[0, 1] ** 2)\n", + " \n", + " a = np.around(np.real(np.sqrt(0.5 * (qq_plus + root))), 3)\n", + " b = np.around(np.real(np.sqrt(0.5 * (qq_plus - root))), 3)\n", + " if qq_minus == 0.0:\n", + " theta = -45.0 * np.sign(np.real(q[0, 1]))\n", + " else:\n", + " theta = np.around(np.real(0.5 * np.arctan(2 * q[0, 1] / qq_minus)) * 180. / np.pi, 3)\n", + " \n", + " if qq_minus > 0.0 and q[0, 1] == 0.0:\n", + " theta += 90.0\n", + " elif qq_minus > 0.0:\n", + " theta -= 90.0 * np.sign(np.real(q[0, 1]))\n", + " \n", + " return a, b, theta" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "65b34552060b49a1acbb333530aeef9f", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "interactive(children=(IntSlider(value=3, description='a', max=5, min=1), IntSlider(value=3, description='b', m…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "@interact(a=(1, 5, 1), b=(1, 5, 1), theta=(-90, 90, 15))\n", + "def show_ellipse(a, b, theta):\n", + " \n", + " data = make_ellipse(a, b, theta)\n", + " centroid = get_centroid(data)\n", + " chi = get_ellipticity(data)\n", + " epsilon = get_ellipticity(data, method='epsilon')\n", + " abt = get_abt(data)\n", + " \n", + " show_image(data, centroid=centroid, chi=chi, epsilon=epsilon, abt=abt)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Edit Metadata", + "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.8.5" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e0eccbb --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +jupyter-book +matplotlib +numpy +scipy diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..2c01230 --- /dev/null +++ b/setup.py @@ -0,0 +1,11 @@ +from setuptools import setup, find_packages + +setup( + name='shrbk', + version='0.0.1', + url='https://github.com/CosmoStat/Shear-and-PSF-Reading-Group/', + author='ShearBook Authors', + description='Shear book ccompanying code', + packages=find_packages(), + install_requires=['numpy', 'scipy', 'matplotlib', 'jupyter-book'], +) \ No newline at end of file diff --git a/shearbook/_bibliography/z_bias.bib b/shearbook/_bibliography/z_bias.bib new file mode 100644 index 0000000..0f687bf --- /dev/null +++ b/shearbook/_bibliography/z_bias.bib @@ -0,0 +1,37 @@ +@ARTICLE{2014MNRAS.441.2528K, + author = {{Kacprzak}, Tomasz and {Bridle}, Sarah and {Rowe}, Barnaby and + {Voigt}, Lisa and {Zuntz}, Joe and {Hirsch}, Michael and + {MacCrann}, Niall}, + title = "{S{\'e}rsic galaxy models in weak lensing shape measurement: model bias, noise bias and their interaction}", + journal = {\mnras}, + keywords = {gravitational lensing: weak, methods: data analysis, methods: statistical, techniques: image processing, cosmology: observations, Astrophysics - Cosmology and Nongalactic Astrophysics}, + year = 2014, + month = jul, + volume = {441}, + number = {3}, + pages = {2528-2538}, + doi = {10.1093/mnras/stu588}, +archivePrefix = {arXiv}, + eprint = {1308.4663}, + primaryClass = {astro-ph.CO}, + adsurl = {https://ui.adsabs.harvard.edu/abs/2014MNRAS.441.2528K}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + +@ARTICLE{2012MNRAS.424.2757M, + author = {{Melchior}, P. and {Viola}, M.}, + title = "{Means of confusion: how pixel noise affects shear estimates for weak gravitational lensing}", + journal = {\mnras}, + keywords = {gravitational lensing: weak, techniques: image processing, Astrophysics - Instrumentation and Methods for Astrophysics, Astrophysics - Cosmology and Extragalactic Astrophysics}, + year = 2012, + month = aug, + volume = {424}, + number = {4}, + pages = {2757-2769}, + doi = {10.1111/j.1365-2966.2012.21381.x}, +archivePrefix = {arXiv}, + eprint = {1204.5147}, + primaryClass = {astro-ph.IM}, + adsurl = {https://ui.adsabs.harvard.edu/abs/2012MNRAS.424.2757M}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} \ No newline at end of file diff --git a/shearbook/_bibliography/z_calibration.bib b/shearbook/_bibliography/z_calibration.bib new file mode 100644 index 0000000..e69de29 diff --git a/shearbook/_bibliography/z_model.bib b/shearbook/_bibliography/z_model.bib new file mode 100644 index 0000000..e69de29 diff --git a/shearbook/_bibliography/z_moments.bib b/shearbook/_bibliography/z_moments.bib new file mode 100644 index 0000000..736c9ea --- /dev/null +++ b/shearbook/_bibliography/z_moments.bib @@ -0,0 +1,20 @@ +--- +--- + +@ARTICLE{BartelmannSchneider:01, + author = {{Bartelmann}, M. and {Schneider}, P.}, + title = "{Weak gravitational lensing}", + journal = {\physrep}, + keywords = {Astrophysics}, + year = 2001, + month = jan, + volume = {340}, + number = {4-5}, + pages = {291-472}, + doi = {10.1016/S0370-1573(00)00082-X}, +archivePrefix = {arXiv}, + eprint = {astro-ph/9912508}, + primaryClass = {astro-ph}, + adsurl = {https://ui.adsabs.harvard.edu/abs/2001PhR...340..291B}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} diff --git a/shearbook/_bibliography/z_moments_methods.bib b/shearbook/_bibliography/z_moments_methods.bib new file mode 100644 index 0000000..25d362d --- /dev/null +++ b/shearbook/_bibliography/z_moments_methods.bib @@ -0,0 +1,20 @@ +--- +--- + +@ARTICLE{2011MNRAS.410.2156V, + author = {{Viola}, M. and {Melchior}, P. and {Bartelmann}, M.}, + title = "{Biases in, and corrections to, KSB shear measurements}", + journal = {MNRAS}, + keywords = {methods: data analysis, cosmology: observations, Astrophysics - Cosmology and Nongalactic Astrophysics, Astrophysics - Astrophysics of Galaxies}, + year = 2011, + month = feb, + volume = {410}, + number = {4}, + pages = {2156-2166}, + doi = {10.1111/j.1365-2966.2010.17589.x}, +archivePrefix = {arXiv}, + eprint = {1006.2470}, + primaryClass = {astro-ph.CO}, + adsurl = {https://ui.adsabs.harvard.edu/abs/2011MNRAS.410.2156V}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} diff --git a/shearbook/_config.yml b/shearbook/_config.yml new file mode 100644 index 0000000..13c6f8e --- /dev/null +++ b/shearbook/_config.yml @@ -0,0 +1,69 @@ +####################################################################################### +# Book settings +title : Shear Book +author : CosmoStat # The author of the book +copyright : "2020" # Copyright year to be placed in the footer +logo : _static/logo.png # A path to the book logo +# Patterns to skip when building the book. Can be glob-style (e.g. "*skip.ipynb") +exclude_patterns : [_build, Thumbs.db, .DS_Store, "**.ipynb_checkpoints", README.md] + +####################################################################################### +# Execution settings +execute: + execute_notebooks : auto # Whether to execute notebooks at build time. Must be one of ("auto", "force", "cache", "off") + cache : "" # A path to the jupyter cache that will be used to store execution artifacs. Defaults to `_build/.jupyter_cache/` + exclude_patterns : [] # A list of patterns to *skip* in execution (e.g. a notebook that takes a really long time) + timeout : 30 # The maximum time (in seconds) each notebook cell is allowed to run. + run_in_temp : false # If `True`, then a temporary directory will be created and used as the command working directory (cwd), + # otherwise the notebook's parent directory will be the cwd. + allow_errors : false # If `False`, when a code cell raises an error the execution is stopped, otherwise all cells are always run. + stderr_output : show # One of 'show', 'remove', 'remove-warn', 'warn', 'error', 'severe' + +####################################################################################### +# Parse and render settings +parse: + myst_extended_syntax : true # enable MyST extended syntax support (see documents for details) + myst_url_schemes : [mailto, http, https] # URI schemes that will be recognised as external URLs in Markdown links + +####################################################################################### +# HTML-specific settings +html: + favicon : "" # A path to a favicon image + use_edit_page_button : true # Whether to add an "edit this page" button to pages. If `true`, repository information in repository: must be filled in + use_repository_button : true # Whether to add a link to your repository button + use_issues_button : true # Whether to add an "open an issue" button + extra_navbar : Powered by Jupyter Book # Will be displayed underneath the left navbar. + extra_footer : "" # Will be displayed underneath the footer. + google_analytics_id : "" # A GA id that can be used to track book views. + home_page_in_navbar : true # Whether to include your home page in the left Navigation Bar + baseurl : "" # The base URL where your book will be hosted. Used for creating image previews and social links. e.g.: https://mypage.com/mybook/ + comments: + hypothesis : false + utterances : false + +####################################################################################### +# LaTeX-specific settings +latex: + latex_engine : pdflatex # one of 'pdflatex', 'xelatex' (recommended for unicode), 'luatex', 'platex', 'uplatex' + +####################################################################################### +# Launch button settings +launch_buttons: + notebook_interface : classic # The interface interactive links will activate ["classic", "jupyterlab"] + binderhub_url : https://mybinder.org # The URL of the BinderHub (e.g., https://mybinder.org) + jupyterhub_url : "" # The URL of the JupyterHub (e.g., https://datahub.berkeley.edu) + thebe : true # Add a thebe button to pages (requires the repository to run on Binder) + colab_url : https://colab.research.google.com # The URL of Google Colab (e.g., https://colab.research.google.com) + +repository: + url : https://github.com/CosmoStat/Shear-and-PSF-Reading-Group # The URL to your book's repository + path_to_book : shearbook # A path to your book's folder, relative to the repository root. + branch : develop # Which branch of the repository should be used when creating links + +####################################################################################### +# Advanced and power-user settings +sphinx: + extra_extensions : # A list of extra extensions to load by Sphinx (added to those already used by JB). + config : # key-value pairs to directly over-ride the Sphinx configuration + +####################################################################################### diff --git a/shearbook/_static/download.png b/shearbook/_static/download.png new file mode 100644 index 0000000000000000000000000000000000000000..98c6ae75cacf65908a2f857f23eb37a3f3a9a4c5 GIT binary patch literal 5033 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 literal 0 HcmV?d00001 diff --git a/shearbook/_static/logo.png b/shearbook/_static/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..06d56f40c838b64eb048a63e036125964a069a3a GIT binary patch literal 9854 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 L>8MsHTOs~GC;Fmw literal 0 HcmV?d00001 diff --git a/shearbook/_static/rocket.png b/shearbook/_static/rocket.png new file mode 100644 index 0000000000000000000000000000000000000000..2a0cea5182f9408f8c9ee49703e64739882b4649 GIT binary patch literal 5271 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 literal 0 HcmV?d00001 diff --git a/shearbook/_toc.yml b/shearbook/_toc.yml new file mode 100644 index 0000000..aa5aba1 --- /dev/null +++ b/shearbook/_toc.yml @@ -0,0 +1,42 @@ +- file: home + numbered: true + +- part: Introduction + chapters: + - file: intro/about + - file: intro/run_code + +- part: Shape Measurement + chapters: + - file: moments/moments-intro + sections: + - file: moments/moments-basic + - file: moments/moments-weighted + - file: moments/moments-append + - file: moments/moments-ref + - file: methods/moments-methods + sections: + - file: methods/ksb + - file: methods/ksb-variants + - file: methods/viola2011 + - file: methods/moments-methods-ref + - file: model/model-intro + sections: + - file: model/model-ref + +- part: Bias and Calibration + chapters: + - file: bias/bias-intro + sections: + - file: bias/model-bias + - file: bias/noise-bias + - file: bias/bias-ref + - file: calibration/calibration-intro + sections: + - file: calibration/calibration-ref + +- part: Contributing + chapters: + - file: contrib/contributors + - file: contrib/guidelines + - file: contrib/style diff --git a/shearbook/bias/bias-intro.md b/shearbook/bias/bias-intro.md new file mode 100644 index 0000000..8f94b29 --- /dev/null +++ b/shearbook/bias/bias-intro.md @@ -0,0 +1,8 @@ +# Shear Biases + +In this section we will take a look at sources of bias that arise when measuring galaxy shapes. + +**Contents** + +```{tableofcontents} +``` diff --git a/shearbook/bias/bias-ref.md b/shearbook/bias/bias-ref.md new file mode 100644 index 0000000..37bafbb --- /dev/null +++ b/shearbook/bias/bias-ref.md @@ -0,0 +1,5 @@ +# References + +```{bibliography} ../_bibliography/z_bias.bib +:all: +``` diff --git a/shearbook/bias/model-bias.ipynb b/shearbook/bias/model-bias.ipynb new file mode 100644 index 0000000..fd65bc0 --- /dev/null +++ b/shearbook/bias/model-bias.ipynb @@ -0,0 +1,95 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "(sec:model-bias)=\n", + "# Model Bias\n", + "\n", + "In this section we will look into the concept of model bias." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Definition\n", + "\n", + "Model bias is a source of bias in shape estimation for [model fitting methods](sec:model-fitting). Model bias arises when the true shape of the galaxy in question is not sufficiently well represneted by the fitted model. In other words, real galaxy morphologies are often more complicated than the models used to fit them, which leads to a bias in the shapes recovered. \n", + "\n", + "```{note}\n", + "Model bias is also refferred to as *underfitting bias*.\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Measuring Model Bias\n", + "\n", + "To isolate the effects of model bias one needs to know the true underlying model of a galaxy image and then to attempt to fit various differnt models to this image. \n", + "\n", + "````{admonition} Example\n", + ":class: tip\n", + "{cite}`2014MNRAS.441.2528K` modelled a \"true\" galaxy as a single Sérsic profile with a given index $n$ then fit profiles with various different Sérsic indices to demonstrate the impact on the ellipticity values recovered.\n", + "\n", + "```{figure} ../_static/kacprazak_bias.jpg\n", + "---\n", + "height: 400px\n", + "name: model-bias\n", + "---\n", + "Galaxy ellipticity estimated when a wrong Sérsic index $S_j$ is used. True\n", + "ellipticity was $e_{true}^1 = 0.2$. True Sérsic index was 1. Additionally, a star marks the true Sérsic index, to guide the eye. Magenta dash-dotted line with cross dot markers shows the model bias; ellipticity estimated by a fit with Sérsic $S_j$ in the absence of noise. Blue and red lines with $+$ and $×$ markers, respectively, show the mean ellipticity measured from galaxy image with added noise, when: (1) galaxy image has the true Sérsic index, (2) galaxy image is the best fit with Sérsic index $S_j$ .\n", + "```\n", + "\n", + "````" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Impact of Model Bias\n", + "\n", + "{cite}`2014MNRAS.441.2528K` claim that, while model bias can be quite variable, it averages to a small value." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Correcting for Model Bias" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "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.9.0" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/shearbook/bias/noise-bias.ipynb b/shearbook/bias/noise-bias.ipynb new file mode 100644 index 0000000..d85ef70 --- /dev/null +++ b/shearbook/bias/noise-bias.ipynb @@ -0,0 +1,83 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "(sec:noise-bias)=\n", + "# Noise Bias\n", + "\n", + "In this section we will look into the concept of noise bias." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Definition\n", + "\n", + "Noise bias is a source of bias in shape estimation. Noise bias arises due to the non-linear relationship between images pixels and the true shape of a galaxy. All definitions of ellipticity are non-linear as they include a ratio of the semi-major and semi-minor axes of the object in question. This non-linearity induces a bias when the noise is propagated (see *e.g.* {cite}`2012MNRAS.424.2757M` for details)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Sources of Noise\n", + "\n", + "Noise in astronomical images stems from various sources including:\n", + "\n", + "- **Shot Noise:** [Shot or Poisson noise](https://en.wikipedia.org/wiki/Shot_noise) arises from that fact that light from distant sources arrives in the form of discrete or quantized packets at random times.\n", + "- **Readout Noise:** Readout or read noise is a source of Gaussian noise arising from the CCD electronics. As pixel values are read some electrons are randomly lost or gained. This has a greater impact on fainter images with shorter exposure times." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Measuring Noise Bias\n", + "\n", + "Noise bias can be estimated by adding noise to images where the shape of the galaxy in question is perfectly known then comparing the measured shape. This process should be repeated for various realisations of the noise model in order to gauge the amplitude of the noise bias. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Correcting for Noise Bias" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Model and Noise Bias Interaction\n", + "\n", + "Images that contain a significant amount of noise, for which an imperfect model is used to fit the galaxy shape, can be impacted by an additional bias effect caused by the interaction between noise bias and [model bias](sec:model-bias) .\n", + "\n", + "{cite}`2014MNRAS.441.2528K` attempted to isolate this interaction component to quantify its impact. While tests with a toy model indicated that the level this effect could be up to a few percent, tests on more realistic COSMOS images showed that this term is very small and often consistent with zero." + ] + } + ], + "metadata": { + "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.9.0" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/shearbook/calibration/calibration-intro.md b/shearbook/calibration/calibration-intro.md new file mode 100644 index 0000000..0833b33 --- /dev/null +++ b/shearbook/calibration/calibration-intro.md @@ -0,0 +1,9 @@ +# Shear Calibration + +In this section we will take a look at methods for calibrating sources of +bias. + +**Contents** + +```{tableofcontents} +``` diff --git a/shearbook/calibration/calibration-ref.md b/shearbook/calibration/calibration-ref.md new file mode 100644 index 0000000..729c143 --- /dev/null +++ b/shearbook/calibration/calibration-ref.md @@ -0,0 +1,5 @@ +# References + +```{bibliography} ../_bibliography/z_calibration.bib +:all: +``` diff --git a/shearbook/contrib/contributors.md b/shearbook/contrib/contributors.md new file mode 100644 index 0000000..ba129c8 --- /dev/null +++ b/shearbook/contrib/contributors.md @@ -0,0 +1,26 @@ +# Contributors + +This book has benefitted from significant contributions from the following people. + +- @sfarrens + +```{toggle} +**Name:** Samuel Farrens +**Institute:** [CosmoStat](http://www.cosmostat.org/) +**Website:** [sfarrens.github.io](https://sfarrens.github.io/) +``` +- @EiffL + +```{toggle} +**Name:** François Lanusse +**Institute:** [CosmoStat](http://www.cosmostat.org/) +**Website:** [flanusse.net](https://flanusse.net/) +``` + +- @fadinammour + +```{toggle} +**Name:** Fadi Nammour +**Institute:** [CosmoStat](http://www.cosmostat.org/) +**Website:** [cosmostat.org/people/fadi-nammour](http://www.cosmostat.org/people/fadi-nammour) +``` diff --git a/shearbook/contrib/guidelines.md b/shearbook/contrib/guidelines.md new file mode 100644 index 0000000..3b0d924 --- /dev/null +++ b/shearbook/contrib/guidelines.md @@ -0,0 +1,119 @@ +# Contribution Guidelines + +This book is an open source project and we welcome contributions from anyone. We do, however, request that you adhere to the following guidelines so that we can ensure that we maintain the best possible quality and standards. + +## Issues + +The easiest way to contribute to this project is by raising a "New issue". This will give you the opportunity to ask questions, report errors or even request new content. + +Remember to use clear and descriptive titles for issues. We also ask that you read the available documentation and browse existing issues on similar topics before raising a new issue in order to avoid repetition. + +## Pull Requests + +If you would like to take a more active roll in the development of this project you can do so by submitting a "Pull request". A Pull Requests (PR) is a way by which you can directly submit modifications or additions the content. PRs need to be reviewed by the package moderators and if accepted are merged into the master branch of the repository. + +Before making a PR, be sure to carefully read the following guidelines. + +### Before Making a PR + +The following steps should be followed before making a pull request: + +1. Log into your GitHub account or create an account if you do not already have one. + +1. Go to the main repository page: [https://github.com/CosmoStat/Shear-and-PSF-Reading-Group](https://github.com/CosmoStat/Shear-and-PSF-Reading-Group) + +1. Fork the repository, *i.e.* press the button on the top right with this symbol . This will create an independent copy of the repository on your account. + +1. Clone your fork of the repository. + +```bash + git clone https://github.com/YOUR_USERNAME/Shear-and-PSF-Reading-Group +``` + +5. Add the original repository (*upstream*) to remote. + +```bash + git remote add upstream https://github.com/CosmoStat/Shear-and-PSF-Reading-Group +``` + +### Making a PR + +The following steps should be followed to make a pull request: + +1. Pull the latest updates to the original repository. + +```bash + git pull upstream master +``` + +2. Create a new branch for your modifications. + +```bash + git checkout -b BRANCH_NAME +``` + +3. Make the desired modifications to the relevant pages. + +4. Add the modified files to the staging area. + +```bash + git add . +``` + +5. Make sure all of the appropriate files have been staged. Note that all files listed in green will be included in the following commit. + +```bash + git status +``` + +6. Commit the changes with an appropriate description. + +```bash + git commit -m "Description of commit" +``` + +7. Push the commits to a branch on your fork of ModOpt. + +```bash + git push origin BRANCH_NAME +``` + +8. Make a pull request for your branch with a clear description of what has been done, why and what issues this relates to. + +9. Wait for feedback and repeat steps 3 through 7 if necessary. + +### After Making a PR + +If your PR is accepted and merged it is recommended that the following steps be followed to keep your fork up to date. + +1. Make sure you switch back to your local master branch. + +```bash + git checkout master +``` + +2. Delete the local branch you used for the PR. + +```bash + git branch -d BRANCH_NAME +``` + +3. Pull the latest updates to the original repository, which include your PR changes. + +```bash + git pull upstream master +``` + +4. Push the commits to your fork. + +```bash + git push origin master +``` + +### Content + +Every PR should correspond to a issue that has already been raised. When you make a PR be sure to tag the issue that it resolves (*e.g.* this PR relates to issue #1). This way the issue can be closed once the PR has been merged. + +### CI Tests + +Continuous Integration (CI) tests are implemented via [GitHub Actions](https://docs.github.com/en/free-pro-team@latest/actions). All PRs must pass the CI tests before being merged. Your PR may not be reviewed by a moderator until all CI test are passed. Therefore, try to resolve any issues in your PR that may cause the tests to fail. diff --git a/shearbook/contrib/style.md b/shearbook/contrib/style.md new file mode 100644 index 0000000..24906bc --- /dev/null +++ b/shearbook/contrib/style.md @@ -0,0 +1,5 @@ +# Style Guidelines + +```{warning} +In progress! +``` diff --git a/shearbook/home.md b/shearbook/home.md new file mode 100644 index 0000000..f18f25f --- /dev/null +++ b/shearbook/home.md @@ -0,0 +1,3 @@ +# Cosmic Shear + +In this book we will cover diff --git a/shearbook/intro/about.md b/shearbook/intro/about.md new file mode 100644 index 0000000..602e597 --- /dev/null +++ b/shearbook/intro/about.md @@ -0,0 +1,3 @@ +# About This Book + +This is an open source online reference guide that aims to provide an in-depth understand of various concepts in weak gravitational lensing. We endeavour to explain concepts and equations in the simplest possible terms without skipping over any important details. We additionally provide practical implementations of various principles in Python in order to cement understanding. diff --git a/shearbook/intro/run_code.md b/shearbook/intro/run_code.md new file mode 100644 index 0000000..c75af5f --- /dev/null +++ b/shearbook/intro/run_code.md @@ -0,0 +1,29 @@ +# Running Code Examples + +Throughout this books we provide frequent code snippets with the objective of +making direct links between mathematical concepts and practical demonstrations. +While we endeavour to provide clear and efficient implementations, the code is +not intended to be highly optimised nor does it include all of the subtleties +required for production ready software. + +## Running the Code Live + +Clicking the rocket logo on the top right of a given page + +![rocket](../_static/rocket.png) + + will provide a drop-down menu with options to launch the page as a Jupyter notebook on [Binder](https://mybinder.org/) or [Google Colab](https://colab.research.google.com/). A third option labelled "Live Code" will use [TheBe](https://thebe.readthedocs.io/en/latest/) with a Binder backend to enable the code cells to be executed directly within the page. + + +## Interactive Notebooks + +Some pages provide the option to launch an "interactive version" of the notebook on Binder. Here "interactive" refers to the use of interactive [ipywidgets](https://ipywidgets.readthedocs.io/en/latest/). These alternative notebooks can be launched by clicking on the Binder badge on the given page. + + + + +## Running the Code Locally + +Alternatively, all of the relevant pages can be downloaded as Jupyter notebooks using the download button on the top right. + +![rocket](../_static/download.png) diff --git a/shearbook/methods/ksb-variants.ipynb b/shearbook/methods/ksb-variants.ipynb new file mode 100644 index 0000000..7800f7e --- /dev/null +++ b/shearbook/methods/ksb-variants.ipynb @@ -0,0 +1,48 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# KSB Variants" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```{warning}\n", + "In progress!\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/shearbook/methods/ksb.ipynb b/shearbook/methods/ksb.ipynb new file mode 100644 index 0000000..d3e8a08 --- /dev/null +++ b/shearbook/methods/ksb.ipynb @@ -0,0 +1,48 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# KSB" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```{warning}\n", + "In progress!\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/shearbook/methods/moments-methods-ref.md b/shearbook/methods/moments-methods-ref.md new file mode 100644 index 0000000..ec9d363 --- /dev/null +++ b/shearbook/methods/moments-methods-ref.md @@ -0,0 +1,5 @@ +# References + +```{bibliography} ../_bibliography/z_moments_methods.bib +:all: +``` diff --git a/shearbook/methods/moments-methods.md b/shearbook/methods/moments-methods.md new file mode 100644 index 0000000..5b0b830 --- /dev/null +++ b/shearbook/methods/moments-methods.md @@ -0,0 +1,8 @@ +# Moments-Based Methods + +In this section we will take a look at moment-based methods for measuring galaxy shapes. + +**Contents** + +```{tableofcontents} +``` diff --git a/shearbook/methods/viola2011.md b/shearbook/methods/viola2011.md new file mode 100644 index 0000000..7554207 --- /dev/null +++ b/shearbook/methods/viola2011.md @@ -0,0 +1,73 @@ +# Biases in, and corrections to, KSB shear measurements. Viola et al, 2010 + +```{warning} +In progress! +``` + +Notes and tools derived from reading groups sessions + +{cite}`2011MNRAS.410.2156V` + +## Goals of the Paper + +1. Show the theoretical and practical caveats of KSB. +2. Suggest improvements to KSB. + +## Main Results and Conclusion + +1. The assumptions in KSB introduce biaises especially for large amplitude +values. +2. Rewriting correctly the Taylor expansion of the ellipticity up to the third +order (see KSB3) gives results with biaises smaller by orders of magnitude. +3. The PSF corrections in KSB are not accurate however they partially cancel +other biaises. + +## Definitions and Equations + +*. Reduced shear, g: (eq. 7) +*. Complex ellipticity, $\chi$: (eq. 10) +*. Shear responsivity $\epsilon$: (eq. 14) +*. KSB (eq. 32) +*. KSB1 (eq. 34) +*. KSBtr (eq.36) +*. KSB3 (eq. 44) +*. Inverse Problem (eq. 50) +*. Circular PSF Formula (eq. 58) +*. Anisotropic PSf Formula (eq. 67) + +> The reduced shear, g, is preferred over the shear responsivity, +$\epsilon$, for its robustness to noise in the data. + +## Assumptions in KSB +1. The reduced shear, g, is constant and the ellipticity average is null, +$\left< \chi \right>=0$ (see eq. 12). +2. Many Taylor expansions like in the reduced shear, g (see eq. 29). +> Taylor expansions gives a polynomial approximation around a certain reference +value. The further from the reference we evaluate the expansion the bigger the +biais. In this paper, since the reference value is zero, the biais will be +greater for large amplitude values. + +3. Assume equality between (eq. 27) and (eq. 28). +4. The PSF has at most slight anisotropies, it is mostly circular. + +## Notations Clarification + +1. The angular coordinates are solid angles so they have two components. These +coordinates can be approximated by linear coordinates because the angles are +very small. +2. The star in eq. 12 stands for the conjugate. +3. Rewriting eq.48-50: + i. (eq. 48) $$\chi = f\left( \tilde{g}, \chi^{s}\right)$$ + ii. (eq. 49) $$\chi = h \left( \chi^{obs}\right)$$ + iii. (eq. 50) $$\chi^{obs} = h^{-1}\left[ f \left( \tilde{g}, h\left(\chi^{obs}\right)\right)\right]$$ + + where $\chi^{s}$ is the intrinsic ellipticity of the galaxy, $\chi^{obs}$ is + the ellipticity of the lensed galaxy and $\chi$ is the ellipticity of the + convoluted and lensed galaxy. +4. In (eq. 51): $$\chi^{sh}_{\alpha} = \chi_{\alpha} + \chi^{s}_{\alpha}$$ +5. In (eq. 52): the asterisk, *, in $P^{sm,*}$ is to indicate the PSF. +Explanation: * --> star --> dirac --> PSF --> illuminati + +## Implementation + +[Link](https://github.com/pmelchior/shapelens/blob/master/src/KSB.cc) to Peter Melchior implementation of KSB codes in C. diff --git a/shearbook/model/model-intro.md b/shearbook/model/model-intro.md new file mode 100644 index 0000000..82a2c5a --- /dev/null +++ b/shearbook/model/model-intro.md @@ -0,0 +1,9 @@ +(sec:model-fitting)= +# Model-Fitting Methods + +In this section we will take a look at model-fitting methods for measuring galaxy shapes. + +**Contents** + +```{tableofcontents} +``` diff --git a/shearbook/model/model-ref.md b/shearbook/model/model-ref.md new file mode 100644 index 0000000..da10e46 --- /dev/null +++ b/shearbook/model/model-ref.md @@ -0,0 +1,5 @@ +# References + +```{bibliography} ../_bibliography/z_model.bib +:all: +``` diff --git a/shearbook/moments/moments-append.md b/shearbook/moments/moments-append.md new file mode 100644 index 0000000..5a1cd94 --- /dev/null +++ b/shearbook/moments/moments-append.md @@ -0,0 +1,108 @@ +(content:appendix)= +# Appendix + +## `data.py` Content + +Here we provide the code used to generate the example data for this chapter in +case you want to see it or reuse it. + +```{admonition} Click the button to reveal code +:class: dropdown +```python +import numpy as np +from scipy.ndimage import rotate + + +def rotate_image(data, angle): + + data_centre = np.array(data.shape) // 2 + rot_data = np.abs(np.around(rotate(data, angle, reshape=False))) + + return rot_data + + +def make_ellipse(a, b, theta): + + x = np.linspace(-10, 10, 45) + y = np.linspace(-10, 10, 45)[:, None] + ellipse = ((x/a)**2 + (y/b)**2 <= 1).astype(int) + + return rotate_image(ellipse, theta) + + +hline = np.zeros((5, 5)) +hline[2, 1:4] += 1 +vline = rotate_image(hline, 90) +diag_up = rotate_image(hline, 45) +diag_down = rotate_image(hline, -45) +circle = hline + vline +circle_off = np.roll(np.roll(circle, 1), 1, axis=0) + +data_dict = {'Horizontal Line': hline, + 'Vertical Line': vline, + 'Diagonal Line (Up)': diag_up, + 'Diagonal Line (Down)': diag_down, + 'Circle': circle, + 'Circle (Off-Centre)': circle_off} +``` + +## `plot.py` Content + +Here we provide the code used to produce the plots for this chapter in case +you want to see it or reuse it. + +```{admonition} Click the button to reveal code +:class: dropdown +```python +from ipywidgets.widgets import * +import matplotlib.pyplot as plt +from matplotlib.patches import Rectangle, Ellipse + + +def show_image(data, show_flux=False, centroid=None, + chi=None, epsilon=None, abt=None): + + limx, limy = data.shape + + def display_image(): + fig = plt.figure() + ax = fig.add_subplot(111) + ax.grid() + ax.imshow(data, cmap='binary', extent=[0, limx, limy, 0]) + return ax + + if show_flux: + @interact(data=fixed(data), x=(0, limx-1, 1), y=(0, limy-1, 1)) + def make_plot(data, x, y): + ax = display_image() + box = Rectangle((x, y), 1, 1, color='red', alpha=0.5) + ax.add_patch(box) + ax.text(limy + limy / 5, limx - 4 * limx / 5, + r'$I({}, {})={}$'.format(x, y, data[y, x]), fontsize=18) + + if not isinstance(centroid, type(None)): + ax = display_image() + ax.plot(*centroid + 0.5, 'rx', markersize=12, markeredgewidth=2) + ax.text(limy + limy / 5, limx - 4 * limx / 5, + r'$\bar x = {}, \bar y = {}$'.format(*centroid), fontsize=18) + + if not isinstance(chi, type(None)): + ax.text(limy + limy / 5, limx - 3 * limx / 5, + r'$\chi_1 = {}, \chi_2 = {}$'.format(*chi), fontsize=18) + + if not isinstance(epsilon, type(None)): + ax.text(limy + limy / 5, limx - 2 * limx / 5, + r'$\epsilon_1 = {}, \epsilon_2 = {}$'.format(*epsilon), + fontsize=18) + + if not isinstance(abt, type(None)): + a, b, theta = abt + ellipse = Ellipse(centroid + 0.5, a * 5, b * 5, -theta, fill=False, + edgecolor='c', linewidth=2) + ax.add_patch(ellipse) + ax.text(limy + limy / 5, limx - limx / 5, + r'$a = {}, b = {}, \theta = {}$'.format(*abt), + fontsize=18) + + plt.show() +``` diff --git a/shearbook/moments/moments-basic.ipynb b/shearbook/moments/moments-basic.ipynb new file mode 100644 index 0000000..28ddc82 --- /dev/null +++ b/shearbook/moments/moments-basic.ipynb @@ -0,0 +1,509 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```{toggle}\n", + "```{code-block}\n", + ":class: thebe, thebe-init\n", + "# Automatic import for live code\n", + "import numpy as np\n", + "from shrbk.plot import *\n", + "from shrbk.data import * \n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Moments Basics\n", + "\n", + "In this section we will take a look at the basics of using moments to measure shapes.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```{note}\n", + "Try out the interactive version of this notebook on Binder!\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "thebelab-init", + "hide-input" + ] + }, + "outputs": [], + "source": [ + "# Import dependencies\n", + "import numpy as np\n", + "from shrbk.plot import *\n", + "from shrbk.data import * \n", + "from IPython.display import Markdown as md\n", + "from shrbk.interact import get_url, make_html_binder_button\n", + "\n", + "# Provide binder badge\n", + "url = get_url('moments.ipynb')\n", + "md(make_html_binder_button(url))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example Data\n", + "\n", + "Let's begin by looking at some dummy images with properties we can easily discern by eye. We will use the `data_dict` imported from `data.py` (see [appendix](content:appendix)) and print the available keys." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for key in data_dict.keys():\n", + " print(key)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can examime the content of the matrices that define the images." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "data_name = 'Diagonal Line (Down)'\n", + "print(data_dict[data_name])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```{admonition} Hint\n", + ":class: tip\n", + "Try changing `data_name` to one of the other possible dictionary keys to compare the matrices.\n", + "```\n", + "\n", + "What you should notice is that we have a collection of (very unrealistic) images with a range of orientions and ellipticities. We can use these images to verify in the following sections that we fully understand how to measure the shape of an object using moments. For the purposes of this section we will treat these example images as *postage stamps* of isolated galaxy images." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Surface Brightness\n", + "\n", + "We can define the surface brightness, $I$, of an object as the amount of light (*i.e.* flux) per square arcsecond on the sky. $I(x, y)$ gives the surface brightness at position $x, y$, which we can relate to a given pixel. To measure the flux density, $S_\\nu$, of an object we need to intragte acoss the whole image\n", + "\n", + "$$S_\\nu = \\iint I(x,y) dx dy$$ (total_flux)\n", + "\n", + "or, for a discrete set of pixels, sum the values of $I(x, y)$.\n", + "\n", + "$$S_\\nu = \\sum_{x,y} I(x,y)$$ (total_flux_discrete)\n", + "\n", + "```{note}\n", + "Surface brightness is also referred to as specific intensity, spectral intensity, spectral brightness, spectral radiance, or just brightness (see [here](https://www.cv.nrao.edu/course/astr534/Brightness.html) for more details). \n", + "```\n", + "\n", + "```{warning}\n", + "Here we assume that the object is isolated so that the surface brightness can be measured across the whole image (see *e.g.* {cite}`BartelmannSchneider:01`). We will revist this point in the next section.\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print('F = {}'.format(np.sum(data_dict['Horizontal Line'])))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Centroid\n", + "\n", + "Now that we have established what the surface brightness is we can use it identfy the first moment or centroid $(\\bar{x}, \\bar{y})$ of a given image.\n", + "\n", + "$$\n", + " \\bar{x} = \\frac{\\sum_{x,y} xI(x,y)}{\\sum_{x,y} I(x,y)}\n", + "$$ (centroid_x)\n", + "\n", + "$$\n", + " \\bar{y} = \\frac{\\sum_{x,y} yI(x,y)}{\\sum_{x,y} I(x,y)}\n", + "$$ (centroid_y)\n", + "\n", + "\n", + "The following cell provides a simple implementation of equations {eq}`centroid_x` and {eq}`centroid_y`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def get_centroid(data):\n", + " \n", + " # Sum flux over x and y individually\n", + " sum_i = np.array([np.sum(data, axis=i) for i in (1, 0)])\n", + " \n", + " # Get range of x and y values\n", + " ranges = np.array([np.arange(i) for i in data.shape])\n", + " \n", + " # Calculate centroids\n", + " cents = np.sum(sum_i * ranges, axis=1) / np.sum(data)\n", + " \n", + " return cents.astype(int)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let's test that our function works on the example data." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def show_centroid(data_name):\n", + " \n", + " data = data_dict[data_name]\n", + " centroid = get_centroid(data)\n", + " \n", + " show_image(data, centroid=centroid)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "show_centroid('Circle (Off-Centre)')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can see that, regardless of the position of the image within the postage stamp, the centroid (highlighted with a red x) is always correctly determined." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Moments\n", + "\n", + "Finally, we can define the second moment or quadrupole moments of an image as follows.\n", + "\n", + "$$\n", + " Q_{xy} = \\frac{\\sum_{x,y} I(x,y)(x - \\bar{x})(y - \\bar{y})}{\\sum_{x,y} I(x,y)}\n", + "$$ (moments)\n", + "\n", + "Equation {eq}`moments` is used to determine object shapes. In the following cell we a simple implementation of this equation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def get_moments(data):\n", + " \n", + " centroid = get_centroid(data)\n", + " ranges = np.array([np.arange(i) for i in data.shape])\n", + " \n", + " x = np.outer(ranges[0] - centroid[0], np.ones(data.shape[1]))\n", + " y = np.outer(np.ones(data.shape[0]), ranges[1] - centroid[1])\n", + " \n", + " q = np.array([np.sum(data * xi * xj) for xi in (x, y) for xj in (x, y)])\n", + " q = (q / np.sum(data)).reshape(2, 2).astype('complex')\n", + " \n", + " return q" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can examine the output of this function for the various example images." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(get_moments(data_dict['Vertical Line']))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "While it is clear that the moments are different for each image, it can be difficult to interpret these numbers." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Measures of Ellipticity\n", + "\n", + "It is convenient to convert these moments into a measure of the object's *ellipticity*, however this can be done in various ways.\n", + "\n", + "### Polarisation\n", + "\n", + "This is the standard convetion used by methods such as KSB and is often denoted by the greek letter $\\chi$.\n", + "\n", + "$$\n", + " \\chi = \\frac{Q_{00}-Q_{11}+2iQ_{01}}{Q_{00}+Q_{11}}\n", + "$$ (chi)\n", + "\n", + "### Ellipticity\n", + "\n", + "This is an alternative convention that... and is often denoted by the greek letter $\\epsilon$.\n", + "\n", + "$$\n", + " \\epsilon = \\frac{Q_{00}-Q_{11}+2iQ_{01}}{Q_{00}+Q_{11}+2\\sqrt{Q_{00}Q_{11}-Q_{01}^2}}\n", + "$$ (epsilon)\n", + "\n", + "```{note}\n", + "We have adopted the convention of labeling $Q_{x, y}$ starting from $x, y$ = 0 to keep the equations consistent with the code.\n", + "```\n", + "\n", + "The following cell provides a simple implementation of both {eq}`chi` and {eq}`epsilon`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def get_ellipticity(data, method='chi'):\n", + " \n", + " # Calculate moments\n", + " q = get_moments(data)\n", + " \n", + " # Calculate the image size.\n", + " r2 = q[0, 0] + q[1, 1]\n", + "\n", + " # Calculate the numerator\n", + " num = (q[0, 0] - q[1, 1] + 2 * np.complex(0, q[0, 1]))\n", + " \n", + " # Calculate the denominator\n", + " den = r2\n", + " \n", + " if method == 'epsilon':\n", + " den += 2 * np.sqrt(q[0, 0] * q[1, 1] - q[0, 1] ** 2)\n", + " \n", + " # Calculate the ellipticity/polarisation\n", + " ellip = num / den\n", + "\n", + " return np.around([ellip.real, ellip.imag], 3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The ellipticity makes it a bit easier to interpret the properties of the example images." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def show_ellipticity(data_name):\n", + " \n", + " data = data_dict[data_name]\n", + " centroid = get_centroid(data)\n", + " chi = get_ellipticity(data)\n", + " epsilon = get_ellipticity(data, method='epsilon')\n", + " \n", + " show_image(data, centroid=centroid, chi=chi, epsilon=epsilon)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "show_ellipticity('Diagonal Line (Up)')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Semi-Major/Minor Axes\n", + "\n", + "It is also possible to relate the quadrupole moments to more traditional measures of ellipticity, namely the semi-major axis ($a$), semi-minor axis ($b$) and angle ($\\theta$)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "$$\n", + " a^2 = \\frac{Q_{00}+Q_{11}+\\sqrt{(Q_{00}-Q_{11})^2 + 4Q_{01}^2}}{2}\n", + "$$ (semi-major)\n", + "\n", + "$$\n", + " b^2 = \\frac{Q_{00}+Q_{11}-\\sqrt{(Q_{00}-Q_{11})^2 + 4Q_{01}^2}}{2}\n", + "$$ (semi-minor)\n", + "\n", + "$$\n", + "\\begin{equation}\n", + "\t\\theta = \n", + "\t\\begin{cases} \n", + " \\frac{\\pi}{4} & \\text{if}~ Q_{00}-Q_{11} = 0 \\\\\n", + " \\frac{1}{2}\\arctan(\\frac{2Q_{01}}{Q_{00}-Q_{11}}) & \\text{if}~ Q_{00}-Q_{11} \\neq 0 \\\\ \n", + "\t\t\\frac{1}{2}\\arctan(\\frac{2Q_{01}}{Q_{00}-Q_{11}}) + \\frac{\\pi}{2} & \\text{if}~ Q_{00}-Q_{11} > 0 ~ \\text{and} ~ Q_{01}, Q_{10} = 0 \\\\ \n", + " \\frac{1}{2}\\arctan(\\frac{2Q_{01}}{Q_{00}-Q_{11}}) - \\frac{\\pi}{2}\\text{sign}(Q_{01}) & \\text{if}~ Q_{00}-Q_{11} > 0 ~ \\text{and} ~ Q_{01}, Q_{10} \\neq 0 \\\\ \n", + "\t\\end{cases}\n", + "\t\\label{eq:soft}\n", + "\\end{equation}\n", + "$$ (rotation-angle)\n", + "\n", + "The following cell implements equations {eq}`semi-major`, {eq}`semi-minor` and {eq}`rotation-angle`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def get_abt(data):\n", + " \n", + " q = get_moments(data)\n", + " \n", + " qq_plus = q[0, 0] + q[1, 1]\n", + " qq_minus = q[0, 0] - q[1, 1]\n", + " root = np.sqrt(qq_minus ** 2 + 4 * q[0, 1] ** 2)\n", + " \n", + " a = np.around(np.real(np.sqrt(0.5 * (qq_plus + root))), 3)\n", + " b = np.around(np.real(np.sqrt(0.5 * (qq_plus - root))), 3)\n", + " if qq_minus == 0.0:\n", + " theta = -45.0 * np.sign(np.real(q[0, 1]))\n", + " else:\n", + " theta = np.around(np.real(0.5 * np.arctan(2 * q[0, 1] / qq_minus)) * 180. / np.pi, 3)\n", + " \n", + " if qq_minus > 0.0 and q[0, 1] == 0.0:\n", + " theta += 90.0\n", + " elif qq_minus > 0.0:\n", + " theta -= 90.0 * np.sign(np.real(q[0, 1]))\n", + " \n", + " return a, b, theta" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can test the accuracy of this implementation by generating some images where we define the properties of the ellipse and seeing if we can recover the correct model from the moments." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def show_ellipse(a, b, theta):\n", + " \n", + " data = make_ellipse(a, b, theta)\n", + " centroid = get_centroid(data)\n", + " chi = get_ellipticity(data)\n", + " epsilon = get_ellipticity(data, method='epsilon')\n", + " abt = get_abt(data)\n", + " \n", + " show_image(data, centroid=centroid, chi=chi, epsilon=epsilon, abt=abt)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "show_ellipse(a=5, b=1, theta=15)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```{note}\n", + "Refer to the [appendix](content:appendix) to see how the input images are produced.\n", + "```" + ] + } + ], + "metadata": { + "celltoolbar": "Edit Metadata", + "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.8.5" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/shearbook/moments/moments-intro.md b/shearbook/moments/moments-intro.md new file mode 100644 index 0000000..4c896d4 --- /dev/null +++ b/shearbook/moments/moments-intro.md @@ -0,0 +1,8 @@ +# Introduction to Moments + +In this section we will take a look what moments are and how they can be used to measure the shapes of objects. + +**Contents** + +```{tableofcontents} +``` diff --git a/shearbook/moments/moments-ref.md b/shearbook/moments/moments-ref.md new file mode 100644 index 0000000..6cf06eb --- /dev/null +++ b/shearbook/moments/moments-ref.md @@ -0,0 +1,5 @@ +# References + +```{bibliography} ../_bibliography/z_moments.bib +:all: +``` diff --git a/shearbook/moments/moments-weighted.ipynb b/shearbook/moments/moments-weighted.ipynb new file mode 100644 index 0000000..d6a338e --- /dev/null +++ b/shearbook/moments/moments-weighted.ipynb @@ -0,0 +1,49 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "(content:weighted)=\n", + "# Weighted Moments" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```{warning}\n", + "In progress!\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/shrbk/__init__.py b/shrbk/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/shrbk/data.py b/shrbk/data.py new file mode 100644 index 0000000..63dd31f --- /dev/null +++ b/shrbk/data.py @@ -0,0 +1,35 @@ +import numpy as np +from scipy.ndimage import rotate + + +def rotate_image(data, angle): + + data_centre = np.array(data.shape) // 2 + rot_data = np.abs(np.around(rotate(data, angle, reshape=False))) + + return rot_data + + +def make_ellipse(a, b, theta): + + x = np.linspace(-10, 10, 45) + y = np.linspace(-10, 10, 45)[:, None] + ellipse = ((x/a)**2 + (y/b)**2 <= 1).astype(int) + + return rotate_image(ellipse, theta) + + +hline = np.zeros((5, 5)) +hline[2, 1:4] += 1 +vline = rotate_image(hline, 90) +diag_up = rotate_image(hline, 45) +diag_down = rotate_image(hline, -45) +circle = hline + vline +circle_off = np.roll(np.roll(circle, 1), 1, axis=0) + +data_dict = {'Horizontal Line': hline, + 'Vertical Line': vline, + 'Diagonal Line (Up)': diag_up, + 'Diagonal Line (Down)': diag_down, + 'Circle': circle, + 'Circle (Off-Centre)': circle_off} diff --git a/shrbk/interact.py b/shrbk/interact.py new file mode 100644 index 0000000..48a1751 --- /dev/null +++ b/shrbk/interact.py @@ -0,0 +1,59 @@ +from os import getcwd +from yaml import safe_load + +REPO_NAME = "Shear-and-PSF-Reading-Group" +BINDER_BADGE = ( + 'https://img.shields.io/badge/launch-binder-579aca.svg?logo=data:image/' + 'png;base64,iVBORw0KGgoAAAANSUhEUgAAAFkAAABZCAMAAABi1XidAAAB8lBMVEX//' + '/9XmsrmZYH1olJXmsr1olJXmsrmZYH1olJXmsr1olJXmsrmZYH1olL1olJXmsr1olJXmsr' + 'mZYH1olL1olJXmsrmZYH1olJXmsr1olL1olJXmsrmZYH1olL1olJXmsrmZYH1olL1olL0n' + 'Ff1olJXmsrmZYH1olJXmsq8dZb1olJXmsrmZYH1olJXmspXmspXmsr1olL1olJXmsrmZYH1' + 'olJXmsr1olL1olJXmsrmZYH1olL1olLeaIVXmsrmZYH1olL1olL1olJXmsrmZYH1olLna31' + 'Xmsr1olJXmsr1olJXmsrmZYH1olLqoVr1olJXmsr1olJXmsrmZYH1olL1olKkfaPobXvviG' + 'abgadXmsqThKuofKHmZ4Dobnr1olJXmsr1olJXmspXmsr1olJXmsrfZ4TuhWn1olL1olJXm' + 'sqBi7X1olJXmspZmslbmMhbmsdemsVfl8ZgmsNim8Jpk8F0m7R4m7F5nLB6jbh7jbiDirOE' + 'ibOGnKaMhq+PnaCVg6qWg6qegKaff6WhnpKofKGtnomxeZy3noG6dZi+n3vCcpPDcpPGn3b' + 'Lb4/Mb47UbIrVa4rYoGjdaIbeaIXhoWHmZYHobXvpcHjqdHXreHLroVrsfG/uhGnuh2bwj2' + 'Hxk17yl1vzmljzm1j0nlX1olL3AJXWAAAAbXRSTlMAEBAQHx8gICAuLjAwMDw9PUBAQEpQU' + 'FBXV1hgYGBkcHBwcXl8gICAgoiIkJCQlJicnJ2goKCmqK+wsLC4usDAwMjP0NDQ1NbW3Nzg' + '4ODi5+3v8PDw8/T09PX29vb39/f5+fr7+/z8/Pz9/v7+zczCxgAABC5JREFUeAHN1ul3k0U' + 'UBvCb1CTVpmpaitAGSLSpSuKCLWpbTKNJFGlcSMAFF63iUmRccNG6gLbuxkXU66JAUef/9L' + 'SpmXnyLr3T5AO/rzl5zj137p136BISy44fKJXuGN/d19PUfYeO67Znqtf2KH33Id1psXoFd' + 'W30sPZ1sMvs2D060AHqws4FHeJojLZqnw53cmfvg+XR8mC0OEjuxrXEkX5ydeVJLVIlV0e1' + '0PXk5k7dYeHu7Cj1j+49uKg7uLU61tGLw1lq27ugQYlclHC4bgv7VQ+TAyj5Zc/UjsPvs1s' + 'd5cWryWObtvWT2EPa4rtnWW3JkpjggEpbOsPr7F7EyNewtpBIslA7p43HCsnwooXTEc3UmP' + 'mCNn5lrqTJxy6nRmcavGZVt/3Da2pD5NHvsOHJCrdc1G2r3DITpU7yic7w/7Rxnjc0kt5GC' + '4djiv2Sz3Fb2iEZg41/ddsFDoyuYrIkmFehz0HR2thPgQqMyQYb2OtB0WxsZ3BeG3+wpRb1' + 'vzl2UYBog8FfGhttFKjtAclnZYrRo9ryG9uG/FZQU4AEg8ZE9LjGMzTmqKXPLnlWVnIlQQT' + 'vxJf8ip7VgjZjyVPrjw1te5otM7RmP7xm+sK2Gv9I8Gi++BRbEkR9EBw8zRUcKxwp73xkaL' + 'iqQb+kGduJTNHG72zcW9LoJgqQxpP3/Tj//c3yB0tqzaml05/+orHLksVO+95kX7/7qgJvn' + 'jlrfr2Ggsyx0eoy9uPzN5SPd86aXggOsEKW2Prz7du3VID3/tzs/sSRs2w7ovVHKtjrX2pd' + '7ZMlTxAYfBAL9jiDwfLkq55Tm7ifhMlTGPyCAs7RFRhn47JnlcB9RM5T97ASuZXIcVNuUDI' + 'ndpDbdsfrqsOppeXl5Y+XVKdjFCTh+zGaVuj0d9zy05PPK3QzBamxdwtTCrzyg/2Rvf2Est' + 'UjordGwa/kx9mSJLr8mLLtCW8HHGJc2R5hS219IiF6PnTusOqcMl57gm0Z8kanKMAQg0qSy' + 'uZfn7zItsbGyO9QlnxY0eCuD1XL2ys/MsrQhltE7Ug0uFOzufJFE2PxBo/YAx8XPPdDwWN0' + 'MrDRYIZF0mSMKCNHgaIVFoBbNoLJ7tEQDKxGF0kcLQimojCZopv0OkNOyWCCg9XMVAi7ARJ' + 'zQdM2QUh0gmBozjc3Skg6dSBRqDGYSUOu66Zg+I2fNZs/M3/f/Grl/XnyF1Gw3VKCez0PN5' + 'IUfFLqvgUN4C0qNqYs5YhPL+aVZYDE4IpUk57oSFnJm4FyCqqOE0jhY2SMyLFoo56zyo6be' + 'cOS5UVDdj7Vih0zp+tcMhwRpBeLyqtIjlJKAIZSbI8SGSF3k0pA3mR5tHuwPFoa7N7reoq2' + 'bqCsAk1HqCu5uvI1n6JuRXI+S1Mco54YmYTwcn6Aeic+kssXi8XpXC4V3t7/ADuTNKaQJdS' + 'cAAAAAElFTkSuQmCC' + ) + + +def get_url(file_name, user='CosmoStat', branch='develop'): + """Get the Binder url for an interactive notebook in this repo""" + + binder = f'https://mybinder.org/v2/gh/{user}/{REPO_NAME}' + + if not file_name.endswith('.ipynb'): + file_name = f'{file_name}.ipynb' + + url = (f'{binder}/{branch}/?urlpath=tree/notebooks/{file_name}') + + return url + + +def make_html_binder_button(url): + """Return a HTML Binder button that links to the provided url""" + return f'' \ No newline at end of file diff --git a/shrbk/plot.py b/shrbk/plot.py new file mode 100644 index 0000000..e048ed4 --- /dev/null +++ b/shrbk/plot.py @@ -0,0 +1,51 @@ +from ipywidgets.widgets import * +import matplotlib.pyplot as plt +from matplotlib.patches import Rectangle, Ellipse + + +def show_image(data, show_flux=False, centroid=None, + chi=None, epsilon=None, abt=None): + + limx, limy = data.shape + + def display_image(): + fig = plt.figure() + ax = fig.add_subplot(111) + ax.grid() + ax.imshow(data, cmap='binary', extent=[0, limx, limy, 0]) + return ax + + if show_flux: + @interact(data=fixed(data), x=(0, limx-1, 1), y=(0, limy-1, 1)) + def make_plot(data, x, y): + ax = display_image() + box = Rectangle((x, y), 1, 1, color='red', alpha=0.5) + ax.add_patch(box) + ax.text(limy + limy / 5, limx - 4 * limx / 5, + r'$I({}, {})={}$'.format(x, y, data[y, x]), fontsize=18) + + if not isinstance(centroid, type(None)): + ax = display_image() + ax.plot(*centroid + 0.5, 'rx', markersize=12, markeredgewidth=2) + ax.text(limy + limy / 5, limx - 4 * limx / 5, + r'$\bar x = {}, \bar y = {}$'.format(*centroid), fontsize=18) + + if not isinstance(chi, type(None)): + ax.text(limy + limy / 5, limx - 3 * limx / 5, + r'$\chi_1 = {}, \chi_2 = {}$'.format(*chi), fontsize=18) + + if not isinstance(epsilon, type(None)): + ax.text(limy + limy / 5, limx - 2 * limx / 5, + r'$\epsilon_1 = {}, \epsilon_2 = {}$'.format(*epsilon), + fontsize=18) + + if not isinstance(abt, type(None)): + a, b, theta = abt + ellipse = Ellipse(centroid + 0.5, a * 5, b * 5, -theta, fill=False, + edgecolor='c', linewidth=2) + ax.add_patch(ellipse) + ax.text(limy + limy / 5, limx - limx / 5, + r'$a = {}, b = {}, \theta = {}$'.format(*abt), + fontsize=18) + + plt.show() From 64baaa62540dd2f60dfb893b566a30f3af80d964 Mon Sep 17 00:00:00 2001 From: mkilbing Date: Sun, 29 Nov 2020 19:10:43 +0100 Subject: [PATCH 04/18] Added notebook on measuring shear bias --- shearbook/_bibliography/z_bias.bib | 18 ++++++++++- shearbook/bias/measuring-bias.ipybn | 46 +++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 shearbook/bias/measuring-bias.ipybn diff --git a/shearbook/_bibliography/z_bias.bib b/shearbook/_bibliography/z_bias.bib index 0f687bf..330bb6e 100644 --- a/shearbook/_bibliography/z_bias.bib +++ b/shearbook/_bibliography/z_bias.bib @@ -34,4 +34,20 @@ @ARTICLE{2012MNRAS.424.2757M primaryClass = {astro-ph.IM}, adsurl = {https://ui.adsabs.harvard.edu/abs/2012MNRAS.424.2757M}, adsnote = {Provided by the SAO/NASA Astrophysics Data System} -} \ No newline at end of file +} + +@ARTICLE{PKSB17, + author = {{Pujol}, A. and {Kilbinger}, M. and {Sureau}, F. and {Bobin}, J. + }, + title = "{A highly precise shape-noise-free shear bias estimator}", + journal = {\aap}, +archivePrefix = "arXiv", + volume = {621}, + pages = {A21}, + keywords = {Astrophysics - Cosmology and Nongalactic Astrophysics}, + year = 2019, + month = jan, + adsurl = {http://cdsads.u-strasbg.fr/abs/2018arXiv180610537P}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + diff --git a/shearbook/bias/measuring-bias.ipybn b/shearbook/bias/measuring-bias.ipybn new file mode 100644 index 0000000..0b3855e --- /dev/null +++ b/shearbook/bias/measuring-bias.ipybn @@ -0,0 +1,46 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " (sec:measuring-bias)=\n", + "# Measuring Bias\n", + "\n", + "In this section we will discuss several methods to measure shear bias.\n", + "\n", + "\n", + "## Shear bias from individual galaxies\n", + "\n", + "{cite}`PKSB17` present a shape-noise-free shear bias estimator from individual simulated galaxies. The principle is similar to the metacalibration technique: A small shear is applied to a galaxy image, and the response matrix of the measured ellipticity to the shear is determined by finite differences.\n", + "\n", + "In general, the shear estimate for a single galaxy is dominated by shape noise. To beat down this noise, traditional methods use a very large number of galaxies. Some additional improvement can be gained by using rotated versions of the same galaxy, such that the sum of the intrinsic ellipticities is zero. The resulting shear estimate has a reduced variance.\n", + "\n", + "However, this does not account for the variance from the measured ellipticity, which does not only depend on the intrinsic one, but on the PSF, pixel noise, etc. In addition, all versions of the rotated galaxy need to be detected and have an measured shape, and therefore, selection biases are difficult to quantify with this method.\n", + "\n", + "To reduce shape noise (to virtually zero), {cite}`PKSB17` uses the same noise realisation is used for each sheared, and the unsheared image. The resulting estimate of the response per galaxy is highly precise. It is still not very accurate, since the measured value depends on the noise realisation. To increase the accuracy, a large number of simulated galaxies with different noise realisations is required. These galaxies are however anyway simulated to cover the large parameter space of galaxy properties." + ] + } + ], + "metadata": { + "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.9.0" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} From fb963255af415429339ff89069244c6583ab7bc2 Mon Sep 17 00:00:00 2001 From: mkilbing Date: Sun, 29 Nov 2020 21:52:33 +0100 Subject: [PATCH 05/18] added bias-definition notebook --- README.md | 2 +- shearbook/_bibliography/z_bias.bib | 30 +++++++++ shearbook/_toc.yml | 2 + shearbook/bias/bias-definition.ipynb | 67 +++++++++++++++++++ ...suring-bias.ipybn => measuring-bias.ipynb} | 0 shearbook/moments/moments-basic.ipynb | 4 +- 6 files changed, 102 insertions(+), 3 deletions(-) create mode 100644 shearbook/bias/bias-definition.ipynb rename shearbook/bias/{measuring-bias.ipybn => measuring-bias.ipynb} (100%) diff --git a/README.md b/README.md index 677d7f5..f432622 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ $ conda activate shear ## Install the `shrbk` library `shrbk` is the library made for this book. -Install it ither in development mode +Install it either in development mode ```bash $ python -m pip install -e . diff --git a/shearbook/_bibliography/z_bias.bib b/shearbook/_bibliography/z_bias.bib index 330bb6e..5935aa1 100644 --- a/shearbook/_bibliography/z_bias.bib +++ b/shearbook/_bibliography/z_bias.bib @@ -51,3 +51,33 @@ @ARTICLE{PKSB17 adsnote = {Provided by the SAO/NASA Astrophysics Data System} } +@ARTICLE{2017arXiv170202600H, + author = {{Huff}, E. and {Mandelbaum}, R.}, + title = "{Metacalibration: Direct Self-Calibration of Biases in Shear Measurement}", + journal = {arXiv}, +archivePrefix = "arXiv", + volume = {1702.02600}, + keywords = {Astrophysics - Cosmology and Nongalactic Astrophysics}, + year = 2017, + month = feb, + adsurl = {http://adsabs.harvard.edu/abs/2017arXiv170202600H}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + +@ARTICLE{2017ApJ...841...24S, + author = {{Sheldon}, E.~S. and {Huff}, E.~M.}, + title = "{Practical Weak-lensing Shear Measurement with Metacalibration}", + journal = {\apj}, +archivePrefix = "arXiv", + eprint = {1702.02601}, + keywords = {cosmology: observations, gravitational lensing: weak, methods: observational}, + year = 2017, + month = may, + volume = 841, + eid = {24}, + pages = {24}, + doi = {10.3847/1538-4357/aa704b}, + adsurl = {http://adsabs.harvard.edu/abs/2017ApJ...841...24S}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + diff --git a/shearbook/_toc.yml b/shearbook/_toc.yml index aa5aba1..83eea4d 100644 --- a/shearbook/_toc.yml +++ b/shearbook/_toc.yml @@ -28,8 +28,10 @@ chapters: - file: bias/bias-intro sections: + - file: bias/bias-definition - file: bias/model-bias - file: bias/noise-bias + - file: bias/measuring-bias - file: bias/bias-ref - file: calibration/calibration-intro sections: diff --git a/shearbook/bias/bias-definition.ipynb b/shearbook/bias/bias-definition.ipynb new file mode 100644 index 0000000..bc33fbe --- /dev/null +++ b/shearbook/bias/bias-definition.ipynb @@ -0,0 +1,67 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " (sec:bias-definition)=\n", + "# Shear bias definition\n", + "\n", + "In most cases, shear bias can be decomposed in a multiplicative and an additive part.\n", + "\n", + "## Population shear bias\n", + "\n", + "First, we define the shear bias for a population of galaxies. First, let us recall the equation that,\n", + "in the weak-lensing regime, relates observed and intrincic ellipticity to reduced shear,\n", + "\n", + "$$\\varepsilon^{\\textrm{obs}}_\\alpha = \\varepsilon^{\\textrm{I}}_\\alpha + g\\alpha,$$\n", + "\n", + "for components $\\alpha = 1, 2$. If the mean intrinsic ellipticity is zero, $\\langle \\varepsilon^{\\textrm{I}}_\\alpha \\rangle = 0$, the observed ellipticity is an unbiased estimator of the shear, $\\langle \\varepsilon^{\\textrm{obs}}_\\alpha \\rangle = g_\\alpha$ in this basic case.\n", + "\n", + "Next, we now introduce the ensemble-average additive bias $c_\\alpha$, and multiplicative bias $m_\\alpha$, and write\n", + "\n", + "$$\n", + "\\langle \\varepsilon^{\\textrm{obs}}_\\alpha \\rangle \\equiv g^{\\textrm{obs}} = c_\\alpha + (1 + m_\\alpha) g_\\alpha.\n", + "$$ (shear-estim-biased)\n", + "\n", + "A common calibration method is to estimate the population biases $c_\\alpha$, $m_\\alpha$, by simulating a large number of galaxy images with different properties and shear. These measured values are then applied to the observed galaxy ellipticities. The quantity $(\\varepsilon^{\\textrm{obs}}_\\alpha - c_\\alpha) / (1 + m_\\alpha)$ is then an unbiased estimator of the reduced shear.\n", + "\n", + "## Individual galaxy shear bias\n", + "\n", + "We see that the multiplicative bias in {eq}`shear-estim-biased` can be interpreted as derivative, or response, of the average observed ellipticity with respect to the shear. Accounting for the two-component nature of ellipticity, and written for an individual galaxy, we define the response matrix $R$ ({cite}:`2017arXiv170202600H`, {cite}:`2017arXiv170202600H`) by\n", + "\n", + "$$R_{\\alpha\\beta} = \\frac{\\partial \\varepsilon^{\\textrm{obs}}_\\alpha}{\\partial g_\\beta} .$$\n", + "\n", + "The additive shear bias for an individual galaxy can be defined as well, in the case of simulations where the intrinsic ellipticity is known,\n", + "\n", + "$$a_\\alpha = \\varepsilon^{\\textrm{obs}}_\\alpha - R_{\\alpha\\alpha} g_\\alpha - \\varepsilon^{\\textrm{I}}_\\alpha .$$\n", + "\n", + "\n", + "\n", + "In the case of individually measured shear bias, one can set up a calibration scheme by building the population shear bias quantities $\\langle R \\rangle$ and $\\langle \\vec a \\rangle$. These are then applied to the observed galaxy ellipticities as\n", + "$\\langle R \\rangle^{-1} \\left( \\vec \\varepsilon^{\\textrm{obs}} - \\langle \\vec a \\rangle \\right)$. " + ] + } + ], + "metadata": { + "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.9.0" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/shearbook/bias/measuring-bias.ipybn b/shearbook/bias/measuring-bias.ipynb similarity index 100% rename from shearbook/bias/measuring-bias.ipybn rename to shearbook/bias/measuring-bias.ipynb diff --git a/shearbook/moments/moments-basic.ipynb b/shearbook/moments/moments-basic.ipynb index 28ddc82..e7df4bf 100644 --- a/shearbook/moments/moments-basic.ipynb +++ b/shearbook/moments/moments-basic.ipynb @@ -157,7 +157,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -488,7 +488,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.5" + "version": "3.9.0" }, "toc": { "base_numbering": 1, From b3e529371c707ba92afda404711d75c73394c648 Mon Sep 17 00:00:00 2001 From: mkilbing Date: Mon, 30 Nov 2020 13:06:52 +0100 Subject: [PATCH 06/18] small changes to bias-definition notebook --- shearbook/bias/bias-definition.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shearbook/bias/bias-definition.ipynb b/shearbook/bias/bias-definition.ipynb index bc33fbe..22db2f1 100644 --- a/shearbook/bias/bias-definition.ipynb +++ b/shearbook/bias/bias-definition.ipynb @@ -28,7 +28,7 @@ "\n", "## Individual galaxy shear bias\n", "\n", - "We see that the multiplicative bias in {eq}`shear-estim-biased` can be interpreted as derivative, or response, of the average observed ellipticity with respect to the shear. Accounting for the two-component nature of ellipticity, and written for an individual galaxy, we define the response matrix $R$ ({cite}:`2017arXiv170202600H`, {cite}:`2017arXiv170202600H`) by\n", + "We see that the multiplicative bias in {eq}`shear-estim-biased` can be interpreted as derivative, or response, of the average observed ellipticity with respect to the shear. Accounting for the two-component nature of ellipticity, and written for an individual galaxy, we define the response matrix $R$ {cite}:`2017arXiv170202600H`, {cite}:`2017arXiv170202600H` by\n", "\n", "$$R_{\\alpha\\beta} = \\frac{\\partial \\varepsilon^{\\textrm{obs}}_\\alpha}{\\partial g_\\beta} .$$\n", "\n", From 8ba9bdb1619c0f6f5a38d6e09e4d97c220f4d925 Mon Sep 17 00:00:00 2001 From: mkilbing Date: Mon, 30 Nov 2020 13:54:01 +0100 Subject: [PATCH 07/18] Adding python code to measuring-bias; need to install shear_bias --- shearbook/_bibliography/z_bias.bib | 2 +- shearbook/bias/bias-definition.ipynb | 2 +- shearbook/bias/measuring-bias.ipynb | 153 +++++++++++++++++++++++++- shearbook/moments/moments-basic.ipynb | 71 ++++++++++-- 4 files changed, 213 insertions(+), 15 deletions(-) diff --git a/shearbook/_bibliography/z_bias.bib b/shearbook/_bibliography/z_bias.bib index 5935aa1..0f5368b 100644 --- a/shearbook/_bibliography/z_bias.bib +++ b/shearbook/_bibliography/z_bias.bib @@ -64,7 +64,7 @@ @ARTICLE{2017arXiv170202600H adsnote = {Provided by the SAO/NASA Astrophysics Data System} } -@ARTICLE{2017ApJ...841...24S, +@ARTICLE{SH17, author = {{Sheldon}, E.~S. and {Huff}, E.~M.}, title = "{Practical Weak-lensing Shear Measurement with Metacalibration}", journal = {\apj}, diff --git a/shearbook/bias/bias-definition.ipynb b/shearbook/bias/bias-definition.ipynb index 22db2f1..473f633 100644 --- a/shearbook/bias/bias-definition.ipynb +++ b/shearbook/bias/bias-definition.ipynb @@ -28,7 +28,7 @@ "\n", "## Individual galaxy shear bias\n", "\n", - "We see that the multiplicative bias in {eq}`shear-estim-biased` can be interpreted as derivative, or response, of the average observed ellipticity with respect to the shear. Accounting for the two-component nature of ellipticity, and written for an individual galaxy, we define the response matrix $R$ {cite}:`2017arXiv170202600H`, {cite}:`2017arXiv170202600H` by\n", + "We see that the multiplicative bias in {eq}`shear-estim-biased` can be interpreted as derivative, or response, of the average observed ellipticity with respect to the shear. Accounting for the two-component nature of ellipticity, and written for an individual galaxy, we define the response matrix $R$ ({cite}`2017arXiv170202600H`, {cite}`SH17`) by\n", "\n", "$$R_{\\alpha\\beta} = \\frac{\\partial \\varepsilon^{\\textrm{obs}}_\\alpha}{\\partial g_\\beta} .$$\n", "\n", diff --git a/shearbook/bias/measuring-bias.ipynb b/shearbook/bias/measuring-bias.ipynb index 0b3855e..ad2f7ea 100644 --- a/shearbook/bias/measuring-bias.ipynb +++ b/shearbook/bias/measuring-bias.ipynb @@ -1,5 +1,17 @@ { "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```{code-block}\n", + ":class: thebe, thebe-init\n", + "# Automatic import for live code\n", + "import numpy as np\n", + "from shrbk.plot import *\n", + "```" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -18,8 +30,147 @@ "\n", "However, this does not account for the variance from the measured ellipticity, which does not only depend on the intrinsic one, but on the PSF, pixel noise, etc. In addition, all versions of the rotated galaxy need to be detected and have an measured shape, and therefore, selection biases are difficult to quantify with this method.\n", "\n", - "To reduce shape noise (to virtually zero), {cite}`PKSB17` uses the same noise realisation is used for each sheared, and the unsheared image. The resulting estimate of the response per galaxy is highly precise. It is still not very accurate, since the measured value depends on the noise realisation. To increase the accuracy, a large number of simulated galaxies with different noise realisations is required. These galaxies are however anyway simulated to cover the large parameter space of galaxy properties." + "To reduce shape noise (to virtually zero), {cite}`PKSB17` uses the same noise realisation is used for each sheared, and the unsheared image. The resulting estimate of the response per galaxy is highly precise. It is still not very accurate, since the measured value depends on the noise realisation. To increase the accuracy, a large number of simulated galaxies with different noise realisations is required. These galaxies are however anyway simulated to cover the large parameter space of galaxy properties.\n", + "\n", + "## Example\n", + "Here we compute the individual shear bias from image simulations. This method is implemented in the library `shear_bias`." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "ename": "ModuleNotFoundError", + "evalue": "No module named 'shear_bias'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;31m# Load the library\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0;32mimport\u001b[0m \u001b[0mshear_bias\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'shear_bias'" + ] + } + ], + "source": [ + "# Load the library\n", + "import shear_bias" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Set up" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Shear values" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First, set the value of the small shear $\\Delta g$ to add to the galaxy images." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "dg = 0.02" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Define the five steps, one in each positive and negative direction along the two coordinate axis, plus the original unchanged image (0, 0)." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "g_steps = [(-1, 0), (0, -1), (1, 0), (0, 1), (0, 0)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Create dictionary of shear values with step tuples as keys." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "g_dict = {}\n", + "for step in g_steps:\n", + " g_dict[step] = (step[0] * dg, step[1] * dg)\n", + "g_values = g_dict.values()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Number of images" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The number of galaxy postage stamps per image is nxy_tiles^2.\n", + "If this number is modified, all output files from a previous\n", + "run should be deleted." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "nxy_tiles = 4" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The number of files with different constant shear and PSF.\n", + "This parameter can be changed without deleting previous output files." ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "nfiles = 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { diff --git a/shearbook/moments/moments-basic.ipynb b/shearbook/moments/moments-basic.ipynb index e7df4bf..8983849 100644 --- a/shearbook/moments/moments-basic.ipynb +++ b/shearbook/moments/moments-basic.ipynb @@ -6,7 +6,7 @@ "source": [ "```{toggle}\n", "```{code-block}\n", - ":class: thebe, thebe-init\n", + " :class: thebe, thebe-init\n", "# Automatic import for live code\n", "import numpy as np\n", "from shrbk.plot import *\n", @@ -34,14 +34,28 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": { "tags": [ "thebelab-init", "hide-input" ] }, - "outputs": [], + "outputs": [ + { + "data": { + "text/markdown": [ + "" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Import dependencies\n", "import numpy as np\n", @@ -66,9 +80,22 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Horizontal Line\n", + "Vertical Line\n", + "Diagonal Line (Up)\n", + "Diagonal Line (Down)\n", + "Circle\n", + "Circle (Off-Centre)\n" + ] + } + ], "source": [ "for key in data_dict.keys():\n", " print(key)" @@ -83,9 +110,21 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[0. 0. 0. 0. 0.]\n", + " [0. 1. 0. 0. 0.]\n", + " [0. 0. 1. 0. 0.]\n", + " [0. 0. 0. 1. 0.]\n", + " [0. 0. 0. 0. 0.]]\n" + ] + } + ], "source": [ "data_name = 'Diagonal Line (Down)'\n", "print(data_dict[data_name])" @@ -128,9 +167,17 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "F = 3.0\n" + ] + } + ], "source": [ "print('F = {}'.format(np.sum(data_dict['Horizontal Line'])))" ] From 66db4f94b271464204821d0ade39ae7550b2aa91 Mon Sep 17 00:00:00 2001 From: mkilbing Date: Tue, 1 Dec 2020 21:17:33 +0100 Subject: [PATCH 08/18] measuring bias using galsim, images can be created using command line galsim --- shearbook/bias/measuring-bias.ipynb | 147 ++++++++++++++++++++++++---- 1 file changed, 130 insertions(+), 17 deletions(-) diff --git a/shearbook/bias/measuring-bias.ipynb b/shearbook/bias/measuring-bias.ipynb index ad2f7ea..631f7e5 100644 --- a/shearbook/bias/measuring-bias.ipynb +++ b/shearbook/bias/measuring-bias.ipynb @@ -38,24 +38,27 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 1, "metadata": {}, "outputs": [ { - "ename": "ModuleNotFoundError", - "evalue": "No module named 'shear_bias'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;31m# Load the library\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0;32mimport\u001b[0m \u001b[0mshear_bias\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'shear_bias'" - ] + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ "# Load the library\n", - "import shear_bias" + "import shear_bias as sb\n", + "\n", + "# Debugging\n", + "import imp\n", + "imp.reload(sb)" ] }, { @@ -81,7 +84,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -97,7 +100,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -113,7 +116,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -141,7 +144,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -153,18 +156,128 @@ "metadata": {}, "source": [ "The number of files with different constant shear and PSF.\n", - "This parameter can be changed without deleting previous output files." + "This parameter can be changed without deleting previous output files.\n", + "\n", + "Since processing takes quite some time, start with a small value for `nfiles`, and run the rest of this example. This value can be increased later, to download and process more image files without re-doing previously computed results." ] }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "nfiles = 2" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Downloading image meta-data\n", + "\n", + "First, download `great3` meta-data and config files to create images." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Creating galsim/great3 input dir 'images/config/great3_csc'\n", + "Downloading https://github.com/martinkilbinger/ede18_shear_cal/blob/master/data/great3/control/space/constant/epoch_catalog-000-0.fits?raw=true to images/config/great3_csc/epoch_catalog-000-0.fits...\n", + "MKDEBUG\n", + "Downloading https://github.com/martinkilbinger/ede18_shear_cal/blob/master/data/great3/control/space/constant/epoch_catalog-001-0.fits?raw=true to images/config/great3_csc/epoch_catalog-001-0.fits...\n", + "MKDEBUG\n", + "Downloading https://github.com/martinkilbinger/ede18_shear_cal/blob/master/data/great3/control/space/constant/epoch_parameters-000-0.yaml?raw=true to images/config/great3_csc/epoch_parameters-000-0.yaml...\n", + "MKDEBUG\n", + "Downloading https://github.com/martinkilbinger/ede18_shear_cal/blob/master/data/great3/control/space/constant/epoch_parameters-001-0.yaml?raw=true to images/config/great3_csc/epoch_parameters-001-0.yaml...\n", + "MKDEBUG\n", + "Downloading https://github.com/martinkilbinger/ede18_shear_cal/blob/master/data/great3/control/space/constant/star_catalog-000-0.fits?raw=true to images/config/great3_csc/star_catalog-000-0.fits...\n", + "MKDEBUG\n", + "Downloading https://github.com/martinkilbinger/ede18_shear_cal/blob/master/data/great3/control/space/constant/star_catalog-001-0.fits?raw=true to images/config/great3_csc/star_catalog-001-0.fits...\n", + "MKDEBUG\n", + "6 files downloaded\n", + "*** End download_great3_data ***\n" + ] + } + ], + "source": [ + "# `great3` branch\n", + "great3_branch = 'control/space/constant'\n", + "\n", + "# Remote and local image directories\n", + "remote_cfg_dir = 'https://github.com/martinkilbinger/ede18_shear_cal/blob/master/data/great3/{}'\\\n", + " ''.format(great3_branch)\n", + "cfg_dir = 'images/config/great3_csc' \n", + "n_downloaded = sb.download_great3_data(cfg_dir, remote_cfg_dir, great3_branch, nfiles, mode='?raw=true')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Create images\n", + "Run `galsim` to create galaxy images." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "*** Start create_all_sims_great3 ***\n", + "For shear (-0.02,0.0), 2 images need to be created, running 'galsim'\n", + "running galsim images/config/galsim/csc_multishear.yaml gal.shear.g1=-0.02 gal.shear.g2=0.0 input.catalog.dir=images/config/great3_csc input.dict.dir=images/config/great3_csc output.dir=images/galsim/g1_-0.02_g2_0.0 output.file_name.format=image-%03d-%1d.fits output.nfiles=2 image.nx_tiles=4 image.ny_tiles=4\n" + ] + }, + { + "ename": "FileNotFoundError", + "evalue": "[Errno 2] No such file or directory: 'galsim'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mFileNotFoundError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 13\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 14\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 15\u001b[0;31m sb.create_all_sims_great3(g_values, galsim_config_path, galsim_config_psf_path, \\\n\u001b[0m\u001b[1;32m 16\u001b[0m \u001b[0mcfg_dir\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mimages_base_dir\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0moutput_gal_fname_format\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;31m \u001b[0m\u001b[0;31m\\\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 17\u001b[0m output_psf_fname_format, nxy_tiles=nxy_tiles, nfiles=nfiles)\n", + "\u001b[0;32m~/miniconda3/envs/shear/lib/python3.9/site-packages/shear_bias-0.3-py3.9-linux-x86_64.egg/shear_bias/simulations.py\u001b[0m in \u001b[0;36mcreate_all_sims_great3\u001b[0;34m(g_list, config_path, config_psf_path, input_dir, output_base_dir, output_gal_fname_format, output_psf_fname_format, nxy_tiles, nfiles, job)\u001b[0m\n\u001b[1;32m 155\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn_out_missing\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 156\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'For shear ({},{}), {} images need to be created, running \\'galsim\\''\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mg\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mg\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn_out_missing\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 157\u001b[0;31m create_sim_one_shear_great3(g, config_path, input_dir, output_dir, \\\n\u001b[0m\u001b[1;32m 158\u001b[0m extra_str, output_gal_fname_format, nxy_tiles=nxy_tiles, job=job)\n\u001b[1;32m 159\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/miniconda3/envs/shear/lib/python3.9/site-packages/shear_bias-0.3-py3.9-linux-x86_64.egg/shear_bias/simulations.py\u001b[0m in \u001b[0;36mcreate_sim_one_shear_great3\u001b[0;34m(g, config_path, input_dir, output_dir, extra_str, output_fname_format, nxy_tiles, job)\u001b[0m\n\u001b[1;32m 209\u001b[0m \u001b[0mgalsim_command\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'galsim {0} gal.shear.g1={1} gal.shear.g2={2} input.catalog.dir={3} input.dict.dir={3} output.dir={4} output.file_name.format={5}{6}'\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0;31m \u001b[0m\u001b[0;31m\\\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 210\u001b[0m \u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mconfig_path\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mg\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mg\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minput_dir\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0moutput_dir\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0moutput_fname_format\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mextra_str\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 211\u001b[0;31m \u001b[0mmisc\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun_command\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgalsim_command\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mjob\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mjob\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 212\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 213\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/miniconda3/envs/shear/lib/python3.9/site-packages/shear_bias-0.3-py3.9-linux-x86_64.egg/shear_bias/misc.py\u001b[0m in \u001b[0;36mrun_command\u001b[0;34m(cmd, job, output_path, shell)\u001b[0m\n\u001b[1;32m 103\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 104\u001b[0m \u001b[0mc\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mshlex\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msplit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcmd\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 105\u001b[0;31m \u001b[0mpipe\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msubprocess\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mPopen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mc\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstdout\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0msubprocess\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mPIPE\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 106\u001b[0m \u001b[0;31m#pipe = subprocess.Popen(c)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 107\u001b[0m \u001b[0mex\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/miniconda3/envs/shear/lib/python3.9/subprocess.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags, restore_signals, start_new_session, pass_fds, user, group, extra_groups, encoding, errors, text, umask)\u001b[0m\n\u001b[1;32m 945\u001b[0m encoding=encoding, errors=errors)\n\u001b[1;32m 946\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 947\u001b[0;31m self._execute_child(args, executable, preexec_fn, close_fds,\n\u001b[0m\u001b[1;32m 948\u001b[0m \u001b[0mpass_fds\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcwd\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0menv\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 949\u001b[0m \u001b[0mstartupinfo\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcreationflags\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mshell\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/miniconda3/envs/shear/lib/python3.9/subprocess.py\u001b[0m in \u001b[0;36m_execute_child\u001b[0;34m(self, args, executable, preexec_fn, close_fds, pass_fds, cwd, env, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, restore_signals, gid, gids, uid, umask, start_new_session)\u001b[0m\n\u001b[1;32m 1817\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0merrno_num\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1818\u001b[0m \u001b[0merr_msg\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mos\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstrerror\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0merrno_num\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1819\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mchild_exception_type\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0merrno_num\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0merr_msg\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0merr_filename\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1820\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mchild_exception_type\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0merr_msg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1821\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mFileNotFoundError\u001b[0m: [Errno 2] No such file or directory: 'galsim'" + ] + } + ], + "source": [ + "# `great3` config files for galsim \n", + "galsim_config_fname = 'csc_multishear.yaml'\n", + "galsim_config_psf_fname = 'csc_psf.yaml'\n", + "galsim_config_dir = 'images/config/galsim'\n", + "galsim_config_path = '{}/{}'.format(galsim_config_dir, galsim_config_fname)\n", + "galsim_config_psf_path = '{}/{}'.format(galsim_config_dir, galsim_config_psf_fname)\n", + "\n", + "images_base_dir = 'images/galsim'\n", + "\n", + "# Image file name formats\n", + "output_gal_fname_format = 'image-%03d-%1d.fits'\n", + "output_psf_fname_format = 'starfield_image-%03d-%1d.fits'\n", + "\n", + "\n", + "sb.create_all_sims_great3(g_values, galsim_config_path, galsim_config_psf_path, \\\n", + " cfg_dir, images_base_dir, output_gal_fname_format, \\\n", + " output_psf_fname_format, nxy_tiles=nxy_tiles, nfiles=nfiles)" + ] + }, { "cell_type": "code", "execution_count": null, From 47bed88d955cc5aba19063e708ae715b12eba72b Mon Sep 17 00:00:00 2001 From: Martin Kilbinger Date: Fri, 22 Jan 2021 18:31:54 +0100 Subject: [PATCH 09/18] Some updates by hand from develop --- .gitignore | 5 +- shearbook/_bibliography/z_bias.bib | 1 - shearbook/moments/moments-basic.ipynb | 75 +++++---------------------- 3 files changed, 18 insertions(+), 63 deletions(-) diff --git a/.gitignore b/.gitignore index 2386a80..9acf192 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,7 @@ __pycache__/ # Distribution / packaging .Python build/ -*_build* +_build/ develop-eggs/ dist/ downloads/ @@ -128,3 +128,6 @@ dmypy.json # Pyre type checker .pyre/ + +# Jupyter book build +_build diff --git a/shearbook/_bibliography/z_bias.bib b/shearbook/_bibliography/z_bias.bib index 0f5368b..f597cae 100644 --- a/shearbook/_bibliography/z_bias.bib +++ b/shearbook/_bibliography/z_bias.bib @@ -80,4 +80,3 @@ @ARTICLE{SH17 adsurl = {http://adsabs.harvard.edu/abs/2017ApJ...841...24S}, adsnote = {Provided by the SAO/NASA Astrophysics Data System} } - diff --git a/shearbook/moments/moments-basic.ipynb b/shearbook/moments/moments-basic.ipynb index 8983849..28ddc82 100644 --- a/shearbook/moments/moments-basic.ipynb +++ b/shearbook/moments/moments-basic.ipynb @@ -6,7 +6,7 @@ "source": [ "```{toggle}\n", "```{code-block}\n", - " :class: thebe, thebe-init\n", + ":class: thebe, thebe-init\n", "# Automatic import for live code\n", "import numpy as np\n", "from shrbk.plot import *\n", @@ -34,28 +34,14 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": { "tags": [ "thebelab-init", "hide-input" ] }, - "outputs": [ - { - "data": { - "text/markdown": [ - "" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# Import dependencies\n", "import numpy as np\n", @@ -80,22 +66,9 @@ }, { "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Horizontal Line\n", - "Vertical Line\n", - "Diagonal Line (Up)\n", - "Diagonal Line (Down)\n", - "Circle\n", - "Circle (Off-Centre)\n" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "for key in data_dict.keys():\n", " print(key)" @@ -110,21 +83,9 @@ }, { "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[[0. 0. 0. 0. 0.]\n", - " [0. 1. 0. 0. 0.]\n", - " [0. 0. 1. 0. 0.]\n", - " [0. 0. 0. 1. 0.]\n", - " [0. 0. 0. 0. 0.]]\n" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "data_name = 'Diagonal Line (Down)'\n", "print(data_dict[data_name])" @@ -167,17 +128,9 @@ }, { "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "F = 3.0\n" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "print('F = {}'.format(np.sum(data_dict['Horizontal Line'])))" ] @@ -204,7 +157,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -535,7 +488,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.0" + "version": "3.8.5" }, "toc": { "base_numbering": 1, From cd6df0834537bee403c5b923ee8c6da919aa49e7 Mon Sep 17 00:00:00 2001 From: Martin Kilbinger Date: Sun, 14 Mar 2021 12:11:31 +0100 Subject: [PATCH 10/18] measuring bias: PSF and gal loop --- shearbook/bias/measuring-bias.ipynb | 232 +++++++++++++++------------- 1 file changed, 122 insertions(+), 110 deletions(-) diff --git a/shearbook/bias/measuring-bias.ipynb b/shearbook/bias/measuring-bias.ipynb index 631f7e5..1784941 100644 --- a/shearbook/bias/measuring-bias.ipynb +++ b/shearbook/bias/measuring-bias.ipynb @@ -38,27 +38,13 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 13, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 1, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "# Load the library\n", + "# Load the libraries\n", "import shear_bias as sb\n", - "\n", - "# Debugging\n", - "import imp\n", - "imp.reload(sb)" + "import galsim" ] }, { @@ -84,7 +70,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -100,7 +86,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -116,166 +102,192 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 47, "metadata": {}, "outputs": [], "source": [ - "g_dict = {}\n", + "g_values = {}\n", "for step in g_steps:\n", - " g_dict[step] = (step[0] * dg, step[1] * dg)\n", - "g_values = g_dict.values()" + " g_values[step] = (step[0] * dg, step[1] * dg)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "#### Number of images" + "#### Number of galaxy images" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [], + "source": [ + "n_gal = 10" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "The number of galaxy postage stamps per image is nxy_tiles^2.\n", - "If this number is modified, all output files from a previous\n", - "run should be deleted." + "### Downloading HST COSMOS galaxy images" ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "nxy_tiles = 4" + "data_dir = 'data'\n", + "cat_file_name = 'real_galaxy_catalog_23.5_example.fits'\n", + "\n", + "sky_level = 1e6 # ADU / arcsec^2\n", + "pixel_scale = 0.16 # arcsec\n", + "random_seed = 5693562491" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": 6, "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Data file data/real_galaxy_catalog_23.5_example.fits already exists, skipping...\n", + "Data file data/acs_I_unrot_sci_20_cf.fits already exists, skipping...\n", + "Data file data/real_galaxy_images.fits already exists, skipping...\n", + "Data file data/real_galaxy_PSF_images.fits already exists, skipping...\n" + ] + } + ], "source": [ - "The number of files with different constant shear and PSF.\n", - "This parameter can be changed without deleting previous output files.\n", - "\n", - "Since processing takes quite some time, start with a small value for `nfiles`, and run the rest of this example. This value can be increased later, to download and process more image files without re-doing previously computed results." + "sb.download_HST_images(dest_dir=data_dir)" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 51, "metadata": {}, "outputs": [], "source": [ - "nfiles = 2" + "real_galaxy_catalog = galsim.RealGalaxyCatalog(cat_file_name, dir=data_dir)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Downloading image meta-data\n", - "\n", - "First, download `great3` meta-data and config files to create images." + "### PSF" ] }, { "cell_type": "code", - "execution_count": 8, - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Creating galsim/great3 input dir 'images/config/great3_csc'\n", - "Downloading https://github.com/martinkilbinger/ede18_shear_cal/blob/master/data/great3/control/space/constant/epoch_catalog-000-0.fits?raw=true to images/config/great3_csc/epoch_catalog-000-0.fits...\n", - "MKDEBUG\n", - "Downloading https://github.com/martinkilbinger/ede18_shear_cal/blob/master/data/great3/control/space/constant/epoch_catalog-001-0.fits?raw=true to images/config/great3_csc/epoch_catalog-001-0.fits...\n", - "MKDEBUG\n", - "Downloading https://github.com/martinkilbinger/ede18_shear_cal/blob/master/data/great3/control/space/constant/epoch_parameters-000-0.yaml?raw=true to images/config/great3_csc/epoch_parameters-000-0.yaml...\n", - "MKDEBUG\n", - "Downloading https://github.com/martinkilbinger/ede18_shear_cal/blob/master/data/great3/control/space/constant/epoch_parameters-001-0.yaml?raw=true to images/config/great3_csc/epoch_parameters-001-0.yaml...\n", - "MKDEBUG\n", - "Downloading https://github.com/martinkilbinger/ede18_shear_cal/blob/master/data/great3/control/space/constant/star_catalog-000-0.fits?raw=true to images/config/great3_csc/star_catalog-000-0.fits...\n", - "MKDEBUG\n", - "Downloading https://github.com/martinkilbinger/ede18_shear_cal/blob/master/data/great3/control/space/constant/star_catalog-001-0.fits?raw=true to images/config/great3_csc/star_catalog-001-0.fits...\n", - "MKDEBUG\n", - "6 files downloaded\n", - "*** End download_great3_data ***\n" - ] - } - ], + "execution_count": 28, + "metadata": {}, + "outputs": [], "source": [ - "# `great3` branch\n", - "great3_branch = 'control/space/constant'\n", + "# Specify PSF with two components\n", + "fwhm_psf = [0.6, 2.3] # in arcsec\n", + "flux_frac_psf = [0.8, 0.2] # flux fraction" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [], + "source": [ + "# Create PSF\n", + "psf = galsim.Gaussian(fwhm=fwhm_psf[0], flux=flux_frac_psf[0])\n", + "for i in range(1, len(fwhm_psf)):\n", + " psf = psf + galsim.Gaussian(fwhm=fwhm_psf[i], flux=flux_frac_psf[i])\n", "\n", - "# Remote and local image directories\n", - "remote_cfg_dir = 'https://github.com/martinkilbinger/ede18_shear_cal/blob/master/data/great3/{}'\\\n", - " ''.format(great3_branch)\n", - "cfg_dir = 'images/config/great3_csc' \n", - "n_downloaded = sb.download_great3_data(cfg_dir, remote_cfg_dir, great3_branch, nfiles, mode='?raw=true')" + "# Draw PSF\n", + "psf_image = psf.drawImage(scale=pixel_scale)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Create images\n", - "Run `galsim` to create galaxy images." + "### Galaxies" ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 54, "metadata": {}, + "outputs": [], + "source": [ + "gal_flux = 1e5 # arbitrary, choose large value for not too noisy images\n", + "magnification = 1" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "scrolled": true + }, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "*** Start create_all_sims_great3 ***\n", - "For shear (-0.02,0.0), 2 images need to be created, running 'galsim'\n", - "running galsim images/config/galsim/csc_multishear.yaml gal.shear.g1=-0.02 gal.shear.g2=0.0 input.catalog.dir=images/config/great3_csc input.dict.dir=images/config/great3_csc output.dir=images/galsim/g1_-0.02_g2_0.0 output.file_name.format=image-%03d-%1d.fits output.nfiles=2 image.nx_tiles=4 image.ny_tiles=4\n" - ] - }, - { - "ename": "FileNotFoundError", - "evalue": "[Errno 2] No such file or directory: 'galsim'", + "ename": "KeyboardInterrupt", + "evalue": "", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mFileNotFoundError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 13\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 14\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 15\u001b[0;31m sb.create_all_sims_great3(g_values, galsim_config_path, galsim_config_psf_path, \\\n\u001b[0m\u001b[1;32m 16\u001b[0m \u001b[0mcfg_dir\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mimages_base_dir\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0moutput_gal_fname_format\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;31m \u001b[0m\u001b[0;31m\\\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 17\u001b[0m output_psf_fname_format, nxy_tiles=nxy_tiles, nfiles=nfiles)\n", - "\u001b[0;32m~/miniconda3/envs/shear/lib/python3.9/site-packages/shear_bias-0.3-py3.9-linux-x86_64.egg/shear_bias/simulations.py\u001b[0m in \u001b[0;36mcreate_all_sims_great3\u001b[0;34m(g_list, config_path, config_psf_path, input_dir, output_base_dir, output_gal_fname_format, output_psf_fname_format, nxy_tiles, nfiles, job)\u001b[0m\n\u001b[1;32m 155\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn_out_missing\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 156\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'For shear ({},{}), {} images need to be created, running \\'galsim\\''\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mg\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mg\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn_out_missing\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 157\u001b[0;31m create_sim_one_shear_great3(g, config_path, input_dir, output_dir, \\\n\u001b[0m\u001b[1;32m 158\u001b[0m extra_str, output_gal_fname_format, nxy_tiles=nxy_tiles, job=job)\n\u001b[1;32m 159\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/miniconda3/envs/shear/lib/python3.9/site-packages/shear_bias-0.3-py3.9-linux-x86_64.egg/shear_bias/simulations.py\u001b[0m in \u001b[0;36mcreate_sim_one_shear_great3\u001b[0;34m(g, config_path, input_dir, output_dir, extra_str, output_fname_format, nxy_tiles, job)\u001b[0m\n\u001b[1;32m 209\u001b[0m \u001b[0mgalsim_command\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'galsim {0} gal.shear.g1={1} gal.shear.g2={2} input.catalog.dir={3} input.dict.dir={3} output.dir={4} output.file_name.format={5}{6}'\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0;31m \u001b[0m\u001b[0;31m\\\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 210\u001b[0m \u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mconfig_path\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mg\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mg\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minput_dir\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0moutput_dir\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0moutput_fname_format\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mextra_str\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 211\u001b[0;31m \u001b[0mmisc\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun_command\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgalsim_command\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mjob\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mjob\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 212\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 213\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/miniconda3/envs/shear/lib/python3.9/site-packages/shear_bias-0.3-py3.9-linux-x86_64.egg/shear_bias/misc.py\u001b[0m in \u001b[0;36mrun_command\u001b[0;34m(cmd, job, output_path, shell)\u001b[0m\n\u001b[1;32m 103\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 104\u001b[0m \u001b[0mc\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mshlex\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msplit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcmd\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 105\u001b[0;31m \u001b[0mpipe\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msubprocess\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mPopen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mc\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstdout\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0msubprocess\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mPIPE\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 106\u001b[0m \u001b[0;31m#pipe = subprocess.Popen(c)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 107\u001b[0m \u001b[0mex\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/miniconda3/envs/shear/lib/python3.9/subprocess.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags, restore_signals, start_new_session, pass_fds, user, group, extra_groups, encoding, errors, text, umask)\u001b[0m\n\u001b[1;32m 945\u001b[0m encoding=encoding, errors=errors)\n\u001b[1;32m 946\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 947\u001b[0;31m self._execute_child(args, executable, preexec_fn, close_fds,\n\u001b[0m\u001b[1;32m 948\u001b[0m \u001b[0mpass_fds\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcwd\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0menv\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 949\u001b[0m \u001b[0mstartupinfo\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcreationflags\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mshell\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/miniconda3/envs/shear/lib/python3.9/subprocess.py\u001b[0m in \u001b[0;36m_execute_child\u001b[0;34m(self, args, executable, preexec_fn, close_fds, pass_fds, cwd, env, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, restore_signals, gid, gids, uid, umask, start_new_session)\u001b[0m\n\u001b[1;32m 1817\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0merrno_num\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1818\u001b[0m \u001b[0merr_msg\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mos\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstrerror\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0merrno_num\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1819\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mchild_exception_type\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0merrno_num\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0merr_msg\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0merr_filename\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1820\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mchild_exception_type\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0merr_msg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1821\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mFileNotFoundError\u001b[0m: [Errno 2] No such file or directory: 'galsim'" + "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0mrng\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mgalsim\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mUniformDeviate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrandom_seed\u001b[0m\u001b[0;34m+\u001b[0m\u001b[0mk\u001b[0m\u001b[0;34m+\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 5\u001b[0;31m \u001b[0mgal\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mgalsim\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mRealGalaxy\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mreal_galaxy_catalog\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mindex\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mk\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mflux\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mgal_flux\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 6\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mstep\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mg_steps\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/anaconda3/envs/shear/lib/python3.9/site-packages/galsim/real.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, real_galaxy_catalog, index, id, random, rng, x_interpolant, k_interpolant, flux, flux_rescale, pad_factor, noise_pad_size, area_norm, gsparams, logger)\u001b[0m\n\u001b[1;32m 250\u001b[0m \u001b[0;31m# want it to be possible to have the RealGalaxyCatalog in another process, and the\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 251\u001b[0m \u001b[0;31m# BaseCorrelatedNoise object is not picklable. So we just build it here instead.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 252\u001b[0;31m \u001b[0mnoise_image\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpixel_scale\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvar\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mreal_galaxy_catalog\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgetNoiseProperties\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0muse_index\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 253\u001b[0m \u001b[0mlogger\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdebug\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'RealGalaxy %d: Got noise_image'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0muse_index\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 254\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcatalog_file\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mreal_galaxy_catalog\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgetFileName\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/anaconda3/envs/shear/lib/python3.9/site-packages/galsim/real.py\u001b[0m in \u001b[0;36mgetNoiseProperties\u001b[0;34m(self, i)\u001b[0m\n\u001b[1;32m 754\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlogger\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdebug\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'RealGalaxyCatalog %d: Got saved noise im'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 755\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 756\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnoise_lock\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0macquire\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 757\u001b[0m \u001b[0;31m# Again, a second check in case two processes get here at the same time.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 758\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnoise_file_name\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msaved_noise_im\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# pragma: no cover\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mKeyboardInterrupt\u001b[0m: " ] } ], "source": [ - "# `great3` config files for galsim \n", - "galsim_config_fname = 'csc_multishear.yaml'\n", - "galsim_config_psf_fname = 'csc_psf.yaml'\n", - "galsim_config_dir = 'images/config/galsim'\n", - "galsim_config_path = '{}/{}'.format(galsim_config_dir, galsim_config_fname)\n", - "galsim_config_psf_path = '{}/{}'.format(galsim_config_dir, galsim_config_psf_fname)\n", + "all_images = {}\n", + "for step in g_steps:\n", + " all_images[step] = []\n", + "\n", + "for k in range(n_gal):\n", "\n", - "images_base_dir = 'images/galsim'\n", + " print(k, end=' ')\n", + " rng = galsim.UniformDeviate(random_seed+k+1)\n", + " gal = galsim.RealGalaxy(real_galaxy_catalog, index=k, flux=gal_flux)\n", + " \n", + " for step in g_steps:\n", + " gal_sh = gal.shear(g1=g_values[step][0], g2=g_values[step][1])\n", "\n", - "# Image file name formats\n", - "output_gal_fname_format = 'image-%03d-%1d.fits'\n", - "output_psf_fname_format = 'starfield_image-%03d-%1d.fits'\n", + " if magnification != 1:\n", + " gal_sh = gal_sh.magnify(magnification)\n", + " \n", + " final = galsim.Convolve([psf, gal_sh])\n", + " \n", + " dx = rng() - 0.5\n", + " dy = rng() - 0.5\n", "\n", + " im = final.drawImage(scale=pixel_scale, offset=(dx,dy))\n", + " xsize, ysize = im.array.shape\n", + " \n", + " # Constant background\n", + " background = sky_level * pixel_scale**2\n", "\n", - "sb.create_all_sims_great3(g_values, galsim_config_path, galsim_config_psf_path, \\\n", - " cfg_dir, images_base_dir, output_gal_fname_format, \\\n", - " output_psf_fname_format, nxy_tiles=nxy_tiles, nfiles=nfiles)" + " im.addNoise(galsim.PoissonNoise(rng))\n", + " \n", + " all_images[step].append(im)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Downloading image meta-data\n", + "\n", + "First, download `great3` meta-data and config files to create images." ] }, { @@ -302,7 +314,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.0" + "version": "3.9.2" } }, "nbformat": 4, From 16ad417ccadc45a624e98683ea9a9156791ebf11 Mon Sep 17 00:00:00 2001 From: Martin Kilbinger Date: Fri, 26 Mar 2021 10:52:04 +0100 Subject: [PATCH 11/18] measuring bias: image creation done --- shearbook/bias/measuring-bias.ipynb | 129 +++++++++++++++------------- 1 file changed, 67 insertions(+), 62 deletions(-) diff --git a/shearbook/bias/measuring-bias.ipynb b/shearbook/bias/measuring-bias.ipynb index 1784941..701f210 100644 --- a/shearbook/bias/measuring-bias.ipynb +++ b/shearbook/bias/measuring-bias.ipynb @@ -38,13 +38,17 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Load the libraries\n", "import shear_bias as sb\n", - "import galsim" + "import galsim\n", + "import matplotlib.pylab as plt\n", + "import numpy as np\n", + "\n", + "%matplotlib inline" ] }, { @@ -70,7 +74,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -86,7 +90,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -102,7 +106,7 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -120,7 +124,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -143,34 +147,25 @@ "data_dir = 'data'\n", "cat_file_name = 'real_galaxy_catalog_23.5_example.fits'\n", "\n", - "sky_level = 1e6 # ADU / arcsec^2\n", + "sky_level = 1e5 # ADU / arcsec^2\n", + "gal_flux = 1e5 # arbitrary, choose large value for not too noisy images\n", + "\n", "pixel_scale = 0.16 # arcsec\n", "random_seed = 5693562491" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Data file data/real_galaxy_catalog_23.5_example.fits already exists, skipping...\n", - "Data file data/acs_I_unrot_sci_20_cf.fits already exists, skipping...\n", - "Data file data/real_galaxy_images.fits already exists, skipping...\n", - "Data file data/real_galaxy_PSF_images.fits already exists, skipping...\n" - ] - } - ], + "outputs": [], "source": [ "sb.download_HST_images(dest_dir=data_dir)" ] }, { "cell_type": "code", - "execution_count": 51, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -186,18 +181,7 @@ }, { "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [], - "source": [ - "# Specify PSF with two components\n", - "fwhm_psf = [0.6, 2.3] # in arcsec\n", - "flux_frac_psf = [0.8, 0.2] # flux fraction" - ] - }, - { - "cell_type": "code", - "execution_count": 30, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -219,37 +203,29 @@ }, { "cell_type": "code", - "execution_count": 54, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "gal_flux = 1e5 # arbitrary, choose large value for not too noisy images\n", - "magnification = 1" + "# Lensing magnification. Set to 1 for no magnification.\n", + "magnification = 1\n", + "\n", + "# Constant background\n", + "background = sky_level * pixel_scale**2" ] }, { "cell_type": "code", - "execution_count": 56, + "execution_count": null, "metadata": { "scrolled": true }, - "outputs": [ - { - "ename": "KeyboardInterrupt", - "evalue": "", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0mrng\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mgalsim\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mUniformDeviate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrandom_seed\u001b[0m\u001b[0;34m+\u001b[0m\u001b[0mk\u001b[0m\u001b[0;34m+\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 5\u001b[0;31m \u001b[0mgal\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mgalsim\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mRealGalaxy\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mreal_galaxy_catalog\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mindex\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mk\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mflux\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mgal_flux\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 6\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mstep\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mg_steps\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/anaconda3/envs/shear/lib/python3.9/site-packages/galsim/real.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, real_galaxy_catalog, index, id, random, rng, x_interpolant, k_interpolant, flux, flux_rescale, pad_factor, noise_pad_size, area_norm, gsparams, logger)\u001b[0m\n\u001b[1;32m 250\u001b[0m \u001b[0;31m# want it to be possible to have the RealGalaxyCatalog in another process, and the\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 251\u001b[0m \u001b[0;31m# BaseCorrelatedNoise object is not picklable. So we just build it here instead.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 252\u001b[0;31m \u001b[0mnoise_image\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpixel_scale\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvar\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mreal_galaxy_catalog\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgetNoiseProperties\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0muse_index\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 253\u001b[0m \u001b[0mlogger\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdebug\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'RealGalaxy %d: Got noise_image'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0muse_index\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 254\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcatalog_file\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mreal_galaxy_catalog\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgetFileName\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/anaconda3/envs/shear/lib/python3.9/site-packages/galsim/real.py\u001b[0m in \u001b[0;36mgetNoiseProperties\u001b[0;34m(self, i)\u001b[0m\n\u001b[1;32m 754\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlogger\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdebug\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'RealGalaxyCatalog %d: Got saved noise im'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 755\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 756\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnoise_lock\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0macquire\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 757\u001b[0m \u001b[0;31m# Again, a second check in case two processes get here at the same time.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 758\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnoise_file_name\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msaved_noise_im\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# pragma: no cover\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mKeyboardInterrupt\u001b[0m: " - ] - } - ], + "outputs": [], "source": [ "all_images = {}\n", + "im_test = {}\n", + "k_test = 3\n", + "\n", "for step in g_steps:\n", " all_images[step] = []\n", "\n", @@ -270,24 +246,53 @@ " dx = rng() - 0.5\n", " dy = rng() - 0.5\n", "\n", - " im = final.drawImage(scale=pixel_scale, offset=(dx,dy))\n", - " xsize, ysize = im.array.shape\n", - " \n", - " # Constant background\n", - " background = sky_level * pixel_scale**2\n", + " if k == 0:\n", + " im = final.drawImage(scale=pixel_scale, offset=(dx, dy))\n", + " xsize, ysize = im.array.shape\n", + " else:\n", + " im = galsim.ImageF(xsize, ysize)\n", + " final.drawImage(im, scale=pixel_scale, offset=(dx, dy))\n", + " \n", + " if k == k_test and step == (0, 0):\n", + " im_test['no_noise'] = np.copy(im.array)\n", "\n", - " im.addNoise(galsim.PoissonNoise(rng))\n", - " \n", - " all_images[step].append(im)" + " im.addNoise(galsim.PoissonNoise(rng=rng, sky_level=sky_level))\n", + "\n", + " #ccd_noise = galsim.CCDNoise(rng, sky_level=0., gain=1., read_noise=0.)\n", + " #im.addNoise(ccd_noise)\n", + " if k == k_test:\n", + " if step == (0, 0):\n", + " im_test['no_shear'] = np.copy(im.array)\n", + " if step == (1, 0):\n", + " im_test['+g1'] = np.copy(im.array)\n", + " if step == (0, 1):\n", + " im_test['-g1'] = np.copy(im.array)\n", + "\n", + " all_images[step].append(im)\n", + "print()\n", + "\n", + "im_test['+g1-g1'] = all_images[(1,0)][k_test].array - all_images[(0, 1)][k_test].array" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Downloading image meta-data\n", + "#### Plot some galaxies" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "nim = len(im_test)\n", + "fig, (axes) = plt.subplots(nrows=1, ncols=nim, figsize=(10, 5), tight_layout=True)\n", "\n", - "First, download `great3` meta-data and config files to create images." + "for ax, key in zip(axes, im_test):\n", + " ax.imshow(im_test[key])\n", + " ax.set_title(key)" ] }, { From 6058c6f3f9e52f1e38c9c94a8bb5728fe4e6e2e3 Mon Sep 17 00:00:00 2001 From: Martin Kilbinger Date: Fri, 26 Mar 2021 16:19:15 +0100 Subject: [PATCH 12/18] measuring bias: create plot --- shearbook/bias/measuring-bias.ipynb | 149 +++++++++++++++++++++++++++- 1 file changed, 145 insertions(+), 4 deletions(-) diff --git a/shearbook/bias/measuring-bias.ipynb b/shearbook/bias/measuring-bias.ipynb index 701f210..ce06bab 100644 --- a/shearbook/bias/measuring-bias.ipynb +++ b/shearbook/bias/measuring-bias.ipynb @@ -128,14 +128,14 @@ "metadata": {}, "outputs": [], "source": [ - "n_gal = 10" + "n_gal = 100" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Downloading HST COSMOS galaxy images" + "### Downloading HST COSMOS galaxy template images" ] }, { @@ -147,7 +147,7 @@ "data_dir = 'data'\n", "cat_file_name = 'real_galaxy_catalog_23.5_example.fits'\n", "\n", - "sky_level = 1e5 # ADU / arcsec^2\n", + "sky_level = 1e3 # ADU / arcsec^2\n", "gal_flux = 1e5 # arbitrary, choose large value for not too noisy images\n", "\n", "pixel_scale = 0.16 # arcsec\n", @@ -176,7 +176,17 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### PSF" + "### Create the PSF image" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "fwhm_psf = [0.6, 2.3]\n", + "flux_frac_psf = [0.8, 0.2]" ] }, { @@ -214,6 +224,13 @@ "background = sky_level * pixel_scale**2" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Create (sheared) galaxy images" + ] + }, { "cell_type": "code", "execution_count": null, @@ -295,6 +312,130 @@ " ax.set_title(key)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Shape measurement" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "e1 = {}\n", + "e2 = {}\n", + "scale = {}\n", + "sn = {}\n", + "beta = {}\n", + "q = {}\n", + "ep = {}\n", + "ex = {}\n", + "\n", + "# Initialise dictionaries as arrays\n", + "for x in [e1, e2, scale, sn, beta, q, ep, ex]:\n", + " for step in g_steps:\n", + " x[step] = []\n", + "\n", + "for k in range(n_gal):\n", + "\n", + " print(k, end=' ')\n", + " \n", + " for step in g_steps:\n", + " result = galsim.hsm.EstimateShear(all_images[step][k], psf_image, shear_est=\"KSB\", strict=False)\n", + " e1[step].append(result.corrected_g1)\n", + " e2[step].append(result.corrected_g2)\n", + " scale[step].append(result.moments_sigma)\n", + " \n", + " # TODO: compute the following parameters\n", + " sn[step].append(0)\n", + " beta[step].append(0)\n", + " q[step].append(0)\n", + " ep[step].append(0)\n", + " ex[step].append(0)\n", + "\n", + "results = {}\n", + "for step in g_steps:\n", + " results[step] = sb.gal_par.from_values(e1[step], e2[step], scale[step], sn[step],\n", + " beta[step], q[step], ep[step], ex[step])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Shear response" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "R = sb.shear_response(results, dg, output_dir='.')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "R = sb.read_R('.')\n", + "\n", + "print('mean(R)')\n", + "print(R.mean(axis=2))\n", + "\n", + "print('std(R)')\n", + "print(R.std(axis=2))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Plot shear response" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Variable on x-axis\n", + "xvar = results[(0, 0)].scale\n", + "\n", + "# Variable name = xlabel\n", + "xname = 'scale'\n", + "\n", + "# Number of x-bins\n", + "nbins = 3\n", + "\n", + "# Variables on y-axis, can be array for multiple curves in plot\n", + "yvar = [sb.shear_bias_m(R, i) for i in [0, 1]]\n", + "yvar.append(R[0,1])\n", + "\n", + "# Variable names for legend\n", + "yname = ['$m_1$', '$m_2$', '$R_{12}$']\n", + "\n", + "# Color of points and error bars\n", + "color = ['g', 'r', 'b']\n", + "\n", + "# Point types\n", + "marker = ['o', 's', 'd']\n", + "\n", + "# Create plot and save to file\n", + "x_mean, y_mean, y_std = sb.plot_mean_per_bin_several(xvar, xname, yvar, yname, nbins, error_mode='std',\n", + " color=color, lw=2, marker=marker,\n", + " out_name='{}_m1.pdf'.format(xname))\n", + "\n", + "plt.show()" + ] + }, { "cell_type": "code", "execution_count": null, From 5e286668f6f41274a01c77bf98ca9a7a1b861f19 Mon Sep 17 00:00:00 2001 From: Martin Kilbinger Date: Sat, 27 Mar 2021 18:36:07 +0100 Subject: [PATCH 13/18] env added shear_bias --- environment.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/environment.yml b/environment.yml index 3d0e635..ca98dfc 100644 --- a/environment.yml +++ b/environment.yml @@ -10,3 +10,4 @@ dependencies: - galsim - pip: - jupyter-book + - shear_bias From c8ecf0873ac3162d0ac099faea27c7689347da17 Mon Sep 17 00:00:00 2001 From: Martin Kilbinger Date: Mon, 29 Mar 2021 10:32:58 +0200 Subject: [PATCH 14/18] bias-def updated --- shearbook/bias/bias-definition.ipynb | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/shearbook/bias/bias-definition.ipynb b/shearbook/bias/bias-definition.ipynb index 473f633..88db79f 100644 --- a/shearbook/bias/bias-definition.ipynb +++ b/shearbook/bias/bias-definition.ipynb @@ -11,14 +11,14 @@ "\n", "## Population shear bias\n", "\n", - "First, we define the shear bias for a population of galaxies. First, let us recall the equation that,\n", + "First, we define the shear bias for a population of galaxies. Let us recall the equation that,\n", "in the weak-lensing regime, relates observed and intrincic ellipticity to reduced shear,\n", "\n", - "$$\\varepsilon^{\\textrm{obs}}_\\alpha = \\varepsilon^{\\textrm{I}}_\\alpha + g\\alpha,$$\n", + "$$\\varepsilon^{\\textrm{obs}}_\\alpha = \\varepsilon^{\\textrm{I}}_\\alpha + g_\\alpha,$$\n", "\n", "for components $\\alpha = 1, 2$. If the mean intrinsic ellipticity is zero, $\\langle \\varepsilon^{\\textrm{I}}_\\alpha \\rangle = 0$, the observed ellipticity is an unbiased estimator of the shear, $\\langle \\varepsilon^{\\textrm{obs}}_\\alpha \\rangle = g_\\alpha$ in this basic case.\n", "\n", - "Next, we now introduce the ensemble-average additive bias $c_\\alpha$, and multiplicative bias $m_\\alpha$, and write\n", + "Next, we introduce the ensemble-average additive bias $c_\\alpha$, and multiplicative bias $m_\\alpha$, and write\n", "\n", "$$\n", "\\langle \\varepsilon^{\\textrm{obs}}_\\alpha \\rangle \\equiv g^{\\textrm{obs}} = c_\\alpha + (1 + m_\\alpha) g_\\alpha.\n", @@ -28,18 +28,27 @@ "\n", "## Individual galaxy shear bias\n", "\n", - "We see that the multiplicative bias in {eq}`shear-estim-biased` can be interpreted as derivative, or response, of the average observed ellipticity with respect to the shear. Accounting for the two-component nature of ellipticity, and written for an individual galaxy, we define the response matrix $R$ ({cite}`2017arXiv170202600H`, {cite}`SH17`) by\n", + "We now define the shear bias for an individual galaxy, and show that the ensemble average of this individual bias\n", + "can be identified with the population shear bias defined above.\n", "\n", - "$$R_{\\alpha\\beta} = \\frac{\\partial \\varepsilon^{\\textrm{obs}}_\\alpha}{\\partial g_\\beta} .$$\n", + "The multiplicative bias in {eq}`shear-estim-biased` can be interpreted as derivative, or response, of the average observed ellipticity with respect to the shear. Instead of using the average ellipticity however, we instead compute this derivative of the ellipticity of an individual galaxy. In addition, we generalise this derivative and \n", + "account for the two-component nature of ellipticity. This defines the response matrix $R$ ({cite}`2017arXiv170202600H`, {cite}`SH17`) by\n", "\n", - "The additive shear bias for an individual galaxy can be defined as well, in the case of simulations where the intrinsic ellipticity is known,\n", + "$$\n", + "R_{\\alpha\\beta} = \\frac{\\partial \\varepsilon^{\\textrm{obs}}_\\alpha}{\\partial g_\\beta} .\n", + "$$ (R)\n", + "\n", + "The additive shear bias for an individual galaxy can be defined as well,\n", "\n", "$$a_\\alpha = \\varepsilon^{\\textrm{obs}}_\\alpha - R_{\\alpha\\alpha} g_\\alpha - \\varepsilon^{\\textrm{I}}_\\alpha .$$\n", "\n", + "This quantity can be computed in the case of image simulations where the intrinsic ellipticity $varepsilon^{\\textrm{I}}$ is known.\n", "\n", + "By applying the ensemble average to {eq}`R` and comparing to {eq}`shear-estim-biased`, we find the equalities\n", + "$\\langle R_{\\alpha\\alpha} \\rangle = m_\\alpha$, and $\\langle a_\\alpha \\rangle = c_\\alpha$.\n", "\n", - "In the case of individually measured shear bias, one can set up a calibration scheme by building the population shear bias quantities $\\langle R \\rangle$ and $\\langle \\vec a \\rangle$. These are then applied to the observed galaxy ellipticities as\n", - "$\\langle R \\rangle^{-1} \\left( \\vec \\varepsilon^{\\textrm{obs}} - \\langle \\vec a \\rangle \\right)$. " + "With calibration schemes where individual shear bias are measured, for example using metacalibration ({cite}`2017arXiv170202600H`), or deep learning ({cite}`pujol_shear_bias_20`), one applies the ensemble-averaged response matrix and additive shear vector o the observed galaxy ellipticities as\n", + "$\\langle R \\rangle^{-1} \\left( \\vec \\varepsilon^{\\textrm{obs}} - \\langle \\vec a \\rangle \\right)$." ] } ], @@ -59,7 +68,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.0" + "version": "3.9.2" } }, "nbformat": 4, From 7f3a2dfec0b989875b4b6d1a4da689c00b7a2c9c Mon Sep 17 00:00:00 2001 From: Martin Kilbinger Date: Mon, 29 Mar 2021 10:33:30 +0200 Subject: [PATCH 15/18] bias refs --- shearbook/_bibliography/z_bias.bib | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/shearbook/_bibliography/z_bias.bib b/shearbook/_bibliography/z_bias.bib index 2481b3a..8123c7c 100644 --- a/shearbook/_bibliography/z_bias.bib +++ b/shearbook/_bibliography/z_bias.bib @@ -195,3 +195,22 @@ @article{Barbary2016 title = {SEP: Source Extractor as a library}, journal = {Journal of Open Source Software} } + +@ARTICLE{pujol_shear_bias_20, + author = {{Pujol}, Arnau and {Bobin}, Jerome and {Sureau}, Florent and {Guinot}, Axel and {Kilbinger}, Martin}, + title = "{Shear measurement bias. II. A fast machine-learning calibration method}", + journal = {\aap}, + keywords = {gravitational lensing: weak, methods: numerical, methods: data analysis, methods: observational, methods: statistical, cosmology: observations, Astrophysics - Cosmology and Nongalactic Astrophysics, Astrophysics - Instrumentation and Methods for Astrophysics}, + year = 2020, + month = nov, + volume = {643}, + eid = {A158}, + pages = {A158}, + doi = {10.1051/0004-6361/202038658}, +archivePrefix = {arXiv}, + eprint = {2006.07011}, + primaryClass = {astro-ph.CO}, + adsurl = {https://ui.adsabs.harvard.edu/abs/2020A&A...643A.158P}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + From 5a43d5595af49db22e595e60da73721d7b995e7f Mon Sep 17 00:00:00 2001 From: Martin Kilbinger Date: Mon, 29 Mar 2021 18:28:58 +0200 Subject: [PATCH 16/18] bias: test of mean and std of dg --- shearbook/bias/measuring-bias.ipynb | 33 +++++++++++++++++++---------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/shearbook/bias/measuring-bias.ipynb b/shearbook/bias/measuring-bias.ipynb index ce06bab..9085705 100644 --- a/shearbook/bias/measuring-bias.ipynb +++ b/shearbook/bias/measuring-bias.ipynb @@ -78,7 +78,7 @@ "metadata": {}, "outputs": [], "source": [ - "dg = 0.02" + "dg = 0.15" ] }, { @@ -283,12 +283,13 @@ " if step == (1, 0):\n", " im_test['+g1'] = np.copy(im.array)\n", " if step == (0, 1):\n", - " im_test['-g1'] = np.copy(im.array)\n", + " im_test['+g2'] = np.copy(im.array)\n", "\n", " all_images[step].append(im)\n", "print()\n", "\n", - "im_test['+g1-g1'] = all_images[(1,0)][k_test].array - all_images[(0, 1)][k_test].array" + "im_test['+g1-(-g1)'] = all_images[(1,0)][k_test].array - all_images[(-1, 0)][k_test].array\n", + "im_test['+g2-0'] = all_images[(0,1)][k_test].array - all_images[(0, 0)][k_test].array" ] }, { @@ -305,7 +306,7 @@ "outputs": [], "source": [ "nim = len(im_test)\n", - "fig, (axes) = plt.subplots(nrows=1, ncols=nim, figsize=(10, 5), tight_layout=True)\n", + "fig, (axes) = plt.subplots(nrows=1, ncols=nim, figsize=(20, 10), tight_layout=True)\n", "\n", "for ax, key in zip(axes, im_test):\n", " ax.imshow(im_test[key])\n", @@ -362,6 +363,23 @@ " beta[step], q[step], ep[step], ex[step])" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "m = np.zeros(2)\n", + "s = np.zeros(2)\n", + "print('{:6s}: {:5s} +- {:3s}'.format('dg', 'mean', 'std'))\n", + "for step in g_steps:\n", + " m[0], s[0] = np.mean(e1[step]), np.std(e1[step])\n", + " m[1], s[1] = np.mean(e2[step]), np.std(e2[step])\n", + " for i in (0, 1):\n", + " print('{: 6.3g}: {: .3f} +- {: .3f}'.format(dg * step[i], m[i], s[i]))\n", + " print()" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -435,13 +453,6 @@ "\n", "plt.show()" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { From e4e5892ce192f28531cde7bc1a647bcc1876632f Mon Sep 17 00:00:00 2001 From: Martin Kilbinger Date: Tue, 30 Mar 2021 09:38:11 +0200 Subject: [PATCH 17/18] Update shearbook/bias/bias-definition.ipynb Co-authored-by: thuiop --- shearbook/bias/bias-definition.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shearbook/bias/bias-definition.ipynb b/shearbook/bias/bias-definition.ipynb index 88db79f..968e36f 100644 --- a/shearbook/bias/bias-definition.ipynb +++ b/shearbook/bias/bias-definition.ipynb @@ -42,7 +42,7 @@ "\n", "$$a_\\alpha = \\varepsilon^{\\textrm{obs}}_\\alpha - R_{\\alpha\\alpha} g_\\alpha - \\varepsilon^{\\textrm{I}}_\\alpha .$$\n", "\n", - "This quantity can be computed in the case of image simulations where the intrinsic ellipticity $varepsilon^{\\textrm{I}}$ is known.\n", + "This quantity can be computed in the case of image simulations where the intrinsic ellipticity $\varepsilon^{\\textrm{I}}$ is known.\n", "\n", "By applying the ensemble average to {eq}`R` and comparing to {eq}`shear-estim-biased`, we find the equalities\n", "$\\langle R_{\\alpha\\alpha} \\rangle = m_\\alpha$, and $\\langle a_\\alpha \\rangle = c_\\alpha$.\n", From fd7126aa4829b4b7011e2ea1455d9ceb1bf09b00 Mon Sep 17 00:00:00 2001 From: Martin Kilbinger Date: Tue, 30 Mar 2021 09:38:22 +0200 Subject: [PATCH 18/18] Update shearbook/_bibliography/z_bias.bib Co-authored-by: thuiop --- shearbook/_bibliography/z_bias.bib | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/shearbook/_bibliography/z_bias.bib b/shearbook/_bibliography/z_bias.bib index 8123c7c..faf4631 100644 --- a/shearbook/_bibliography/z_bias.bib +++ b/shearbook/_bibliography/z_bias.bib @@ -79,7 +79,7 @@ @ARTICLE{SH17 doi = {10.3847/1538-4357/aa704b}, adsurl = {http://adsabs.harvard.edu/abs/2017ApJ...841...24S}, adsnote = {Provided by the SAO/NASA Astrophysics Data System} - +} @ARTICLE{Bosch2018, author = {{Bosch}, James and {Armstrong}, Robert and {Bickerton}, Steven and {Furusawa}, Hisanori and {Ikeda}, Hiroyuki and {Koike}, Michitaro and {Lupton}, Robert and {Mineo}, Sogo and {Price}, Paul and {Takata}, Tadafumi and {Tanaka}, Masayuki and {Yasuda}, Naoki and {AlSayyad}, Yusra and {Becker}, Andrew C. and {Coulton}, William and {Coupon}, Jean and {Garmilla}, Jose and {Huang}, Song and {Krughoff}, K. Simon and {Lang}, Dustin and {Leauthaud}, Alexie and {Lim}, Kian-Tat and {Lust}, Nate B. and {MacArthur}, Lauren A. and {Mandelbaum}, Rachel and {Miyatake}, Hironao and {Miyazaki}, Satoshi and {Murata}, Ryoma and {More}, Surhud and {Okura}, Yuki and {Owen}, Russell and {Swinbank}, John D. and {Strauss}, Michael A. and {Yamada}, Yoshihiko and {Yamanoi}, Hitomi}, title = "{The Hyper Suprime-Cam software pipeline}", @@ -213,4 +213,3 @@ @ARTICLE{pujol_shear_bias_20 adsurl = {https://ui.adsabs.harvard.edu/abs/2020A&A...643A.158P}, adsnote = {Provided by the SAO/NASA Astrophysics Data System} } -