From 2888c7c01530544c4ee2944f63ff5d3ffd71ce8a Mon Sep 17 00:00:00 2001 From: Eunkyu Han Date: Sat, 14 Sep 2024 13:54:36 +0900 Subject: [PATCH] Updating the Pandeia notebook based on the comments from the RSP internal validation --- content/notebooks/pandeia/pandeia.ipynb | 160 +++++++++++++++--------- 1 file changed, 104 insertions(+), 56 deletions(-) diff --git a/content/notebooks/pandeia/pandeia.ipynb b/content/notebooks/pandeia/pandeia.ipynb index c1887dc..481df90 100644 --- a/content/notebooks/pandeia/pandeia.ipynb +++ b/content/notebooks/pandeia/pandeia.ipynb @@ -47,7 +47,7 @@ "\n", "## Imports\n", "\n", - "Besides the Pandeia-related imports, we will use `scipy.optimize.minimize_scalar` to help with optimizing signal-to-noise ratios (SNRs)." + "Besides the Pandeia-related imports, we will use `scipy.optimize.minimize_scalar` to help with optimizing signal-to-noise ratios (SNRs), `scipy.interpolate.interp1d` to calculate a desired target magnitude for a given observing setup and `numpy` to handle numerical computing." ] }, { @@ -59,7 +59,9 @@ "source": [ "from pandeia.engine.calc_utils import build_default_calc\n", "from pandeia.engine.perform_calculation import perform_calculation\n", - "from scipy.optimize import minimize_scalar" + "from scipy.optimize import minimize_scalar\n", + "from scipy import interpolate\n", + "import numpy as np" ] }, { @@ -67,7 +69,7 @@ "id": "b6865f31-258d-4cf6-a6a0-1dae41773b02", "metadata": {}, "source": [ - "We set `FILTER` as global variable before beginning since all examples make use of the same F129 imaging filter." + "We set `FILTER` as global variable before beginning since all examples make use of the same F129 imaging filter. Please note that Pandeia's filter definition is case-sensitive and will only take lower-case letters for filter names. " ] }, { @@ -77,7 +79,7 @@ "metadata": {}, "outputs": [], "source": [ - "FILTER = 'f129'" + "FILTER = 'f129' # Pandeia's filter definitaion is case-sensitive and will only take lower-case letters for filter names " ] }, { @@ -105,7 +107,7 @@ "\n", "In this first example, we calculate the expected SNR for a point source with a flat spectral distribution (default target) normalized to 25 AB magnitudes. We take three exposures in band F129 with the multi-accumulation (MA) table \"High Latitude Wide Area Survey - Imaging\", truncated after 5 resultants (91.20 seconds of total exposure time). MA tables describe the sequence of individual reads that are combined into resultants and comprise the up-the-ramp sampling during a single exposure of the WFI detectors. For more information on the WFI MA tables, please refer to the [RDox documentation](https://roman-docs.stsci.edu/raug/astronomers-proposal-tool-apt/appendix/appendix-wfi-multiaccum-tables).\n", "\n", - "We first define the observing setup:" + "We first create a default calculation using Pandeia's built-in function `build_default_calc(, , )`: " ] }, { @@ -115,10 +117,50 @@ "metadata": {}, "outputs": [], "source": [ - "calc = build_default_calc('roman', 'wfi', 'imaging')\n", - "\n", - "calc['configuration']['instrument']['filter'] = FILTER\n", - "calc['configuration']['detector']['nexp'] = 3" + "calc = build_default_calc('roman', 'wfi', 'imaging') # Creating a default calculation using Roman's WFI with an imaing mode" + ] + }, + { + "cell_type": "markdown", + "id": "d8036f34", + "metadata": {}, + "source": [ + "Let's take a look at how the default calculation is set up:" + ] + }, + { + "cell_type": "markdown", + "id": "db27d9f0", + "metadata": {}, + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a48d0642", + "metadata": {}, + "outputs": [], + "source": [ + "print(calc) # Print the default calculation" + ] + }, + { + "cell_type": "markdown", + "id": "013b4561", + "metadata": {}, + "source": [ + "The `build_default_calc` created a scene with a single point source to observe with F087 filter, High Latitude Wide Area Survey - Imaging (hlwas_imaging) MA table, truncated after 5 resultants, and with a single exposure. With the WFI, an exposure refers to a single multi-accum sequence of the detector array at a single dither point in the dither pattern. Next, we define the observing setup and make some changes to the default setting:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5cdd524", + "metadata": {}, + "outputs": [], + "source": [ + "calc['configuration']['instrument']['filter'] = FILTER # Setting the filter to F129\n", + "calc['configuration']['detector']['nexp'] = 3 # Taking three exposures of multi-accum sequence" ] }, { @@ -146,7 +188,7 @@ "id": "55f5d8ac-126c-4b94-acd6-c26d9bfd63ac", "metadata": {}, "source": [ - "Finally, we perform the signal to noise calculation and print the result:" + "Finally, we perform the signal to noise calculation using Pandeia's another built-in function `perform_calculation` and print the result:" ] }, { @@ -177,11 +219,11 @@ "id": "ca56cbc7-ade0-4cc3-8e21-81a2c1b4edf9", "metadata": {}, "source": [ - "### Estimate the Limiting Magnitude for a Given SNR\n", + "### Calculate Corresponding Magnitude for a Given Setup\n", "\n", - "In this example, we are interested in understanding the limited magnitude corresponding to a given SNR and for a specific observing setup.\n", + "In this example, we are interested in understanding the corresponding magnitude for a given SNR and for a specific observing setup.\n", "\n", - "The following helper functions use Pandeia to simulate a range of scenes at different magnitudes in order to estimate the limiting magnitude corresponding to a given SNR. As above, we assume a point source with a flat spectrum, and the MA table is set to the \"High Latitude Wide Area Survey - Imaging\" table with 5 resultants." + "The following helper functions use Pandeia to simulate a range of scenes at different magnitudes in order to estimate the corresponding magnitude for a given SNR and a number of exposures. As above, we assume a point source with a flat spectrum, and the MA table is set to the \"High Latitude Wide Area Survey - Imaging\" table with 5 resultants." ] }, { @@ -191,28 +233,7 @@ "metadata": {}, "outputs": [], "source": [ - "def _mag2sn_(mag, calc, sntarget):\n", - " \"\"\"\n", - " Optimize a S/N ratio given a magnitude. This is a helper function used as\n", - " an argument for scipy's minimize_scalar() as used in compute_mag().\n", - " \n", - " Parameters\n", - " ----------\n", - " mag: TYPE??\n", - " The magnitude used in an iteration of minimize_scalar()\n", - " calc: TYPE??\n", - " A Pandeia calculation object\n", - " sntarget: TYPE??\n", - " Required S/N from the matching argument of compute_mag()\n", - " \n", - " \"\"\"\n", - " calc['scene'][0]['spectrum']['normalization']['norm_flux'] = mag\n", - " etc = perform_calculation(calc)['scalar']\n", - " \n", - " return (sntarget - etc['sn'])**2\n", - "\n", - "\n", - "def compute_mag(filt, sn, nexp, bracket=(18., 30.), xtol=1e-4):\n", + "def compute_mag(filt, nexp, bracket=(18, 30)):\n", " \"\"\"\n", " Method to compute the magnitude from S/N and number of exposures\n", " \n", @@ -220,21 +241,18 @@ " ----------\n", " filt : str\n", " Name of Roman WFI filter\n", - " sn : float\n", - " Required S/N\n", " nexp : int\n", " Number of exposures\n", " bracket : tuple\n", - " Range of magnitudes to test. default: (18., 30.)\n", - " xtold: float, default 1e-4\n", - " Target tolerance for minimizer\n", + " Range of magnitudes to test. default: (18, 30)\n", + "\n", " \n", " Returns\n", " -------\n", - " mag : float\n", - " Optimal magnitude for specified S/N and number of exposures\n", - " report: dict\n", - " Pandeia dictionary with optimal parameters\n", + " mag_range : float\n", + " An array of magnitudes used to compute the SNRs\n", + " computed_snrs: float\n", + " An array of computed SNRs from Pandeia calculations\n", " \"\"\"\n", " \n", " # Set up default Roman observation\n", @@ -247,14 +265,38 @@ " # Set number of exposures and filter\n", " calc['configuration']['detector']['nexp'] = nexp\n", " calc['configuration']['instrument']['filter'] = filt\n", - " \n", - " res = minimize_scalar(_mag2sn_, bracket=bracket, args=(calc, sn),\n", - " method='brent', options={'xtol': xtol})\n", - " mag = res['x']\n", - " calc['scene'][0]['spectrum']['normalization']['norm_flux'] = mag\n", - " report = perform_calculation(calc)\n", + "\n", + " # Create an array of magnitudes range of interest\n", + " mag_range = np.arange(bracket[0], bracket[1]+1, 1)\n", + " # Create empty lists to save the computations\n", + " computed_snrs = []\n", + " # Compute the SNRs for a given magnitude\n", + " for m in range(len(mag_range)):\n", + " mag = mag_range[m]\n", + " calc['scene'][0]['spectrum']['normalization']['norm_flux'] = mag\n", + " report = perform_calculation(calc)\n", + " computed_snrs.append(report['scalar']['sn'])\n", + "\n", + " return mag_range, computed_snrs\n", + "\n", + "def _mag2sn_(mag_range, computed_snrs, sntarget):\n", + " \"\"\"\n", + " Calculate a magnitude given a desired SNR by interpolating (computed_snrs, mag_range) from compute_mag\n", + " \n", + " Parameters\n", + " ----------\n", + " mag_range: float\n", + " An array of magnitudes used in calculating a range of SNRs in compute_mag\n", + " computed_snrs: float\n", + " An array of computed SNR given the mag_range using Pandeia calculation object\n", + " sntarget: float\n", + " Required S/N\n", " \n", - " return mag, report" + " \"\"\"\n", + " interpolator = interpolate.interp1d(computed_snrs, mag_range)\n", + " mag = interpolator(sntarget)\n", + "\n", + " return mag" ] }, { @@ -278,7 +320,9 @@ "sn = 5\n", "nexp = 10\n", "\n", - "mag, report = compute_mag(FILTER, sn, nexp)\n", + "# mag, report = compute_mag(FILTER, sn, nexp)\n", + "mag_range, computed_snrs = compute_mag(FILTER, nexp)\n", + "mag = _mag2sn_(mag_range, computed_snrs, sn)\n", "print(f'Estimated magnitude: {mag:.2f}')" ] }, @@ -370,7 +414,11 @@ " res = minimize_scalar(_nexp2sn_, bracket=bracket, bounds=bracket,\n", " args=(calc, sn), method='bounded',\n", " options={'xatol': xtol})\n", - " nexp = int(res['x'])\n", + " \n", + " # Take the optimization result and set it to nexp\n", + " # 'x' is the solution array in the optimization result object\n", + " # For more details on the minimize_scalar function, refer to https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize_scalar.html\n", + " nexp = int(res['x']) \n", " calc['configuration']['detector']['nexp'] = nexp\n", " report = perform_calculation(calc)\n", " \n", @@ -466,7 +514,7 @@ "id": "f3b4f34e-6dab-4d83-8f0e-0dc900c0e2b6", "metadata": {}, "source": [ - "## Aditional Resources\n", + "## Additional Resources\n", "\n", "- The Roman User Documentation's [\"Pandeia for Roman\"](https://roman-docs.stsci.edu/simulation-tools-handbook-home/pandeia-for-roman) page and associated overview.\n", "- Full API references for [Pandeia Engine inputs](https://outerspace.stsci.edu/display/PEN/Pandeia+Engine+Input+API) and [Pandeia Engine outputs](https://outerspace.stsci.edu/display/PEN/Pandeia+Engine+Output+API).\n", @@ -485,8 +533,8 @@ "source": [ "## About this notebook\n", "\n", - "**Author:** Justin Otor \n", - "**Updated On:** 2024-05" + "**Author:** Justin Otor, Eunkyu Han \n", + "**Updated On:** 2024-09" ] }, {