{ "cells": [ { "cell_type": "markdown", "id": "44ef49d8", "metadata": {}, "source": [ "# Calibration and error propagation\n", "\n", "Previously, we compared `phoptic` to [`Source-Extractor`](https://sextractor.readthedocs.io/en/latest/index.html) and showed that the two programs produce [very similar results](sextractor_comparison.ipynb). However, we neglected image calibrations in that example to keep things simple.\n", "\n", "When performing image calibrations, `phoptic` will automatically propagate the errors; in this notebook, I will prove that `phoptic` does this correctly. Additionally, I will also verify that calibrations applied by `phoptic` are correct. \n", "\n", "## Generating data\n", "\n", "First, we need to generate some data that contain only noise. We will include typical values for the bias and dark current, and simulate long exposures to allow for a significant dark noise contribution. We will also generate flat-field images, which introduce some additional noise. These images will be generated as if they were produced by OPTICAM-MX, but we'll only simulate data from a single camera for simplicity.\n", "\n", "Let's define the properties of these generated data: " ] }, { "cell_type": "code", "execution_count": 1, "id": "fdc354ea", "metadata": { "execution": { "iopub.execute_input": "2026-06-16T12:21:13.031913Z", "iopub.status.busy": "2026-06-16T12:21:13.031832Z", "iopub.status.idle": "2026-06-16T12:21:13.034260Z", "shell.execute_reply": "2026-06-16T12:21:13.033874Z" } }, "outputs": [], "source": [ "X, Y = 512, 512 # image dimensions\n", "\n", "gain = 1. # gain of OPTICAM-MX\n", "read_noise = 1.1 # nominal read noise of OPTICAM-MX\n", "dark_curr = 0.1 # nominal dark current of OPTICAM-MX\n", "texp = 30. # exposure time of science images (in seconds)\n", "flat_texp = 3. # exposure time of flat-field images (in seconds)\n", "fltr = 'g' # image filter\n", "\n", "binning = f'{2048 // X}x{2048 // Y}' # binning mode\n", "\n", "bias_value = 400. # typical bias according to Handbook of CCD Astronomy, Howell\n", "\n", "flat_level = 40000. # mean flux of flats\n", "\n", "# dark noise contributions\n", "dark_signal = dark_curr * texp\n", "flat_dark_signal = dark_curr * flat_texp\n", "\n", "n_calibration_images = 30 # number of images to generate for each calibration step\n", "\n", "science_flux = 1000. # mean flux in science images (before noise)" ] }, { "cell_type": "markdown", "id": "e1eec252", "metadata": {}, "source": [ "We can compute the analytical variances for each calibration:" ] }, { "cell_type": "code", "execution_count": 2, "id": "ecb6071d", "metadata": { "execution": { "iopub.execute_input": "2026-06-16T12:21:13.035158Z", "iopub.status.busy": "2026-06-16T12:21:13.035082Z", "iopub.status.idle": "2026-06-16T12:21:13.080750Z", "shell.execute_reply": "2026-06-16T12:21:13.080201Z" } }, "outputs": [], "source": [ "import numpy as np\n", "\n", "true_bias_var = read_noise**2 / n_calibration_images\n", "\n", "true_dark_var = np.pi / (2 * n_calibration_images) * (dark_signal + read_noise**2 + true_bias_var)\n", "\n", "true_flat_var = np.pi / (2 * n_calibration_images) * (flat_level + read_noise**2 + true_bias_var + flat_dark_signal)\n", "effective_flat_var = true_flat_var * science_flux**2 / flat_level**2" ] }, { "cell_type": "markdown", "id": "70542fa0", "metadata": {}, "source": [ "I have included $\\frac{\\pi}{2 N}$ factors in the dark and flat variances since `phoptic` computes master darks and master flats using the median (to remove outliers due to, e.g., cosmic rays), while the master bias is computed using the mean. Note that the dark and flat-field variances include a bias contribution, and the flat-field variance further includes a (small) dark-noise contribution. The flat variance also has an additional term because it acts multiplicatively, whereas the bias and dark corrections are additive.\n", "\n", "Now let's create the directories for storing our simulated data:" ] }, { "cell_type": "code", "execution_count": 3, "id": "af934725", "metadata": { "execution": { "iopub.execute_input": "2026-06-16T12:21:13.086758Z", "iopub.status.busy": "2026-06-16T12:21:13.086627Z", "iopub.status.idle": "2026-06-16T12:21:13.088936Z", "shell.execute_reply": "2026-06-16T12:21:13.088719Z" } }, "outputs": [], "source": [ "from pathlib import Path\n", "\n", "out_dir = Path('out')\n", "\n", "if not out_dir.joinpath('biases').is_dir():\n", " out_dir.joinpath('biases').mkdir(parents=True)\n", "\n", "if not out_dir.joinpath('darks').is_dir():\n", " out_dir.joinpath('darks').mkdir(parents=True)\n", "\n", "if not out_dir.joinpath('flats').is_dir():\n", " out_dir.joinpath('flats').mkdir(parents=True)\n", "\n", "if not out_dir.joinpath('flat_darks').is_dir():\n", " out_dir.joinpath('flat_darks').mkdir(parents=True)" ] }, { "cell_type": "markdown", "id": "97a40484", "metadata": {}, "source": [ "We'll use `numpy`'s `default_rng()` Generator to generate random noise. For reproducibility, I'll pass a fixed seed:" ] }, { "cell_type": "code", "execution_count": 4, "id": "33f7ef94", "metadata": { "execution": { "iopub.execute_input": "2026-06-16T12:21:13.089710Z", "iopub.status.busy": "2026-06-16T12:21:13.089627Z", "iopub.status.idle": "2026-06-16T12:21:13.099811Z", "shell.execute_reply": "2026-06-16T12:21:13.099455Z" } }, "outputs": [], "source": [ "rng = np.random.default_rng(42)" ] }, { "cell_type": "markdown", "id": "8aeb4644", "metadata": {}, "source": [ "Let's now generate our calibration images.\n", "\n", "### Biases\n", "\n", "Bias images should be taken with the telescope shutter closed and have an exposure time of 0.0 seconds:" ] }, { "cell_type": "code", "execution_count": 5, "id": "b5186356", "metadata": { "execution": { "iopub.execute_input": "2026-06-16T12:21:13.100596Z", "iopub.status.busy": "2026-06-16T12:21:13.100494Z", "iopub.status.idle": "2026-06-16T12:21:16.972234Z", "shell.execute_reply": "2026-06-16T12:21:16.971821Z" } }, "outputs": [], "source": [ "from astropy.io import fits\n", "\n", "for i in range(n_calibration_images):\n", " bias = rng.normal(bias_value, read_noise, size=(X, Y))\n", " \n", " hdu = fits.PrimaryHDU(bias)\n", " \n", " hdu.header['EXPOSURE'] = 0.0\n", " hdu.header[\"BINNING\"] = binning\n", " hdu.header[\"GAIN\"] = gain\n", " hdu.header['FILTER'] = fltr\n", " hdu.header['INSTRUME'] = 'OPTICAM'\n", " \n", " path = out_dir / 'biases' / f'image_{i}.fits.gz'\n", " if not path.is_file():\n", " hdu.writeto(path, overwrite=True)" ] }, { "cell_type": "markdown", "id": "6a7bbb48", "metadata": {}, "source": [ "Let's pass these bias images to a `BiasCorrector`: " ] }, { "cell_type": "code", "execution_count": 6, "id": "08f054a1", "metadata": { "execution": { "iopub.execute_input": "2026-06-16T12:21:16.973803Z", "iopub.status.busy": "2026-06-16T12:21:16.973681Z", "iopub.status.idle": "2026-06-16T12:21:19.359852Z", "shell.execute_reply": "2026-06-16T12:21:19.359525Z" } }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "\r", "[OPTICAM] Scanning data directory: 0%| |[00:00" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from matplotlib import pyplot as plt\n", "\n", "fig, axes = plt.subplots(\n", " nrows=2,\n", " ncols=2,\n", " figsize=(12.8, 12.8),\n", " )\n", "\n", "axes[0, 0].set_title('Bias')\n", "axes[0, 0].hist(np.mean(bias_vars / true_bias_var, axis=0).flatten(),\n", " bins=100,\n", " histtype='step',\n", " color='k',\n", " )\n", "\n", "axes[0, 1].set_title('Dark')\n", "axes[0, 1].hist(np.mean(dark_vars / true_dark_var, axis=0).flatten(),\n", " bins=100,\n", " histtype='step',\n", " color='k',\n", " )\n", "\n", "axes[1, 0].set_title('Flat')\n", "axes[1, 0].hist(np.mean(flat_vars / effective_flat_var, axis=0).flatten(),\n", " bins=100,\n", " histtype='step',\n", " color='k',\n", " )\n", "\n", "axes[1, 1].set_title('Total')\n", "axes[1, 1].hist((empirical_err / measured_err).flatten(),\n", " bins=100,\n", " histtype='step',\n", " color='k',\n", " )\n", "\n", "for ax in axes.flatten():\n", " ax.axvline(1, c='r', ls='-')\n", " ax.set_xlabel('Measured / expected')\n", "\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "5ba1508f", "metadata": {}, "source": [ "Clearly, `phoptic` is correctly propagating the errors from each calibration. However, this doesn't prove that the calibrations have actually been applied correctly. To verify this, we can check that the mean flux of the calibrated images is close to the expected value:" ] }, { "cell_type": "code", "execution_count": 21, "id": "c591828b", "metadata": { "execution": { "iopub.execute_input": "2026-06-16T12:21:38.254370Z", "iopub.status.busy": "2026-06-16T12:21:38.254295Z", "iopub.status.idle": "2026-06-16T12:21:38.272036Z", "shell.execute_reply": "2026-06-16T12:21:38.271609Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0.9999779237287508\n" ] } ], "source": [ "print(np.mean(calibrated_images / science_flux))" ] }, { "cell_type": "code", "execution_count": 22, "id": "f52323de", "metadata": { "execution": { "iopub.execute_input": "2026-06-16T12:21:38.272829Z", "iopub.status.busy": "2026-06-16T12:21:38.272755Z", "iopub.status.idle": "2026-06-16T12:21:38.288975Z", "shell.execute_reply": "2026-06-16T12:21:38.288645Z" }, "tags": [ "remove-cell" ] }, "outputs": [], "source": [ "assert np.isclose(np.mean(calibrated_images / science_flux), 1., rtol=0.01, atol=0)" ] }, { "cell_type": "markdown", "id": "03ec43d8", "metadata": {}, "source": [ "Another reassuring result! Comparing the calibrated fluxes graphically:" ] }, { "cell_type": "code", "execution_count": 23, "id": "d75e9d7a", "metadata": { "execution": { "iopub.execute_input": "2026-06-16T12:21:38.290158Z", "iopub.status.busy": "2026-06-16T12:21:38.290087Z", "iopub.status.idle": "2026-06-16T12:21:38.460570Z", "shell.execute_reply": "2026-06-16T12:21:38.460209Z" } }, "outputs": [ { "data": { "image/png": "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dcHHICdVqlTR6NGjNWbMGKWlpalRo0ZatmyZ/vKXv+irr74K2Hbs2LFauHChvvnmG+sM0eTJk3X11Vfrjjvu0Pr16xUeHl6g9QHFVYk6M7Jz506FhIRo7Nix1tdrr72myMhIRURESJIVNnbt2qXffvtNa9eu1bZt27Rw4UK9++67dpYPSDp16eLll1+W1+vVsGHDJEkpKSnq0KGDFi9erB07dliXC5o1a6ZffvlFgwcPVlpamq655hotX75cN9xwg0aOHGldUslp6vec2urUqaORI0eqR48emjNnjpxOpypUqKCPPvpI//3vf+VwOM7Z9vTYjTMFW1dISIji4uL01FNP6ejRo+fMM3L//fcrPj5e999/v7KyslSmTBkNGDBAHo8nYMbUDz/8UEuWLFG9evUkSe+//77uvPNOjRw5UldfffV5j/kjjzyiG2+8MaCtRo0aGjly5DmXmp5//nktXrxYderU0WWXXaYpU6aod+/eGjlypKKjoyVJqampOnLkiD7++GPdeuutVt9KlSrp66+/Vq9evfT999+fty6gtAgxFzrCzUaLFi1Sr169zrmb5p133tHYsWMDTi+fbc+ePWrUqJGmTp2q+++/32rv37+/jDGaO3duYZYOAADOUqLOjHTt2lVHjhwJuJ4ryZpdUZIaNmyo5s2bKz4+PmCb+Pj4C7oFEgAAXJxiNWZk586dio+Pt5b0XrlypSpXrqzWrVurevXqatu2rUaOHGmNWm/VqpX27Nmj6dOn67333tN1110n6dQZlEGDBiksLEzNmzfX7NmztXPnTv373/+28d0BAFA6FavLNJ988om+/PLLc9pffPFFK2hI0ty5c/XNN98oOTlZTZo00YMPPqiWLVsG9Pn+++81efJkJSUlqUmTJnrssceYFREAABsUqzACAABKnhI1ZgQAABQ/hBEAAGCrYjGANTs7WwkJCQoPD1dISIjd5QAAgCAYY3Ts2DHVrl07z+UfikUYSUhIsCYzAgAAxcu+fftUt27dXJ8vFmHk9JTJ+/btO2cFXgAlUFqa9H+zzCohQQpicT4ARU9qaqrq1at33qUPikUYOX1ppnLlyoQRoDQ4c5XeypUJI0Axd74hFgxgBQAAtiKMAAAAWxFGAACArQgjAADAVoQRAABgK8IIAACwFWEEAADYijACAABsRRgBAAC2IowAAABbEUYAAICtCCMAAMBWhBEAAGCrYrFqL4DSZd++far3f//evHmzssPC5HQ65XK5bK0LQOEgjACwndfrlc/nkyQlJiYq5tZblfh/z3W+9lr5JTkcDnk8HgIJUAIRRgDYyuv1yu12y+/3W23OsDDr39+vXq0de/cqJiZGPp+PMAKUQIQRALby+Xzy+/2KjY2V2+2WJFVzOKT/+3ebNm2U/X/hxOPxWP24bAOUHIQRAEWC2+1WVFTUqQdpaQHPOZ1OORwOxcTEWG1ctgFKDsIIgCLP5XLJ4/FY40o8Hg+XbYAShDACoFhwuVwED6CEIowAuKTOvHNGChwHAqB0IowAuGRyunNGOjX+w+l02lQVALsRRgBcMjndOSNxZwxQ2hFGAFxyAXfOXISzL/EQaoDiiTACoNjJ6VZfidt9geKKMAKg2Dn7Vl+J232B4owwAqBY4lZfoOQoY3cBAACgdCOMAAAAW3GZBkChOnOSMyY4A5ATwgiAQpPTJGdMcAbgbIQRAIUmp0nOmAsEwNkIIwAKXUFNcgagZGIAKwAAsBVnRgCUKGcOkuWSEFA8EEYAlAg5TRHP9PBA8UAYAVAinD1FPNPDA8UHYQRAicEU8UDxxABWAABgK8IIAACwFWEEAADYijACAABsxQBWAAXmzEXxJBbGAxAcwgiAApHTongSC+MBOD/CCIACkdOieBKzoAI4P8IIgALFongA8osBrAAAwFacGQFQop09iJbLRkDRQxgBUCLltHCexOJ5QFFEGAFQIp29cJ7E4nlAUXVBYSQ1NVW//vqrGjVqpKpVqwbVJzk5Wfv371f9+vVVuXLlC3lZAMgXFs4Diod8DWDdvXu3hg0bpmbNmunqq6/WwoULz9vHGKORI0eqVq1a6t+/v6pXr65XXnnlggsGAAAlS77CyKZNm9S2bVvt3Lkz6D6TJk3S1KlTtX79eu3evVtxcXF6+eWXNW/evHwXCwAASp58Xaa5/fbb8/0CkyZN0u23365WrVpJkrp06aKuXbtq8uTJ6tu3b773BwAASpZCnWckKytLW7Zs0dVXXx3Q3qFDB23cuLEwXxoAABQThXo3zbFjx5SZmanIyMiAdqfTqaSkpFz7ZWRkKCMjw3qcmppaaDUCAAB7FeqZkXLlyklSQLCQpPT0dOu5nIwbN04RERHWV7169QqzTAAAYKNCDSMVK1ZU1apVlZCQENCekJCQ5+12o0ePVkpKivW1b9++wiwTAADYqMDDSEJCgjZv3mw97tatmxYsWGA9zs7O1sKFC9WtW7dc9xEaGqrKlSsHfAEoerxerzZu3KiNGzeeM+06AAQrX2NGjh07pl9++cV6vGfPHq1fv17VqlVT/fr1JUmffPKJxo8fr6NHj0qSxowZo44dO2rEiBG65ZZbNH36dCUnJ+vJJ58suHcB4JLzer1yu93y+/1Wm8PhkNPptLEqAMVRvs6MeDwePfzww3r44YcVHR2tuXPn6uGHH9aMGTOsbWrXrq22bdtaj1u3bq1Vq1YpMTFRf//731W2bFmtWbOGWRGBYs7n88nv9ys2NlYbNmzQhg0bWPMFwAXJ15mR9u3ba/369XluM3ToUA0dOjSgLTo6Wp9//nn+qwNQ5LndbkVFRdldBoBirFAHsAIAAJwPYQQAANiKMAIAAGxVqDOwAkBRdOZtyE6nk0G3gM0IIwBKDafTKYfDoZiYGKvN4XBwFxBgM8IIgFLD5XLJ4/HI5/NJOnWGJCYmRj6fjzAC2IgwAqBUcblcBA+giGEAKwAAsBVhBAAA2IowAgAAbEUYAQAAtiKMAAAAWxFGAACArQgjAADAVoQRAABgKyY9AxAUr9drzVwqBa7vAgAXgzAC4Ly8Xq/cbrf8fn9Au8PhkNPptKkqACUFYQTAefl8Pvn9fsXGxsrtdlvtrHgLoCAQRgAEze12Kyoqyu4yAJQwDGAFAAC2IowAAABbcZkGQKl39p1BjIUBLi3CCIBSy+l0yuFwKCYmJqDd4XDI4/EQSIBLhDACoNRyuVzyeDznzJ8SExMjn89HGAEuEcIIgFLN5XIROgCbMYAVAADYijACAABsRRgBAAC2IowAAABbEUYAAICtCCMAAMBWhBEAAGArwggAALAVYQQAANiKMAIAAGxFGAEAALZibRoAOfJ6vdYCch6Px+ZqAJRkhBEA5/B6vXK73fL7/Vabw+GQ0+m0sSoAJRVhBMA5fD6f/H6/YmNj5Xa7JUlOp5PVbQEUCsIIgFy53W5FRUXZXQaAEo4BrAAAwFaEEQAAYCvCCAAAsBVjRgAgB2fezszgXaBwEUYA4AxOp1MOh0MxMTFWm8PhkMfjIZAAhYQwAgBncLlc8ng8ARO+xcTEyOfzEUaAQkIYAYCzuFwuggdwCTGAFQAA2IowAgAAbEUYAQAAtiKMAAAAW+U7jEyfPl1NmzZVaGioWrZsqfnz5+e5fXp6ukaNGiWXy6UKFSqoUaNGeuGFF5SVlXXBRQMAgJIjX2Fk0aJFGjx4sMaMGaODBw/qwQcf1IABA7Rhw4Zc+zz77LP66quvNHv2bB09elSffvqpJkyYoLfeeuuiiwcAAMVfvsLI22+/rVtuuUX33nuvLr/8cj3xxBNq06aNxo8fn2ufjRs36qabblJ0dLQqVKigG264Qddee22eAQYAAJQeQYcRY4zWrVunG264IaC9W7duWrNmTa79/vznPysuLk6bNm1SRkaGVq5cqXXr1mnQoEEXXDQAACg5gp707NixY0pLS1O1atUC2qtXr66DBw/m2u/RRx/V7t27FRUVJUkqU6aMXnvtNQ0YMCDXPhkZGcrIyLAep6amBlsmAAAoZi76bhpjjEJCQnJ9/plnntHXX3+tdevWKS0tTd99953efPNNTZgwIdc+48aNU0REhPVVr169iy0TAAAUUUGHkfDwcFWsWFGJiYkB7YmJiapRo0aOfbKzszVx4kQ98cQT6tChgxwOh7p27aqhQ4fq3XffzfW1Ro8erZSUFOtr3759wZYJAACKmaAv04SEhKhjx45avny5RowYYbUvW7ZM11xzTa59ypYte86ZE2OMypYtm+trhYaGKjQ0NNjSAFwkr9drLQwnnVocDgAulXwtlPfXv/5Vt9xyi6ZPn65bbrlF//rXv7Rp0yZ9+OGH1jZ///vfNX78eB09elQhISHq37+/3nnnHXXo0EGtW7fWunXrNGnSJA0ePLjA3wyA/PN6vXK73fL7/QHtDodDTqfTpqoAlCb5CiM33XSTpkyZoldeeUUPPfSQmjRpoq+//lrR0dG59vnggw/0t7/9TQMHDtShQ4dUp04dDR8+XGPGjLno4gFcPJ/PJ7/fr9jYWLndbqvd6XSyci2ASyLEGGPsLuJ8UlNTFRERoZSUFFWuXNnucoASZePGjYqOjtaGDRusu95sl5YmVap06t9//CFVrGhbKUXy+ADFRLC/v1mbBgAA2IowAgAAbEUYAQAAtsrXAFYAKK3Ovt2ZAb5AwSGMAEAenE6nHA6HYmJiAtodDoc8Hg+BBCgAhBEAyIPL5ZLH4zlnUriYmBj5fD7CCFAACCMAcB4ul4vQARQiBrACAABbEUYAAICtCCMAAMBWhBEAAGArwggAALAVYQQAANiKMAIAAGxFGAEAALYijAAAAFsRRgAAgK0IIwAAwFaEEQAAYCsWygNKIa/Xa61C6/F4bK4GQGlHGAFKGa/XK7fbLb/fb7U5HA45nU4bqwJQmhFGgFLG5/PJ7/crNjZWbrdbkuR0OuVyuWyuDEBpRRgBSim3262oqCi7ywAABrACAAB7EUYAAICtCCMAAMBWjBkBgAt05m3RDAIGLhxhBADyyel0yuFwKCYmxmpzOBzyeDwEEuACEEYAIJ9cLpc8Hk/AxHExMTHy+XyEEeACEEYA4AK4XC6CB1BAGMAKAABsRRgBAAC2IowAAABbEUYAAICtCCMAAMBWhBEAAGArwggAALAVYQQAANiKMAIAAGxFGAEAALYijAAAAFsRRgAAgK0IIwAAwFas2guUcF6v11rqXjq13D0AFCWEEaAE83q9crvd8vv9Ae0Oh0NOp9OmqgAgEGEEKMF8Pp/8fr9iY2PldrutdqfTKZfLZWNlAPD/EUaAUsDtdisqKsruMgAgRwxgBQAAtiKMAAAAW3GZBgAKyNl3KjE2BwgOYQQALpLT6ZTD4VBMTExAu8PhkMfjIZAA55HvyzQ//PCD7rzzTnXo0EExMTFBzVmQkZGhf/zjH+revbtuvPFGTZ069YKKBYCiyOVyyePxaMOGDdZXbGys/H5/wBwvAHKWrzMjW7ZsUZcuXfTII4/okUce0bRp09S5c2dt2rRJ9evXz7HPiRMn1L17d6WkpOjll19WtWrVNH36dIWHh+v2228vkDcBAHZzuVycAQEuUL7CyKuvvqoOHTroH//4hySpS5cuat68ud555x1NmDAhxz4TJkzQli1btHPnTtWoUUOS1LlzZ504ceIiSwcAACVBvi7TLFu2TL179/7/ncuUUe/evbV06dJc+3z22Wfq37+/FUROK1++fD5LBQAAJVHQYSQtLU3JycmqVatWQHvt2rW1b9++XPt5PB5deeWVeu6559SpUyf169dPM2bMkDEm1z4ZGRlKTU0N+AIAACVT0GEkMzNTkhQaGhrQHhoaaj13NmOMTpw4obFjx0qS3n77bfXp00ePPvqoXn311Vxfa9y4cYqIiLC+6tWrF2yZAACgmAk6jISHh6t8+fJKSkoKaE9KSlJkZGSOfUJCQlS1alVFRUXptddeU+fOnfXQQw/piSee0Pvvv5/ra40ePVopKSnWV15nXgAAQPEWdBgpW7asrrrqKv3www8B7WvXrlV0dHSu/a6++mpVr149oK169eo6duxYrpdqQkNDVbly5YAvAABQMuVrAOvQoUM1a9Ysbd68WdKpAa3Lly/XQw89ZG3z0Ucf6brrrrMeP/LII1q0aJF++uknSdLRo0f16aefqkePHgoJCSmAtwAAAIqzfN3aO3jwYHk8HnXo0EE1a9bU4cOH9corr6hPnz7WNgcPHtS2bdusx3379tWYMWPUsWNH1axZUwkJCbr++uv18ccfF9y7AAAAxVa+wkhISIjeeecdvfDCCzpw4IDq1aunSpUqBWzz8MMP67bbbgtoe/rpp/WXv/xFXq9XtWrVUkRExMVXDgAASoQLWpumSpUqqlKlSo7P1axZUzVr1jyn3eFwqHnz5hfycgAAoATL99o0AAAABYkwAgAAbHVBl2kAFF1er9daKTaYVbUBwG6EEaAE8Xq9crvd8vv9VpvD4ZDT6bSxKgDIG2EEKEF8Pp/8fr9iY2PldrslSU6nk6XtARRphBGgBHK73YqKirK7DAAICgNYAQCArQgjAADAVlymAYBCdOYdTYzfAXJGGAGAQuB0OuVwOBQTE2O1ORwOeTweAglwFsIIABQCl8slj8cTMOdLTEyMfD4fYQQ4C2EEAAqJy+UieABBYAArAACwFWEEAADYijACAABsRRgBAAC2IowAAABbEUYAAICtCCMAAMBWhBEAAGArwggAALAVYQQAANiKMAIAAGxFGAEAALYijAAAAFuxai9QjHm9XmuJeunUMvUAUNwQRoBiyuv1yu12y+/3B7Q7HA45nU6bqgKA/COMAMWUz+eT3+9XbGys3G631e50OuVyuWysDADyhzACFHNut1tRUVF2lwEAF4wwAgCX0NnjejiTBRBGAOCScDqdcjgciomJCWh3OBzyeDwEEpRqhBEAuARcLpc8Hs85dz/FxMTI5/MRRlCqEUYA4BJxuVyEDiAHTHoGAABsRRgBAAC2IowAAABbEUYAAICtCCMAAMBWhBEAAGArwggAALAVYQQAANiKMAIAAGxFGAEAALYijAAAAFsRRgAAgK0IIwAAwFas2gsUI16v11qC3uPx2FwNABQMwghQTHi9Xrndbvn9fqvN4XDI6XTaWBUAXDzCCFBM+Hw++f1+xcbGyu12S5KcTqdcLpfNlQHAxSGMAMWM2+1WVFSU3WUAQIHJdxjx+Xz69NNPFR8fryZNmmjw4MEKDw8Pqu/27ds1fvx4tWvXTg8//HC+iwWAkujM8T+c7UJplK+7aRISEtSmTRvFxcWpQYMG+vzzz9W+fXulpqaet6/f79ef//xnLViwQEuWLLngggGgpHA6nXI4HIqJiVF0dLSio6Pldrvl9XrtLg24pPIVRl555RVVqVJFixYt0lNPPaUlS5boyJEjGj9+/Hn7jhgxQt27d1d0dPSF1goAJYrL5ZLH49GGDRu0YcMGxcbGyu/3W3dMAaVFvsLI/Pnzddttt6lcuXKSpPDwcN1yyy2aP39+nv2++OIL/e9//9Prr79+4ZUCQAnkcrkUFRWlqKgoa2AyUNoEPWbk+PHj2r9/vxo0aBDQ3rBhQ82aNSvXfr/99ptGjBih7777ThUqVAjqtTIyMpSRkWE9DuYyEAAAKJ6CPjOSnp4uSecMVg0PD7eeO1tmZqYGDRqkZ599Vq1btw66qHHjxikiIsL6qlevXtB9AQBA8RJ0GKlUqZLKlCmjI0eOBLQnJyercuXKOfaJi4vT5s2btX37dg0ZMkRDhgzR1q1btWHDBg0ZMkRJSUk59hs9erRSUlKsr3379uXjLQEAgOIk6Ms05cqVU9OmTfXTTz8FtO/YsUMtW7bMsU+rVq30/vvvB7T9+OOPKleunDp27KjQ0NAc+4WGhub6HAAAKFnyNc/IoEGD9NFHH+m5555TtWrVtHv3bi1cuFDvvvuutc28efO0ZMkSTZgwQfXr19eQIUMC9jFnzhxVqFDhnHYAAFA65etumqeeekrNmjVT27Ztddttt6lTp07q2bOnHnzwQWubjRs3avr06QVeKAAAKJnydWYkLCxMS5cu1apVq+T1evXUU0+pY8eOAdv07dtXjRs3znUfI0aMUNmyZS+sWgAAUOLkezr4MmXKqEuXLrk+f/p++dz06NEjvy8JAABKsHxdpgEAAChohBEAAGArwggAALBVvseMALg0vF5vwIJpZy4zDwAlCWEEKIK8Xq/cbrf8fn9Au8PhkNPptKkqACgchBGgCPL5fPL7/YqNjQ1YydXpdMrlctlYGQAUPMIIUIS53e48b5VHyXT2JTlCKEo6wggAFBFOp1MOh0MxMTEB7Q6HQx6Ph0CCEoswAgBFhMvlksfjOWfgckxMjHw+H2EEJRZhBACKEJfLRehAqcM8IwAAwFaEEQAAYCvCCAAAsBVhBAAA2IowAgAAbEUYAQAAtiKMAAAAWxFGAACArQgjAADAVoQRAABgK8IIAACwFWvTAEWE1+u1Fkg7ewl5ACjJCCNAEeD1euV2u+X3+602h8Mhp9NpY1UAcGkQRoAiwOfzye/3KzY2Vm63W5LkdDpZvRWWM8+W8b2BkoYwAhQhbrdbUVFRdpeBIsTpdMrhcCgmJsZqczgc8ng8BBKUGIQRACjCXC6XPB5PwHiimJgY+Xw+wghKDMIIABRxLpeL4IESjVt7AQCArQgjAADAVoQRAABgK8IIAACwFWEEAADYijACAABsRRgBAAC2IowAAABbEUYAAICtCCMAAMBWTAcP2MDr9VprjUiBK7ICQGlDGAEuMa/XK7fbLb/fH9DucDjkdDptqgoA7EMYAS4xn88nv9+v2NhYud1uq93pdLIYGoJ29tk0vn9QnBFGAJu43W5FRUXZXQaKGafTKYfDoZiYmIB2h8Mhj8dDIEGxRBgBgGLE5XLJ4/GcM+YoJiZGPp+PMIJiiTACAMWMy+UidKBE4dZeAABgK8IIAACwFWEEAADYijACAABsRRgBAAC2IowAAABbEUYAAICt8j3PSFZWlpYuXar4+Hg1adJEXbp0UUhISJ59EhIStGbNGmVmZio6OlpNmza94IIBAEDJkq8zI2lpabr++us1bNgwrVixQnfddZduueUWZWZm5trnL3/5izp37qyZM2dqzpw5atu2rf76179edOEAAKBkyNeZkTfeeEPx8fHaunWrqlatqvj4eLVs2VKTJ0/WI488kmOfrl27avz48brsslMvtXLlSnXp0kV9+/bV9ddff/HvACgGvF6vNX332QucAQXlzO8tFs5DcZKvMPLFF19o4MCBqlq1qiSpfv366tOnj7744otcw8iAAQMCHl9//fUqX768fv75Z8IISgWv1yu32y2/32+1ORwOOZ1OG6tCSZLT4nksnIfiJOgwkpmZqZ07dwYseS6dWnl06dKlQb/gokWLdOLECUVHR+e6TUZGhjIyMqzHqampQe8fKGp8Pp/8fr9iY2Otzw9/taIgnb14HgvnobgJOoykpaXJGKMqVaoEtF9++eVBh4X9+/dr8ODBiomJyTOMjBs3Ti+99FKwpQHFgtvtVlRUlN1loIRi8TwUZ0EPYK1QoYIk6dixYwHtqampcjgc5+1/6NAhde/eXVdeeaUmT56c57ajR49WSkqK9bVv375gywQAAMVM0GdGKlSooHr16mnPnj0B7b/99puaNGmSZ9/Dhw+ra9euqlu3rubOnavQ0NA8tw8NDT3vNgAAoGTI1629ffv21VdffWWN50hJSdH8+fPVt29fa5sffvhBkyZNsh4nJiaqa9euql27tubNm6ewsLACKh0AAJQE+bqb5oUXXtC3336r7t27q0ePHpo9e7Zq1KihESNGWNssXLhQ48eP10MPPSRJ6tmzp+Lj43XfffdpypQp1nbt27dX+/btC+htAACA4ipfYaRGjRratGmTpk+fLq/Xq6FDh+ree+8NGDPSvn17DR061HrcpUsXXXPNNYqPjw/YV6NGjS6ydAAAUBLkezr4yy+/XCNHjsz1+d69e6t3797W43fffffCKgMAAKUCC+UBAABbEUYAAICt8n2ZBkDezlyHRmItGtjn7O89Zv5FUUUYAQpQTuvQSKxFg0srp7VqJNarQdFFGAEKUE7r0Ej8RYpL6+y1aiTWq0HRRhgBCgHr0MBurFWD4oQBrAAAwFaEEQAAYCvCCAAAsBVhBAAA2IowAgAAbEUYAQAAtuLWXuAinTnjKrOtoqg783uU+W9QVBBGgIuQ04yrzLaKoiinWVmZkRVFBWEEuAg5zbjKX5sois6elZUZWVGUEEaAAsCMqygOmJUVRRUDWAEAgK0IIwAAwFaEEQAAYCvCCAAAsBUDWAGgFDt7bhzuBoMdCCNAPpw5wZnEJGcovnKad0Ri7hHYgzACBCmnCc4kJjlD8XT2vCMSc4/APoQRIEg5TXAmcVobxRfzjqCoIIwA+cQEZwBQsLibBgAA2IowAgAAbEUYAQAAtmLMCJCHM2/l5TZelBZnfq8zQBuXAmEEyEVOt/JyGy9KspzmHmHeEVwKhBEgFzndystfiSjJzp57hHlHcKkQRoDz4FZelCbMPQI7MIAVAADYijMjAIA8sZgeChthBPg/LIIHBGIxPVwqhBFALIIH5ITF9HCpEEYAsQgekBsGtOJSIIwAZ+DOGQC49AgjAIB8Y5ZWFCTCCEotpnoH8o9ZWlEYCCMolZjqHbgwzNKKwkAYQanEVO/AhWNQKwoaYQSlGgNWgYLBxGi4GIQRlApMaAYUDiZGQ0EgjKDEY0IzoPAwMRoKAmEEJR4TmgGFK7cxJNz+i2ARRlAi5XTbLuNDgEuD23+RX4QRlDjctgvYK7fbf1etWsXZSeSIMIJiL6fBqdy2C9jrzEs3DHLF+RBGUKzlNTj1uuuu44ccUATkNcj1zLMl/NFQel1QGDl48KD27dunRo0aKTIystD6AGcL5iyIxA81oKg5e5Ar40pwpnyFkezsbD3yyCOaNm2aGjdurN27d+vJJ5/U2LFjC7QPcNqZ4SMxMVEDBgzgLAhQAjCuBGfKVxj58MMP9cUXX2jz5s1q3ry51q5dqy5duig6OloDBgwosD4onc4+65FT+HA4HFq0aJGqVatmtfHDCiiegh1X8s033wR85s/Gz4DiL8QYY4LdODo6WlFRUZo0aZLV1qtXL5UtW1bffvttgfU5W2pqqiIiIpSSkqLKlSsHWy6KkLODxtnyOutx5g8ifuiUEmlpUqVKp/79xx9SxYr21oNLIpg/SHKSU2DhZ0XREOzv76DPjGRlZWnbtm166KGHAtrbt2+vyZMnF1gfScrIyFBGRob1OCUlRdKpN1XQDh48qIMHDxb4fvH/+Xw+xcTEKD09Pc/twsLC9PXXXwfcghsZGal69eoFbFcY3wcoYtLS/v+/U1OlrCz7asElU6VKFVWpUsV6fMUVV+iHH35QUlJSrn1O/3y56aabAtrDwsIUGxvLLf1BqlmzpmrWrFng+z398/p85z2CDiPHjh1TZmbmOYNPIyMjlZycXGB9JGncuHF66aWXzmk/+5cSSpb09HTddtttdpeBoqZ2bbsrQDHEz5Oi5dixY4qIiMj1+aDDSLly5SRJx48fD2hPT09X+fLlC6yPJI0ePVpPPPGE9Tg7O1vJycmKjIxUSEhIsCUXK6mpqapXr5727dtXqi9FcRxO4ThwDE7jOJzCcSiex8AYo2PHjqn2ef6oCDqMVKxYUZGRkdq/f39A+/79+3O9LnchfSQpNDRUoaGhAW1nnrorySpXrlxsvskKE8fhFI4Dx+A0jsMpHIfidwzyOiNyWpn87PDGG2/U/PnzrcdZWVlasGCBbrzxRqvN6/Vq3bp1+eoDAABKr3yFkRdeeEFbtmzRsGHDNH/+fA0aNEjHjh3Tk08+aW3z6aefBgwkCqYPAAAovfIVRlq0aKG1a9fqxIkTGj9+vKpWrap169apTp061jYul0udOnXKVx+cujT14osvnnN5qrThOJzCceAYnMZxOIXjULKPQb7mGQEAACho+TozAgAAUNAIIwAAwFaEEQAAYKt8LZSH/Nm3b58OHTqkpk2bBnVPuDFGe/bs0ZEjR1SvXj1Vr1494Pndu3frwIEDAW0VK1ZU27ZtC7TugpSVlaUdO3YoJCREV155pcqUOX/+9fv92rVrlypUqKCmTZsW2H7tdPToUe3atUs1a9ZU3bp1g+pz6NAh7du3T/Xr1z9nkbDk5GT99NNP5/Rp3759nhMK2s3r9crr9SoqKkoOhyOoPnv27FFycrKaN2+uirmsURPMNkXJpk2bJCnoz+7Jkye1fft2lS9fXm63+5zJH1evXn1OnyuuuKJQpvcuKH/88Ye2bNmi+vXrB/2ZCKZPUlKSfvvtN9WrV69Iv//TfvvtNyUkJOTrs5tXn+3bt+vo0aMBbZGRkQErIRdJBgUuPT3dDBgwwISFhZnmzZubsLAwM2HChDz7bNmyxTRv3tzUqFHDREVFGYfDYQYMGGD8fr+1zbBhw0xkZKTp3Lmz9XXPPfcU9tu5YBs3bjT169c3derUMTVr1jSNGzc227Zty7PPG2+8YSpVqmRatWplateubdq2bWvi4+Mver92euutt0yFChWM2+02YWFhZuDAgSYjIyPX7ZOSkkzv3r1N5cqVTZs2bUx4eLgZMWKEyc7OtraZPXu2KVOmTMD3QufOnc3hw4cvxVvKt5UrV5qbb77ZREZGGklB/X+lpKSY7t27m0qVKpmmTZuaSpUqmenTp+d7m6Lk3XffNc2aNTNVqlQxV155ZVB9Vq9ebWrVqmVcLpepVq2aadGihdm1a5f1fGZmppFkWrZsGfC9MGfOnMJ6Gxdl3759Zvjw4aZmzZqmXLlyZty4cQXW529/+5sJDQ01LVq0MKGhoWbw4MEmKyuroN9CgYiLizPdu3c3VatWNZLMvn37CqRPt27dTN26dQO+F0aPHl0Yb6FAEUYKwbPPPmvq1q1rEhISjDGnfnFIMuvWrcu1zzXXXGN69uxpMjMzjTHGxMfHm4iICPPWW29Z2wwbNszcdttthVt8ATlx4oRp1KiRue+++4wxxmRnZ5s77rjDNG/ePNcfDnFxcaZMmTImLi7OGGPMyZMnzX333Wc6d+58Ufu104oVK0xISIj1nuLj40316tXNSy+9lGufe+65x7Ro0cIkJSUZY4zZv3+/qV27tvnwww+tbWbPnm0qVqxYuMUXoPfff9/Mnz/fbNiwIegwMmTIENOsWTOTnJxsjDFm0qRJ5rLLLjO//PJLvrYpSkaOHGl++ukn88wzzwQVRv744w9Ts2ZNM2LECGPMqc9Ez549zdVXX21tczqMLF++vLDKLlArVqww//znP01KSoqpU6dOUGEkmD5z5swx5cqVM99//70xxpiff/7ZREREmPfee6/A30NBeOedd0xcXJxZtmxZ0GEkmD7dunUzzzzzTGGUXKgII4WgRo0a5u9//3tAW8uWLc2wYcNy7dOkSRMzZsyYgLYrr7zSPP3009bjYcOGmZtvvtls2LDB7Nq1q0j+8j1t8eLFRlLAX3CbN282ksyqVaty7PP000+bpk2bBrStWbPGSDI7duy44P3a6d577zUdO3YMaPvrX/9q6tevn2uf2rVrm5dffjmgbfjw4aZt27bW49Nh5KeffjJbt2416enpBVp3Ydm0aVNQYeT48ePG4XCYf/7zn1Zbdna2qV27tnn++eeD3qaoCjaMfPnll6ZMmTLm0KFDVtuKFSsCjuHpMPLZZ5+Z9evXWyG2OAg2jATTp2/fvuamm24KaBsyZIi56qqrLqbEQrd8+fKgw0gwfbp162aGDx9ufvzxR+P1egPOqBZlRftCezGUkJCgQ4cOKTo6OqC9ffv21nXinLz88suaOnWqpkyZoiVLlujpp5+W3+/X8OHDA7aLi4vT/fffr06dOql+/fpauHBhobyPi7Vp0yZFRESocePGVttVV12l8uXL53ocqlatKp/Pp4yMDKvt9LpGGzZsuOD92mnTpk05fi/Ex8fryJEjOfapWrVqjus5bdu2TZmZmVZbWlqabrnlFt166626/PLL9corrxT8G7DJL7/8Ir/fH3DsQkJC1K5dO+v/OZhtirtNmzadM36sffv21nNnGjFihB544AHVqlVLt912m5KSki5prXbL7bO2ffv2gM9NafDpp59qyJAhat26tVq1aqUff/zR7pLOizBSwJKTkyWdGjB0psjISOu5nHTt2lXt27fX6NGj9fTTT+uTTz7R0KFDAxYU7Nmzp/bv36+tW7fqwIEDGjRokO644w7t3r27cN7MRTi9yvLZ8joO99xzjyTpzjvv1MKFCzVt2jSNGTNGl112mdXnQvZrp5zqPf04t3off/xxffrppxo3bpwWL16s5557TitXrtTJkyeVmpoqSapXr57Wr1+v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AwYMsPq88cYbJjIy0uzfv98YY0x0dLS5/vrrrdrq1q1rWrdubXw+n9Vn7969pnXr1qZ58+bmlltuMXXq1DF9+/Y1GRkZAf+vV111laldu7bp3bu3adSokXn99deNMca8/fbbxul0miuvvNL6fzp69GhQ+/3xxx9NZGSkadWqlenatatp0qSJcbvd5sknn8zz2AHFHWEEKESnw8jatWvNtddea95//31jjDGff/65cbvdZufOnQFhZN26dSY8PDxg2fNvv/3WhIeHBywtfqaMjAzTuXNnM2bMGKttyJAh5s477wzY5ttvvzXG5C+MjBw5MmC75557znTq1MmkpaVZbQ899JDp2bOn9XjUqFGmZcuWJjU11RhjzG+//WbCw8PPG0Y++eQTc+211+b6fDCvvXPnTlOpUiUzadIks379elO+fHkze/Zs6/no6GhTqVIl89tvvxljjElNTTUtW7Y0I0aMsLa55pprzOOPP26ys7ONMcakpaWZ1q1bm3HjxlnbtG/f3vTo0cP4/X5jjDFZWVlm/vz51vOdO3c2L774YkD9wew3Ojra3H///dY2X331lZFEGEGJx5gR4BIZPHiwJk6cqOHDh2vy5MkaPHjwOdtMmzZNV1xxhX7++Wd5PB6ZU38wKDMzU+vXr1evXr2sbTdv3qz4+Hilp6erTp06+uGHH6znwsLC5PF4lJSUpMjISJUvX/6CxmGMGDEi4PHUqVPVt29fLVy40KqtevXqmjFjhrKzs1WmTBl9+eWXGj16tMLDwyVJDRs21B133KEtW7bk+Vrnu0QTzGtfccUVmjBhgh577DHVqFFD9913n/r37x+wn9tvv10NGzaUJIWHh2v48OF6+eWX9d577+nXX3/VmjVr9Oc//1lff/219TqNGzfW8uXL9eyzz8rj8eiHH37QunXrFBYWJkkqU6ZMnrUHs99du3Zpw4YNmjZtmrXc++23365GjRrledyAkoAwAlwid9xxh0aMGKHZs2dr9erV+ve//63U1NSAbfbu3auUlBTNmjUroL1fv36qWLGipFPjSXr06KGEhAS1bdtWlStX1q+//hqw/XPPPachQ4aobt26ioqK0k033aRHH31UVatWzVfNtWrVsv594sQJHThwQB6PR0ePHj2nvuPHj6t8+fJKSEhQgwYNAp5v2LBhnmEkIyNDS5cu1WuvvZbj88G8tsPhkCQ98MADGjt2rH7//Xe9+eab5+wrp9oOHjyojIwM7d27V5K0cuVKlS1b1tqmfPnyatq0qSTJ6/VKkvU4GPnZb071ASUdYQS4RCpWrKg///nPeuCBB3TzzTerevXq54SRypUrq3Hjxpo5c2au+3nvvfckSfv27dNll536CD/77LNatGiRtU3NmjX17bffKiUlRStXrtSbb76pzz//XD/99JPKlClzzoDX0wMsz3b6L3RJKleunCpUqKCBAwdq+PDhudYXERERMIhU0jmPz7Zs2TJVq1ZNV155ZY7PB/vakvT+++/L5/MpMjJSr7/+ul5//fU8azly5IjCw8MVGhqqypUrS5JeeukltWjRIsf9V6lSRdKpUHj55ZfnWctpwez39N1UR44csYJnTvUCJRF30wCX0KOPPqqbbrpJo0aNyvH5m266SStWrNDPP/8c0O7z+XTixAlJ0sGDB9W4cWMriJw8eVJz584N2H7//v2STgWDW265Ra+99po8Ho9SU1NVp04dpaenKyEhwdp++fLl5609JCREPXv21OTJk5WVlZXj60nStddeqzlz5liPT548qXnz5uW57/Ndogn2tX/66Sc99dRT+uCDDzRjxgy98847+u9//3vOa508edJ6/M0336hz586SpKioKFWvXl0fffTROTWcPl5t27ZVtWrVNH369IDnz7xrqFKlSgEBL5j9NmvWTE6nM+DY7d69+7yXt4CSgDMjwCV01VVX5XnW45577tE333yj6667TiNGjFCtWrW0bds2LVy4UBs3blT58uXVr18/9e/fXy+88ILq1q2rGTNm6ODBg6pfv761nzFjxig5OVndunVThQoV9Mknn6hHjx6qXLmy2rRpo1atWunOO+/UAw88oF9//VWff/55UPX/4x//UJcuXXTNNdcoJiZGxhitXLlS5cuX17///W9J0ssvv6xrrrlG9913nzp37qwvv/xSR48eVURERK77XbBggT7++OOLeu0TJ07o7rvv1oABA3TXXXdJkh5//HHdc8892rp1q3VGIyUlRT179tTAgQO1Zs0azZ8/X6tXr5Z06rLJ5MmTdccdd+jQoUPq3r27fD6f5s2bp3vvvVePPPKIypcvr48++kiDBg3S/v371bFjR23btk2JiYnWcWzXrp1mzpypZs2aKSwsTP379z/vfitUqKBXXnlFjz/+uA4fPqzq1atrwoQJ1lkVoCQLMSaHaSEBFIjExEQ99thjevXVV3XFFVec8/yhQ4c0cuTIgOezs7M1e/ZsLV26VJmZmWrTpo3uvfdea0CoJMXFxWnOnDnKzMzUddddpypVqmjt2rUBlyTmz5+vJUuWKCMjQ+3atVNMTIwqVKggSUpOTtaECRP0+++/68orr1SfPn30wgsv6IMPPlDVqlX1yy+/6MUXX9SMGTNUrly5gJqPHj2qadOmafPmzapSpYq6dOmifv36BVzS2bJli3UWo3PnzoqIiNDatWs1duzYc47Btm3b1KlTJyUlJSk0NDTP45nXa8fFxemzzz7TxIkTreBz4sQJPfbYY7rhhhs0aNAgtWvXTrfffrvq16+v1atXq0yZMho8eLDatGkT8Dq7du3SjBkztHfvXtWrV0/9+/dXu3btArbZvn27ZsyYocTERLVr105DhgxR+fLlJUl+v18fffSRtm3bpvT0dH388ceKiIgIar/z5s3T3Llzdfnll6t///5as2aN6tSpo7vvvjvPYwMUZ4QRALYaN26cfvjhB82ePbvQX+t0GHn22WcL/bUABI8xIwBsFRISoqFDh9pdBgAbMWYEgK0u5VmKnj175no3CwD7cJkGAADYiss0AADAVoQRAABgK8IIAACwFWEEAADYijACAABsRRgBAAC2IowAAABbEUYAAICtCCMAAMBW/w+ij4YvTJvd+gAAAABJRU5ErkJggg==", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fig, ax = plt.subplots()\n", "\n", "ax.hist(\n", " calibrated_images.flatten() / science_flux,\n", " bins=100,\n", " histtype='step',\n", " color='k',\n", " )\n", "\n", "ax.axvline(1, c='r')\n", "ax.set_xlabel('Measured / expected')\n", "ax.set_title('Calibrated flux')\n", "\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "dbabc8bd", "metadata": {}, "source": [ "We can see that, unsurprisingly, the calibrated fluxes follow a Gaussian distribution centred on the expected value.\n", "\n", "That concludes this demonstration of `phoptic`'s calibration error propagation." ] } ], "metadata": { "kernelspec": { "display_name": "opticam7", "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.14.6" } }, "nbformat": 4, "nbformat_minor": 5 }