From 465c7e43de61d93805637db967067a2fe3f50161 Mon Sep 17 00:00:00 2001 From: Michael Dulude Date: Tue, 21 Nov 2023 14:17:44 -0500 Subject: [PATCH] Add WFC3 notebook 'wfc3_exception_report.ipynb' (#102) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * updated _toc.yml and _config.yml files * Added trailing line to rad_prof.py. * Created notebook-level requirements.txt file. * wfc3_exception_report.ipynb: cleared all notebook outputs * Update rad_prof.py for PEP8 changes for PEP8 compliance * Update display_image.py for PEP8 edits for PEP8 compliance * Update display_image.py for PEP8 changes for PEP8 compiance * Update display_image.py for PEP8 🫠 * Update rad_prof.py for PEP8 final edits for PEP8 compliance * Update wfc3_exception_report.ipynb for PEP8 changes for PEP8 compliance * Update wfc3_exception_report.ipynb for PEP8 * Update wfc3_exception_report.ipynb for PEP8 hopefully final PEP8 changes. sorry im so bad at this 🫠 * Update requirements.txt Removing pins for version numbers. Also removing ginga dependency since I'm having problems with it in Python 3.11. Instead, I'm going to use astropy.visualization's ZScaleInterval * Update display_image.py swapping ginga.util.zscale for astropy.visualization's ZScaleInterval * Update wfc3_exception_report.ipynb removed `ginga.util.zscale` dependence and replaced with `astropy.visualization`'s `ZScaleInterval` * Update wfc3_exception_report.ipynb second markdown table in section 6.1 wasn't formatted correctly * Update README.md removing the old environment installation instructions and replacing them with information about using the `requirements.txt` file * Update README.md adding line breaks * Update wfc3_exception_report.ipynb minor changes to make the code more robust and platform-independent * Update wfc3_exception_report.ipynb i added shutil to the imports but forgot to describe it in the import markdown table --------- Co-authored-by: bjkuhn --- _config.yml | 1 - _toc.yml | 2 +- notebooks/WFC3/exception_report/README.md | 21 +- .../exception_report/docs/display_image.py | 273 ++++++++---------- .../WFC3/exception_report/docs/rad_prof.py | 51 ++-- .../WFC3/exception_report/requirements.txt | 7 + .../wfc3_exception_report.ipynb | 231 +++++++-------- 7 files changed, 285 insertions(+), 301 deletions(-) create mode 100644 notebooks/WFC3/exception_report/requirements.txt diff --git a/_config.yml b/_config.yml index 0ec637038..e7f606c78 100644 --- a/_config.yml +++ b/_config.yml @@ -46,7 +46,6 @@ exclude_patterns: [notebooks/DrizzlePac/align_mosaics/align_mosaics.ipynb, notebooks/DrizzlePac/use_ds9_regions_in_tweakreg/use_ds9_regions_in_tweakreg.ipynb, notebooks/WFC3/calwf3_recalibration/calwf3_recal_tvb.ipynb, notebooks/WFC3/dash/dash.ipynb, - notebooks/WFC3/exception_report/wfc3_exception_report.ipynb, notebooks/WFC3/filter_transformations/filter_transformations.ipynb, notebooks/WFC3/flux_conversion_tool/flux_conversion_tool.ipynb, notebooks/WFC3/image_displayer_analyzer/wfc3_image_displayer_analyzer.ipynb, diff --git a/_toc.yml b/_toc.yml index 3703fb2aa..94dbd254f 100644 --- a/_toc.yml +++ b/_toc.yml @@ -62,7 +62,7 @@ parts: # - file: notebooks/WFC3/calwf3_recalibration/calwf3_recal_tvb.ipynb - file: notebooks/WFC3/calwf3_v1.0_cte/calwf3_with_v1.0_PCTE.ipynb # - file: notebooks/WFC3/dash/dash.ipynb -# - file: notebooks/WFC3/exception_report/wfc3_exception_report.ipynb + - file: notebooks/WFC3/exception_report/wfc3_exception_report.ipynb # - file: notebooks/WFC3/filter_transformations/filter_transformations.ipynb # - file: notebooks/WFC3/flux_conversion_tool/flux_conversion_tool.ipynb # - file: notebooks/WFC3/image_displayer_analyzer/wfc3_image_displayer_analyzer.ipynb diff --git a/notebooks/WFC3/exception_report/README.md b/notebooks/WFC3/exception_report/README.md index def41952f..47fd10a66 100644 --- a/notebooks/WFC3/exception_report/README.md +++ b/notebooks/WFC3/exception_report/README.md @@ -3,19 +3,22 @@ when an observer receives a WFC3 Exception Report Email. This directory, once downloaded, should contain this README.md, the tutorial Jupyter Notebook `wfc3_exception_report.ipynb`, an `html` copy of the notebook, -and a subdirectory titled `docs`. The subdirectory should contain two `.py` -files, one `.png`, and one `.gif` file that are used in the notebook. +a `requirements.txt` file, and a subdirectory titled `docs`. The subdirectory +should contain two `.py` files, one `.png`, and one `.gif` file that are used +in the notebook. -In order to run this Jupyter Notebook, you must have created a virtual -environment, such as the one in [WFC3 Library's](https://github.com/spacetelescope/WFC3Library) installation instructions. If -you are using the `wfc3_env` environment from the `wfc3_env_legacy.yml` file in the -WFC3Library repository, then you should not need any other packages to run this -notebook. +To run this Jupyter Notebook, you must have created a virtual environment +that contains (at minimum) the packages listed in the `requirements.txt` file +that is included within the repository. We recommend creating a new conda +environment using the requirements file: + `$ conda create -n except_report python=3.11`
+ `$ conda activate except_report`
+ `$ pip install -r requirements.txt`
+ Optional Note: The tools in this notebook (specifically display_image) look much better in Jupyter Lab rather than in the classic Jupyter Notebook. If your environment has Jupyter Lab installed it's recommended you use that to run the -.ipynb file. If you're interested in adding Jupyter Lab to your environment see -the install instructions on the [Jupyter website](https://jupyter.org/install). +.ipynb file. See the [Jupyter website](https://jupyter.org/install) for more info. Please submit any questions or comments to the [WFC3 Help Desk](https://stsci.service-now.com/hst). diff --git a/notebooks/WFC3/exception_report/docs/display_image.py b/notebooks/WFC3/exception_report/docs/display_image.py index c503a9ab2..b9f046808 100644 --- a/notebooks/WFC3/exception_report/docs/display_image.py +++ b/notebooks/WFC3/exception_report/docs/display_image.py @@ -1,23 +1,23 @@ #! /usr/bin/env python - -import numpy as np import sys from astropy.io import fits -from ginga.util import zscale +from astropy.visualization import ZScaleInterval import matplotlib.pyplot as plt +import numpy as np + def display_image(filename, - colormaps=['Greys_r','Greys_r','inferno_r'], - scaling=[(None,None),(None,None),(None,None)], + colormaps=['Greys_r', 'Greys_r', 'inferno_r'], + scaling=[(None, None), (None, None), (None, None)], printmeta=False, ima_multiread=False, - figsize=(18,18), + figsize=(18, 18), dpi=200): - - """ A function to display the 'SCI', 'ERR/WHT', and 'DQ/CTX' arrays - of any WFC3 fits image. This function returns nothing, but will display - the requested image on the screen when called. + """ + A function to display the 'SCI', 'ERR/WHT', and 'DQ/CTX' arrays + of any WFC3 fits image. This function returns nothing, but will display + the requested image on the screen when called. Authors ------- @@ -51,8 +51,8 @@ def display_image(filename, List of real numbers to act as scalings for the SCI, ERR, and DQ arrays. The first element in the list is for the SCI array the second is for the ERR array and the third element in the list is for the DQ extension. If - no scalings are given the default scaling will use - ginga.util.zscale.zscale(). All three scalings must be provided even if + no scalings are given the default scaling will use astropy.visualization + ZScaleInterval.get_limits(). All three scalings must be provided even if only changing 1-2 scalings. E.g. to change SCI array scaling: scaling = [(5E4,8E4),(None,None),(None,None)] @@ -108,7 +108,7 @@ def display_image(filename, print("Invalid image section specified") return 0, 0 try: - xstart = int(xsec[: xs]) + xstart = int(xsec[:xs]) except ValueError: print("Problem getting xstart") return @@ -132,7 +132,6 @@ def display_image(filename, print("Problem getting yend") return - bunit = get_bunit(h1) detector = h['detector'] issubarray = h['subarray'] si = h['primesi'] @@ -151,33 +150,32 @@ def display_image(filename, print('-'*44) print(f"Filter = {h['filter']}, Date-Obs = {h['date-obs']} T{h['time-obs']},\nTarget = {h['targname']}, Exptime = {h['exptime']}, Subarray = {issubarray}, Units = {h1['bunit']}\n") - if detector == 'UVIS': - if ima_multiread == True: + if ima_multiread is True: sys.exit("keyword argument 'ima_multiread' can only be set to True for 'ima.fits' files") try: if all_pixels: xstart = 0 ystart = 0 - xend = naxis1 # full x size + xend = naxis1 # full x size yend = naxis2*2 # full y size with fits.open(imagename) as hdu: - uvis2_sci = hdu["SCI",1].data + uvis2_sci = hdu["SCI", 1].data uvis2_err = hdu[2].data uvis2_dq = hdu[3].data - uvis1_sci = hdu["SCI",2].data + uvis1_sci = hdu["SCI", 2].data uvis1_err = hdu[5].data uvis1_dq = hdu[6].data try: - fullsci = np.concatenate([uvis2_sci,uvis1_sci]) - fulldq = np.concatenate([uvis2_dq,uvis1_dq]) - fullerr = np.concatenate([uvis2_err,uvis1_err]) + fullsci = np.concatenate([uvis2_sci, uvis1_sci]) + fulldq = np.concatenate([uvis2_dq, uvis1_dq]) + fullerr = np.concatenate([uvis2_err, uvis1_err]) - fullsci = fullsci[ystart:yend,xstart:xend] - fulldq = fulldq[ystart:yend,xstart:xend] - fullerr = fullerr[ystart:yend,xstart:xend] + fullsci = fullsci[ystart:yend, xstart:xend] + fulldq = fulldq[ystart:yend, xstart:xend] + fullerr = fullerr[ystart:yend, xstart:xend] make1x3plot(scaling, colormaps, fullsci, fullerr, fulldq, xstart, xend, ystart, yend, @@ -185,26 +183,26 @@ def display_image(filename, figsize, dpi) except ValueError: - fullsci = np.concatenate([uvis2_sci,uvis1_sci]) - fullsci = fullsci[ystart:yend,xstart:xend] + fullsci = np.concatenate([uvis2_sci, uvis1_sci]) + fullsci = fullsci[ystart:yend, xstart:xend] - z1_sci, z2_sci = get_scale_limits(scaling[0],fullsci,'SCI') + z1_sci, z2_sci = get_scale_limits(scaling[0], fullsci, 'SCI') - fig, ax1 = plt.subplots(1,1,figsize=figsize,dpi=dpi) - im1 = ax1.imshow(fullsci,origin='lower',extent=(xstart,xend,ystart,yend),cmap=colormaps[0],vmin=z1_sci, vmax=z2_sci) + fig, ax1 = plt.subplots(1, 1, figsize=figsize, dpi=dpi) + im1 = ax1.imshow(fullsci, origin='lower', extent=(xstart, xend, ystart, yend), cmap=colormaps[0], vmin=z1_sci, vmax=z2_sci) if len(fname) > 18: ax1.set_title(f"WFC3/{detector} {fname}\n{h1['extname']} ext") else: ax1.set_title(f"WFC3/{detector} {fname} {h1['extname']} ext") - fig.colorbar(im1, ax=ax1,shrink=.75,pad=.03) + fig.colorbar(im1, ax=ax1, shrink=.75, pad=.03) - except (IndexError,KeyError): + except (IndexError, KeyError): if all_pixels: - xstart = 0 - ystart = 0 - xend = naxis1 # full x size - yend = naxis2 # full y size + xstart = 0 + ystart = 0 + xend = naxis1 # full x size + yend = naxis2 # full y size with fits.open(imagename) as hdu: uvis_ext1 = hdu[1].data @@ -212,35 +210,34 @@ def display_image(filename, uvis_ext3 = hdu[3].data try: - uvis_ext1 = uvis_ext1[ystart:yend,xstart:xend] - uvis_ext2 = uvis_ext2[ystart:yend,xstart:xend] - uvis_ext3 = uvis_ext3[ystart:yend,xstart:xend] + uvis_ext1 = uvis_ext1[ystart:yend, xstart:xend] + uvis_ext2 = uvis_ext2[ystart:yend, xstart:xend] + uvis_ext3 = uvis_ext3[ystart:yend, xstart:xend] make1x3plot(scaling, colormaps, uvis_ext1, uvis_ext2, uvis_ext3, xstart, xend, ystart, yend, detector, fname, h1, h2, h3, figsize, dpi) - except (TypeError,IndexError,AttributeError): + except (TypeError, IndexError, AttributeError): - z1_sci, z2_sci = get_scale_limits(scaling[0],uvis_ext1,'SCI') - fig, ax1 = plt.subplots(1,1,figsize=figsize,dpi=dpi) - im1 = ax1.imshow(uvis_ext1,origin='lower',extent=(xstart,xend,ystart,yend),cmap=colormaps[0],vmin=z1_sci, vmax=z2_sci) + z1_sci, z2_sci = get_scale_limits(scaling[0], uvis_ext1, 'SCI') + fig, ax1 = plt.subplots(1, 1, figsize=figsize, dpi=dpi) + im1 = ax1.imshow(uvis_ext1, origin='lower', extent=(xstart, xend, ystart, yend), cmap=colormaps[0], vmin=z1_sci, vmax=z2_sci) if len(fname) > 18: ax1.set_title(f"WFC3/{detector} {fname}\n{h1['extname']} ext") else: ax1.set_title(f"WFC3/{detector} {fname} {h1['extname']} ext") - fig.colorbar(im1, ax=ax1,shrink=.75,pad=.03) - + fig.colorbar(im1, ax=ax1, shrink=.75, pad=.03) if detector == 'IR' and '_ima.fits' not in fname: - if ima_multiread == True: + if ima_multiread is True: sys.exit("keyword argument 'ima_multiread' can only be set to True for 'ima.fits' files") if all_pixels: xstart = 0 ystart = 0 - xend = naxis1 # full x size - yend = naxis2 # full y size + xend = naxis1 # full x size + yend = naxis2 # full y size try: with fits.open(imagename) as hdu: @@ -248,9 +245,9 @@ def display_image(filename, data_err = hdu[2].data data_dq = hdu[3].data - data_sci = data_sci[ystart:yend,xstart:xend] - data_err = data_err[ystart:yend,xstart:xend] - data_dq = data_dq[ystart:yend,xstart:xend] + data_sci = data_sci[ystart:yend, xstart:xend] + data_err = data_err[ystart:yend, xstart:xend] + data_dq = data_dq[ystart:yend, xstart:xend] make1x3plot(scaling, colormaps, data_sci, data_err, data_dq, xstart, xend, ystart, yend, @@ -258,49 +255,48 @@ def display_image(filename, figsize, dpi) except (AttributeError, TypeError, ValueError): - z1_sci, z2_sci = get_scale_limits(scaling[0],data_sci,'SCI') - fig, ax1 = plt.subplots(1,1,figsize=figsize,dpi=dpi) - im1 = ax1.imshow(data_sci,origin='lower',extent=(xstart,xend,ystart,yend),cmap=colormaps[0],vmin=z1_sci, vmax=z2_sci) - if len(fname) > 18: - ax1.set_title(f"WFC3/{detector} {fname}\n{h1['extname']} ext") - else: - ax1.set_title(f"WFC3/{detector} {fname} {h1['extname']} ext") - fig.colorbar(im1, ax=ax1,shrink=.75,pad=.03) - + z1_sci, z2_sci = get_scale_limits(scaling[0], data_sci, 'SCI') + fig, ax1 = plt.subplots(1, 1, figsize=figsize, dpi=dpi) + im1 = ax1.imshow(data_sci, origin='lower', extent=(xstart, xend, ystart, yend), cmap=colormaps[0], vmin=z1_sci, vmax=z2_sci) + if len(fname) > 18: + ax1.set_title(f"WFC3/{detector} {fname}\n{h1['extname']} ext") + else: + ax1.set_title(f"WFC3/{detector} {fname} {h1['extname']} ext") + fig.colorbar(im1, ax=ax1, shrink=.75, pad=.03) if '_ima.fits' in fname: if all_pixels: xstart = 0 ystart = 0 - xend = naxis1 # full x size - yend = naxis2 # full y size + xend = naxis1 # full x size + yend = naxis2 # full y size - if ima_multiread == True: + if ima_multiread is True: nsamps = h['NSAMP'] - for ext in reversed(range(1,nsamps+1)): + for ext in reversed(range(1, nsamps+1)): with fits.open(imagename) as hdu: - data_sci = hdu['SCI',ext].data - data_err = hdu['ERR',ext].data - data_dq = hdu['DQ',ext].data + data_sci = hdu['SCI', ext].data + data_err = hdu['ERR', ext].data + data_dq = hdu['DQ', ext].data - data_sci = data_sci[ystart:yend,xstart:xend] - data_err = data_err[ystart:yend,xstart:xend] - data_dq = data_dq[ystart:yend,xstart:xend] + data_sci = data_sci[ystart:yend, xstart:xend] + data_err = data_err[ystart:yend, xstart:xend] + data_dq = data_dq[ystart:yend, xstart:xend] makeIR1x3plot(scaling, colormaps, data_sci, data_err, data_dq, - xstart, xend, ystart, yend, - detector, fname, h1, h2, h3, nsamps, ext, - figsize, dpi) + xstart, xend, ystart, yend, + detector, fname, h1, h2, h3, nsamps, ext, + figsize, dpi) - if ima_multiread == False: + if ima_multiread is False: with fits.open(imagename) as hdu: - data_sci = hdu['SCI',1].data - data_err = hdu['ERR',1].data - data_dq = hdu['DQ',1].data + data_sci = hdu['SCI', 1].data + data_err = hdu['ERR', 1].data + data_dq = hdu['DQ', 1].data - data_sci = data_sci[ystart:yend,xstart:xend] - data_err = data_err[ystart:yend,xstart:xend] - data_dq = data_dq[ystart:yend,xstart:xend] + data_sci = data_sci[ystart:yend, xstart:xend] + data_err = data_err[ystart:yend, xstart:xend] + data_dq = data_dq[ystart:yend, xstart:xend] make1x3plot(scaling, colormaps, data_sci, data_err, data_dq, xstart, xend, ystart, yend, @@ -308,37 +304,9 @@ def display_image(filename, figsize, dpi) -def get_bunit(ext1header): - """ Get the brightness unit for the plot axis label. - - Parameters - ---------- - ext1header: Header - The extension 1 header of the fits file being displayed. This is the - extension that contains the brightness unit keyword. - - Returns - ------- - The string of the brightness unit for the axis label - {'counts', 'counts/s','e$^-$', 'e$^-$/s'} - - """ - units = ext1header['bunit'] - - if units == 'COUNTS': - return 'counts' - elif units == 'COUNTS/S': - return 'counts/s' - elif units == 'ELECTRONS': - return 'e$^-$' - elif units == 'ELECTRONS/S': - return 'e$^-$/s' - else: - return units - - def get_scale_limits(scaling, array, extname): - """ Get the scale limits to use for the image extension being displayed. + """ + Get the scale limits to use for the image extension being displayed. Parameters ---------- @@ -346,8 +314,8 @@ def get_scale_limits(scaling, array, extname): List of real numbers to act as scalings for the SCI, ERR, and DQ arrays. The first element in the list is for the SCI array the second is for the ERR array and the third element in the list is for the DQ extension. If - no scalings are given the default scaling will use - ginga.util.zscale.zscale(). All three scalings must be provided even if + no scalings are given the default scaling will use astropy.visualization + ZScaleInterval.get_limits(). All three scalings must be provided even if only changing 1-2 scalings. E.g. to change SCI array scaling: scaling = [(5E4,8E4),(None,None),(None,None)] @@ -366,29 +334,31 @@ def get_scale_limits(scaling, array, extname): The maximum value for the image scale. """ + + z = ZScaleInterval() if extname == 'DQ': - if scaling[0] == None and scaling[1] == None: + if scaling[0] is None and scaling[1] is None: z1, z2 = array.min(), array.max() - elif scaling[0] == None and scaling[1] != None: + elif scaling[0] is None and scaling[1] is not None: z1 = array.min() z2 = scaling[1] - elif scaling[0] != None and scaling[1] == None: + elif scaling[0] is not None and scaling[1] is None: z1 = scaling[0] z2 = array.max() - elif scaling[0] != None and scaling[1] != None: + elif scaling[0] is not None and scaling[1] is not None: z1 = scaling[0] z2 = scaling[1] - + elif extname == 'SCI' or extname == 'ERR': - if scaling[0] == None and scaling[1] == None: - z1, z2 = zscale.zscale(array) - elif scaling[0] == None and scaling[1] != None: - z1 = zscale.zscale(array)[0] + if scaling[0] is None and scaling[1] is None: + z1, z2 = z.get_limits(array) + elif scaling[0] is None and scaling[1] is not None: + z1 = z.get_limits(array)[0] z2 = scaling[1] - elif scaling[0] != None and scaling[1] == None: + elif scaling[0] is not None and scaling[1] is None: z1 = scaling[0] - z2 = zscale.zscale(array)[1] - elif scaling[0] != None and scaling[1] != None: + z2 = z.get_limits(array)[1] + elif scaling[0] is not None and scaling[1] is not None: z1 = scaling[0] z2 = scaling[1] else: @@ -401,7 +371,7 @@ def get_scale_limits(scaling, array, extname): def make1x3plot(scaling, colormaps, fullsci, fullerr, fulldq, xstart, xend, ystart, yend, detector, fname, h1, h2, h3, - figsize=(9,6), dpi=100): + figsize=(9, 6), dpi=100): """ Make a 3 column figure to display any WFC3 image or image section. Parameters @@ -410,8 +380,8 @@ def make1x3plot(scaling, colormaps, fullsci, fullerr, fulldq, List of real numbers to act as scalings for the SCI, ERR, and DQ arrays. The first element in the list is for the SCI array the second is for the ERR array and the third element in the list is for the DQ extension. If - no scalings are given the default scaling will use - ginga.util.zscale.zscale(). All three scalings must be provided even if + no scalings are given the default scaling will use astropy.visualization + ZScaleInterval.get_limits(). All three scalings must be provided even if only changing 1-2 scalings. E.g. to change SCI array scaling: scaling = [(5E4,8E4),(None,None),(None,None)] @@ -477,15 +447,15 @@ def make1x3plot(scaling, colormaps, fullsci, fullerr, fulldq, """ - z1_sci, z2_sci = get_scale_limits(scaling[0],fullsci,'SCI') - z1_err, z2_err = get_scale_limits(scaling[1],fullerr,'ERR') - z1_dq, z2_dq = get_scale_limits(scaling[2],fulldq,'DQ') + z1_sci, z2_sci = get_scale_limits(scaling[0], fullsci, 'SCI') + z1_err, z2_err = get_scale_limits(scaling[1], fullerr, 'ERR') + z1_dq, z2_dq = get_scale_limits(scaling[2], fulldq, 'DQ') - fig, [ax1,ax2,ax3] = plt.subplots(1,3,figsize=figsize,dpi=dpi) + fig, [ax1, ax2, ax3] = plt.subplots(1, 3, figsize=figsize, dpi=dpi) - im1 = ax1.imshow(fullsci,origin='lower',extent=(xstart,xend,ystart,yend),cmap=colormaps[0],vmin=z1_sci, vmax=z2_sci) - im2 = ax2.imshow(fullerr,origin='lower',extent=(xstart,xend,ystart,yend),cmap=colormaps[1],vmin=z1_err, vmax=z2_err) - im3 = ax3.imshow(fulldq, origin='lower',extent=(xstart,xend,ystart,yend),cmap=colormaps[2],vmin=z1_dq, vmax=z2_dq) + im1 = ax1.imshow(fullsci, origin='lower', extent=(xstart, xend, ystart, yend), cmap=colormaps[0], vmin=z1_sci, vmax=z2_sci) + im2 = ax2.imshow(fullerr, origin='lower', extent=(xstart, xend, ystart, yend), cmap=colormaps[1], vmin=z1_err, vmax=z2_err) + im3 = ax3.imshow(fulldq, origin='lower', extent=(xstart, xend, ystart, yend), cmap=colormaps[2], vmin=z1_dq, vmax=z2_dq) if len(fname) > 18: ax1.set_title(f"WFC3/{detector} {fname}\n{h1['extname']} ext") @@ -495,14 +465,15 @@ def make1x3plot(scaling, colormaps, fullsci, fullerr, fulldq, ax1.set_title(f"WFC3/{detector} {fname} {h1['extname']} ext") ax2.set_title(f"WFC3/{detector} {fname} {h2['extname']} ext") ax3.set_title(f"WFC3/{detector} {fname} {h3['extname']} ext") - fig.colorbar(im1, ax=ax1,shrink=.25,pad=.03) - fig.colorbar(im2, ax=ax2,shrink=.25,pad=.03) - fig.colorbar(im3, ax=ax3,shrink=.25,pad=.03) + fig.colorbar(im1, ax=ax1, shrink=.25, pad=.03) + fig.colorbar(im2, ax=ax2, shrink=.25, pad=.03) + fig.colorbar(im3, ax=ax3, shrink=.25, pad=.03) + def makeIR1x3plot(scaling, colormaps, data_sci, data_err, data_dq, xstart, xend, ystart, yend, detector, fname, h1, h2, h3, nsamps, ext, - figsize=(9,6), dpi=100): + figsize=(9, 6), dpi=100): """ Make a 3 column figure to display any WFC3 IMA image or image section. Parameters @@ -511,8 +482,8 @@ def makeIR1x3plot(scaling, colormaps, data_sci, data_err, data_dq, List of real numbers to act as scalings for the SCI, ERR, and DQ arrays. The first element in the list is for the SCI array the second is for the ERR array and the third element in the list is for the DQ extension. If - no scalings are given the default scaling will use - ginga.util.zscale.zscale(). All three scalings must be provided even if + no scalings are given the default scaling will use astropy.visualization + ZScaleInterval.get_limits(). All three scalings must be provided even if only changing 1-2 scalings. E.g. to change SCI array scaling: scaling = [(5E4,8E4),(None,None),(None,None)] @@ -584,17 +555,17 @@ def makeIR1x3plot(scaling, colormaps, data_sci, data_err, data_dq, """ - z1_sci, z2_sci = get_scale_limits(scaling[0],data_sci,'SCI') - z1_err, z2_err = get_scale_limits(scaling[1],data_err,'ERR') - z1_dq, z2_dq = get_scale_limits(scaling[2],data_dq,'DQ') - - fig, [ax1,ax2,ax3] = plt.subplots(1,3,figsize = figsize,dpi=dpi) - im1 = ax1.imshow(data_sci,origin='lower',extent=(xstart,xend,ystart,yend),cmap=colormaps[0],vmin=z1_sci, vmax=z2_sci) - im2 = ax2.imshow(data_err,origin='lower',extent=(xstart,xend,ystart,yend),cmap=colormaps[1],vmin=z1_err, vmax=z2_err) - im3 = ax3.imshow(data_dq, origin='lower',extent=(xstart,xend,ystart,yend),cmap=colormaps[2],vmin=z1_dq, vmax=z2_dq) - fig.colorbar(im1, ax=ax1,shrink=.25,pad=.03) - fig.colorbar(im2, ax=ax2,shrink=.25,pad=.03) - fig.colorbar(im3, ax=ax3,shrink=.25,pad=.03) + z1_sci, z2_sci = get_scale_limits(scaling[0], data_sci, 'SCI') + z1_err, z2_err = get_scale_limits(scaling[1], data_err, 'ERR') + z1_dq, z2_dq = get_scale_limits(scaling[2], data_dq, 'DQ') + + fig, [ax1, ax2, ax3] = plt.subplots(1, 3, figsize=figsize, dpi=dpi) + im1 = ax1.imshow(data_sci, origin='lower', extent=(xstart, xend, ystart, yend), cmap=colormaps[0], vmin=z1_sci, vmax=z2_sci) + im2 = ax2.imshow(data_err, origin='lower', extent=(xstart, xend, ystart, yend), cmap=colormaps[1], vmin=z1_err, vmax=z2_err) + im3 = ax3.imshow(data_dq, origin='lower', extent=(xstart, xend, ystart, yend), cmap=colormaps[2], vmin=z1_dq, vmax=z2_dq) + fig.colorbar(im1, ax=ax1, shrink=.25, pad=.03) + fig.colorbar(im2, ax=ax2, shrink=.25, pad=.03) + fig.colorbar(im3, ax=ax3, shrink=.25, pad=.03) if len(fname) > 18: ax1.set_title(f"WFC3/{detector} {fname}\n {h1['extname']} read {(nsamps+1)-ext}") diff --git a/notebooks/WFC3/exception_report/docs/rad_prof.py b/notebooks/WFC3/exception_report/docs/rad_prof.py index 24a5aaa05..970931b5a 100644 --- a/notebooks/WFC3/exception_report/docs/rad_prof.py +++ b/notebooks/WFC3/exception_report/docs/rad_prof.py @@ -40,10 +40,11 @@ import numpy as np import matplotlib.pyplot as plt -from photutils.centroids import centroid_com, centroid_1dg, centroid_2dg +from photutils.centroids import centroid_2dg from photutils.aperture import CircularAperture from scipy.optimize import curve_fit + class RadialProfile: """Main function to calulate radial profiles. @@ -139,29 +140,29 @@ def __init__(self, x, y, data, r=5, fit=True, recenter=False, self.fit_profile() # performs fit, updates self.fitted if show: self.show_profile(ax) - + def _create_profile(self): """Compute distances to pixels in cutout""" iY, iX = np.mgrid[self.sy, self.sx] # Pixel grid indices # extent = [sx.start, sx.stop-1, sy.start, sy.stop-1] - self.distances = np.sqrt( (iX - self.x) ** 2. - + (iY - self.y) ** 2. ).flatten() + self.distances = np.sqrt((iX - self.x) ** 2. + + (iY - self.y) ** 2.).flatten() self.values = self.cutout.flatten() - + def _setup_cutout(self, data): """Cuts out the aperture and defines slice objects. General setup procedure. """ self.ap = CircularAperture((self.x, self.y), r=self.r) mask = self.ap.to_mask() - self.sy = slice(mask.bbox.iymin,mask.bbox.iymax,None) - self.sx = slice(mask.bbox.ixmin,mask.bbox.ixmax,None) + self.sy = slice(mask.bbox.iymin, mask.bbox.iymax, None) + self.sx = slice(mask.bbox.ixmin, mask.bbox.ixmax, None) self.cutout = mask.cutout(data, fill_value=np.nan) if self.cutout is None: self.is_empty = True - + def fit_profile(self): """Fits 1d Moffat function to measured radial profile. Fits a moffat profile to the distance and values of the pixels. @@ -171,11 +172,11 @@ def fit_profile(self): amp0 = np.amax(self.values) bias0 = np.nanmedian(self.values) best_vals, covar = curve_fit(RadialProfile.profile_model, - self.distances, - self.values, - p0 = [amp0, 1.5, 1.5, bias0], - bounds = ([0., .3, .5, 0], - [np.inf, 10., 10., np.inf])) + self.distances, + self.values, + p0=[amp0, 1.5, 1.5, bias0], + bounds=([0., .3, .5, 0], + [np.inf, 10., 10., np.inf])) hwhm = best_vals[1] * np.sqrt(2. ** (1./best_vals[2]) - 1.) self.fwhm = 2 * hwhm self.amp, self.gamma, self.alpha, self.bias = best_vals @@ -188,7 +189,7 @@ def fit_profile(self): self.fwhm = np.nan self.fitted = False self.chisquared = np.nan - + @staticmethod def profile_model(r, amp, gamma, alpha, bias): """Returns 1D Moffat profile evaluated at r values. @@ -219,7 +220,7 @@ def profile_model(r, amp, gamma, alpha, bias): """ model = amp * (1. + (r / gamma) ** 2.) ** (-1. * alpha) + bias return model - + def recenter_source(self, data): """Recenters source position in cutout and updates x,y attributes""" @@ -244,7 +245,7 @@ def recenter_source(self, data): self.x = xg1 + self.sx.start self.y = yg1 + self.sy.start self._setup_cutout(data) - + def show_profile(self, ax=None, show_fit=True): """Makes plot of radial profile. @@ -274,24 +275,22 @@ def show_profile(self, ax=None, show_fit=True): fig = plt.figure(dpi=110) ax = fig.add_subplot(111) - ax.scatter(self.distances, self.values, alpha=.5,s=3) - min_y = np.amin(self.values[self.values >0.])/2. - #ax.set_ylim(min_y, np.nanmax(self.values)*2.) + ax.scatter(self.distances, self.values, alpha=.5, s=3) ax.set_ylim(0.1, np.nanmax(self.values)*2.) ax.set_xlim(0.) ax.set_yscale('log') - ax.set_ylabel('Pixel Value',size=13) - ax.set_xlabel('Distance from centroid [pix]',size=13) + ax.set_ylabel('Pixel Value', size=13) + ax.set_xlabel('Distance from centroid [pix]', size=13) if self.fitted and show_fit: - tmp_r = np.arange(0,np.ceil(np.amax(self.distances)),.1) + tmp_r = np.arange(0, np.ceil(np.amax(self.distances)), .1) model_fit = RadialProfile.profile_model(tmp_r, self.amp, self.gamma, self.alpha, self.bias) - label = r'$\gamma$= {}, $\alpha$ = {}'.format(round(self.gamma,2), - round(self.alpha,2)) + label = r'$\gamma$= {}, $\alpha$ = {}'.format(round(self.gamma, 2), + round(self.alpha, 2)) label += '\nFWHM = {}'.format(round(self.fwhm, 2)) - ax.plot(tmp_r, model_fit, label=label,color='k') - ax.legend(loc=1,prop={'size':13}) + ax.plot(tmp_r, model_fit, label=label, color='k') + ax.legend(loc=1, prop={'size': 13}) return ax diff --git a/notebooks/WFC3/exception_report/requirements.txt b/notebooks/WFC3/exception_report/requirements.txt new file mode 100644 index 000000000..26d1dafa3 --- /dev/null +++ b/notebooks/WFC3/exception_report/requirements.txt @@ -0,0 +1,7 @@ +astropy +astroquery +jupyter +matplotlib +numpy +photutils +scipy diff --git a/notebooks/WFC3/exception_report/wfc3_exception_report.ipynb b/notebooks/WFC3/exception_report/wfc3_exception_report.ipynb index 56efe0fd5..c1089bb4a 100755 --- a/notebooks/WFC3/exception_report/wfc3_exception_report.ipynb +++ b/notebooks/WFC3/exception_report/wfc3_exception_report.ipynb @@ -116,18 +116,22 @@ "source": [ "## 1. Imports \n", "\n", - "Installation instructions for this notebook are in a `README.md` attached to the repository.
\n", + "
This notebook assumes you have created and activated a virtual environment using the requirements file in this notebook's repository.
\n", + "\n", "Please make sure you have read the contents of the `README.md` before continuing the notebook\n", " \n", - "We import: \n", + "We import:
\n", "\n", - "- *glob* to make lists of files\n", - "- *os* to name files and remove directories \n", - "- *astropy.io.fits* for accessing FITS files\n", - "- *astropy.table Table* for creating tidy tables of the data\n", - "- *astroquery.mast.Observations* for downloading data from MAST\n", - "- *matplotlib.pyplot* for plotting data\n", - "- *display_image* for displaying any type of WFC3 image" + "| Package Name | Purpose |\n", + "|:-------------------------------|:--------------------------------------|\n", + "| `glob` | creating list of files |\n", + "| `os` | setting environment variables |\n", + "| `shutil` | direcotry clean up |\n", + "| `astropy.io.fits` | opening and modifying fits files |\n", + "| `astroquery.mast.Observations` | downloading data from MAST |\n", + "| `astropy.table.Table` | creating and manipulating data tables |\n", + "| `matplotlib.pyplot` | plotting and displaying images |\n", + "| `docs.display_image` | for displaying any type of WFC3 image |" ] }, { @@ -143,13 +147,14 @@ "%matplotlib inline\n", "import glob \n", "import os\n", + "import shutil\n", "\n", "from astropy.io import fits\n", "from astropy.table import Table\n", "from astroquery.mast import Observations\n", "import matplotlib.pyplot as plt\n", "\n", - "from docs.display_image import display_image\n" + "from docs.display_image import display_image" ] }, { @@ -184,22 +189,23 @@ "metadata": {}, "outputs": [], "source": [ - "exp_ids = ['IEPP01010'] # Edit with exposure ID(s)\n", + "# Edit with exposure ID(s)\n", + "exp_ids = ['IEPP01010'] \n", "\n", "# Specify flle types to download\n", - "file_types = ['FLT', 'JIF','JIT']\n", + "file_types = ['FLT', 'JIF', 'JIT']\n", "\n", - "#loop through exposure id\n", + "# Loop through exposure id\n", "for obsid in exp_ids: \n", " # make new directory to hold fits files - named by exposure id\n", + " newdir = os.path.join(os.getcwd(), obsid.lower())\n", " try:\n", - " newdir = os.getcwd()+'/'+ obsid.lower()+'/'\n", " mkdir = os.mkdir(newdir)\n", " print(f'Making new directory {newdir}')\n", " except FileExistsError: \n", - " pass\n", + " print(f'Directory {newdir} already exists.')\n", " \n", - " # loop through to get FLTs, JIFs, and JITs\n", + " # Loop through to get FLTs, JIFs, and JITs\n", " for file_type in file_types:\n", " print(f'Working on getting {file_type} files for Exposure ID {obsid}')\n", " obs_table = Observations.query_criteria(obs_id=obsid.lower())\n", @@ -209,22 +215,25 @@ " \n", " # For convenience move raws to cwd and remove empty download dir\n", " for file in download_table['Local Path']:\n", - " filename = file.split('/')[-1]\n", - " print(f'Moving {file} to {newdir+filename}')\n", - " os.rename(file, newdir+filename)\n", + " filename = os.path.basename(file)\n", + " new_file_path = os.path.join(newdir, filename)\n", + " print(f'Moving {file} to {new_file_path}')\n", + " os.rename(file, new_file_path)\n", + " remove_dir = os.path.join('mastDownload', 'HST', filename[:9]) \n", + "\n", " try:\n", - " os.rmdir(f'mastDownload/HST/{filename[:9]}')\n", - " print(f'Removing mastDownload/HST/{filename[:9]}') \n", - " os.rmdir(f'mastDownload/HST/{obsid.lower()}')\n", - " print(f'Remvoing mastDownload/HST/{obsid.lower()}') \n", + " os.rmdir(remove_dir)\n", + " print(f'Removing {remove_dir}')\n", " except (OSError, FileNotFoundError): \n", - " pass\n", - " \n", - " print(f'Remvoing mastDownload/HST/') \n", - " os.rmdir(f'mastDownload/HST/')\n", - " print(f'Remvoing mastDownload/')\n", - " os.rmdir('mastDownload/')\n", - " " + " print(f'Error removing directory {remove_dir}')\n", + "\n", + " mast_dir = 'mastDownload'\n", + " # Check and remove mastDownload directory\n", + " if os.path.exists(mast_dir):\n", + " print(f'Removing {mast_dir} directory')\n", + " shutil.rmtree(mast_dir)\n", + " else:\n", + " print(f'{mast_dir} does not exist')" ] }, { @@ -256,16 +265,6 @@ "recalibration. " ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# only run this cell if you would like to see the docscring for the function\n", - "display_image?" - ] - }, { "cell_type": "code", "execution_count": null, @@ -279,12 +278,12 @@ "\n", "for f in fltfiles:\n", " display_image(f,\n", - " colormaps=['Greys_r', 'Greys_r', 'inferno_r'],\n", - " scaling=[(-10, 130), (None, None), (None, None)],\n", - " printmeta=True,\n", - " ima_multiread=False,\n", - " figsize=(16, 16),\n", - " dpi=150)" + " colormaps=['Greys_r', 'Greys_r', 'inferno_r'],\n", + " scaling=[(-10, 130), (None, None), (None, None)],\n", + " printmeta=True,\n", + " ima_multiread=False,\n", + " figsize=(16, 16),\n", + " dpi=150)" ] }, { @@ -315,8 +314,9 @@ "metadata": {}, "outputs": [], "source": [ - "jif_file = f'{exp_ids[0].lower()}/{exp_ids[0].lower()}_jif.fits' # Edit with the path to your own _jif.fits file\n", - "fits.getheader(jif_file,0)[-21:-6]" + "# Edit with the path to your own _jif.fits file\n", + "jif_file = f'{exp_ids[0].lower()}/{exp_ids[0].lower()}_jif.fits' \n", + "fits.getheader(jif_file, 0)[-21:-6]" ] }, { @@ -333,7 +333,7 @@ "metadata": {}, "outputs": [], "source": [ - "fits.getheader(jif_file,0)['T_GSFAIL*']" + "fits.getheader(jif_file, 0)['T_GSFAIL*']" ] }, { @@ -350,26 +350,26 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "tags": [] - }, + "metadata": {}, "outputs": [], "source": [ - "numexts = fits.getheader(jif_file,0)['NEXTEND'] # number of extensions i.e. exposures\n", + "numexts = fits.getheader(jif_file, 0)['NEXTEND'] # number of extensions i.e. exposures\n", "\n", - "keywords = ['EXPNAME*','GUIDEACT*','GSACQ*','ACTGSSEP*',\n", - " 'GSSEPRMS*','NLOSSES*','CRVAL1*','CRVAL2*',\n", - " 'V2_RMS*','V3_RMS*','GSFAIL*']\n", + "keywords = ['EXPNAME*', 'GUIDEACT*', 'GSACQ*', 'ACTGSSEP*',\n", + " 'GSSEPRMS*', ' NLOSSES*', 'CRVAL1*', 'CRVAL2*',\n", + " 'V2_RMS*', 'V3_RMS*', 'GSFAIL*']\n", "\n", - "for ext in range(1,numexts+1):\n", - " print(\"JIF Header Ext Number:\",ext)\n", + "for ext in range(1, numexts+1):\n", + " print(\"JIF Header Ext Number:\", ext)\n", " print('-'*80)\n", + " header = fits.getheader(jif_file, ext)\n", " for keyword in keywords:\n", " # try to display keyword because it may not be present \n", - " try: \n", - " print(fits.getheader(jif_file,ext)[keyword])\n", - " except KeyError: \n", - " pass\n", + " details = header.get(keyword)\n", + " if details is not None:\n", + " print(f'{details}')\n", + " else:\n", + " print(f'Keyword {keyword} not found in extension {ext}')\n", " print('\\n')" ] }, @@ -410,29 +410,28 @@ "outputs": [], "source": [ "jit_file = f'{exp_ids[0].lower()}/{exp_ids[0].lower()}_jit.fits' # Edit with the path to your own _jit.fits file\n", - "numexts = fits.getheader(jit_file,0)['NEXTEND'] # number of extensions i.e. exposures\n", + "numexts = fits.getheader(jit_file, 0)['NEXTEND'] # number of extensions i.e. exposures\n", "\n", - "figure_size = (7,5) # Edit if you want to chage the figure size\n", + "figure_size = (7, 5) # Edit if you want to chage the figure size\n", "dotsperinch = 115 # Edit if you want to change the figure resolution\n", "\n", - "for ext in range(1,numexts+1):\n", + "for ext in range(1, numexts+1):\n", "\n", - " jit_tbl = Table(fits.getdata(jit_file,ext))\n", - " expname = fits.getheader(jit_file,ext)['EXPNAME']\n", + " jit_tbl = Table(fits.getdata(jit_file, ext))\n", + " expname = fits.getheader(jit_file, ext)['EXPNAME']\n", " flt_file = glob.glob(f\"{exp_ids[0].lower()}/{expname[:8]}*flt.fits\")\n", " \n", " plt.figure(figsize=figure_size, dpi=dotsperinch)\n", " plt.grid(alpha=0.5)\n", - " plt.scatter(jit_tbl['Seconds'],jit_tbl['SI_V2_AVG'],15,alpha=.5,marker='o',label='V2_AVG')\n", - " plt.scatter(jit_tbl['Seconds'],jit_tbl['SI_V3_AVG'],15,alpha=.5,marker='o',label='V3_AVG')\n", - " plt.scatter(jit_tbl['Seconds'],jit_tbl['SI_V2_RMS'],10,alpha=.5,marker='s',label='V2_RMS')\n", - " plt.scatter(jit_tbl['Seconds'],jit_tbl['SI_V3_RMS'],10,alpha=.5,marker='s',label='V3_RMS')\n", - " \n", + " plt.scatter(jit_tbl['Seconds'], jit_tbl['SI_V2_AVG'], 15, alpha=.5, marker='o', label='V2_AVG')\n", + " plt.scatter(jit_tbl['Seconds'], jit_tbl['SI_V3_AVG'], 15, alpha=.5, marker='o', label='V3_AVG')\n", + " plt.scatter(jit_tbl['Seconds'], jit_tbl['SI_V2_RMS'], 10, alpha=.5, marker='s', label='V2_RMS')\n", + " plt.scatter(jit_tbl['Seconds'], jit_tbl['SI_V3_RMS'], 10, alpha=.5, marker='s', label='V3_RMS')\n", " \n", - " plt.xlabel('Exposure Time [Seconds]',size=13)\n", - " plt.ylabel('Coordinate Axis [Arcsec]',size=13)\n", - " plt.title(f\"Jitter File Ext Number: {ext}\\n Corresponding FLT: {flt_file[0].split('/')[-1]}\",size=14)\n", - " plt.legend(prop={'size':12},ncol=2)\n", + " plt.xlabel('Exposure Time [Seconds]', size=13)\n", + " plt.ylabel('Coordinate Axis [Arcsec]', size=13)\n", + " plt.title(f\"Jitter File Ext Number: {ext}\\n Corresponding FLT: {flt_file[0].split('/')[-1]}\", size=14)\n", + " plt.legend(prop={'size': 12}, ncol=2)\n", " plt.minorticks_on()" ] }, @@ -486,12 +485,17 @@ "Below, we show an example of searching for sources with `photutils.detection.DAOStarFinder`
\n", "in a 100x100 pixel subsection and subsequently plotting their radial profiles using the `RadialProfile`
class within the file `rad_prof.py`. \n", "\n", - "We also import:\n", - "- *numpy* for handling arrays\n", - "- *astropy.stats.sigma_clipped_stats* for sigma clipping statistics\n", - "- *ginga.util.zscale* for scaling images\n", - "- *matplotlib.colors.LogNorm* for logarithmic normalization\n", - "- *photutils.detection.CircularAperture* for aperture photometry" + "We also import:
\n", + "\n", + "| Package Name | Purpose |\n", + "|:---------------------------------------|:---------------------------|\n", + "| `numpy` | handling arrays |\n", + "| `astropy.stats.sigma_clipped_stats` | sigma clipping statistics |\n", + "| `astropy.visualization.ZScaleInterval` | z-scaling images |\n", + "| `matplotlib.colors.LogNorm` | logarithmic normalization |\n", + "| `photutils.detection.CircularAperture` | aperture photometry |\n", + "| `photutils.detection.DAOStarFinder` | point source detection |\n", + "| `docs.rad_prof.RadialProfile` | generating radial profiles |" ] }, { @@ -500,11 +504,10 @@ "metadata": {}, "outputs": [], "source": [ - "import numpy as np\n", - "\n", "from astropy.stats import sigma_clipped_stats\n", - "from ginga.util import zscale\n", + "from astropy.visualization import ZScaleInterval\n", "from matplotlib.colors import LogNorm\n", + "import numpy as np\n", "from photutils.aperture import CircularAperture\n", "from photutils.detection import DAOStarFinder\n", "\n", @@ -532,11 +535,11 @@ "source": [ "# Read in data\n", "filename = 'iepp01010/iepp01uvq_flt.fits'\n", - "uvis2 = fits.getdata(filename,'SCI',1)\n", + "uvis2 = fits.getdata(filename, 'SCI', 1)\n", "header = fits.getheader(filename)\n", "\n", "# Trim data to 100x100 subsection\n", - "data = uvis2[:100,65:165]\n", + "data = uvis2[:100, 65:165]\n", "\n", "# 3 sigma clip data to get median and std values\n", "mean, median, std = sigma_clipped_stats(data, sigma=3.0) \n", @@ -546,23 +549,24 @@ "sources = daofind(data - median) \n", "\n", "# Truncate list to show just a few sources \n", - "sources = sources[(sources['flux'] > 10) &\\\n", - " (sources['xcentroid'] > 10) & (sources['xcentroid'] < 90) &\\\n", - " (sources['ycentroid'] > 18) & (sources['ycentroid'] < 90) ]\n", + "sources = sources[(sources['flux'] > 10) &\n", + " (sources['xcentroid'] > 10) & (sources['xcentroid'] < 90) &\n", + " (sources['ycentroid'] > 18) & (sources['ycentroid'] < 90)]\n", "\n", "# Create circular apertures to plot\n", - "positions = np.transpose((sources['xcentroid'],sources['ycentroid']))\n", + "positions = np.transpose((sources['xcentroid'], sources['ycentroid']))\n", "apertures = CircularAperture(positions, r=5.)\n", "\n", "# Get zscale image min and max limits\n", - "z1,z2 = zscale.zscale(data)\n", + "z = ZScaleInterval()\n", + "z1, z2 = z.get_limits(data)\n", "\n", "# Plot 100x100 subsection and apertures\n", - "plt.figure(figsize=(15,10))\n", - "im1 = plt.imshow(data-z1+.01, origin='lower', cmap='Greys', norm = LogNorm(vmin=.01, vmax=z2*100.-z1) )\n", + "plt.figure(figsize=(15, 10))\n", + "im1 = plt.imshow(data-z1+.01, origin='lower', cmap='Greys', norm=LogNorm(vmin=.01, vmax=z2*100.-z1))\n", "apertures.plot(color='red', lw=1.5, alpha=0.5)\n", - "plt.title(filename,size=14)\n", - "plt.colorbar(im1,pad=0.01)\n" + "plt.title(filename, size=14)\n", + "plt.colorbar(im1, pad=0.01)" ] }, { @@ -584,17 +588,17 @@ "outputs": [], "source": [ "# Loop through sources and plot star stamp next to corresponding radial profile plot\n", - "for xy in zip(sources['xcentroid'],sources['ycentroid']):\n", + "for xy in zip(sources['xcentroid'], sources['ycentroid']):\n", " \n", - " fig, [ax1,ax2] = plt.subplots(1,2,figsize=(11,5))\n", + " fig, [ax1, ax2] = plt.subplots(1, 2, figsize=(11, 5))\n", " \n", " # Calculate radial profile and plot on ax2\n", - " my_prof = RadialProfile(xy[0],xy[1],data,\n", - " r=5,\n", - " fit=True,\n", - " recenter=True,\n", - " show=True,\n", - " ax=ax2)\n", + " my_prof = RadialProfile(xy[0], xy[1], data,\n", + " r=5,\n", + " fit=True,\n", + " recenter=True,\n", + " show=True,\n", + " ax=ax2)\n", " \n", " # Create boundaries for stamp \n", " x1 = int(round(my_prof.x-7))\n", @@ -603,10 +607,10 @@ " y2 = int(round(my_prof.y+7))\n", " \n", " # Plot star stamp \n", - " im1 = ax1.imshow(data[y1:y2,x1:x2]-z1+.01, origin='lower', cmap='Greys', extent= [x1,x2,y1,y2],norm = LogNorm(vmin=.01, vmax=z2*100.-z1) )\n", + " im1 = ax1.imshow(data[y1:y2, x1:x2]-z1+.01, origin='lower', cmap='Greys', extent=[x1, x2, y1, y2], norm=LogNorm(vmin=.01, vmax=z2*100.-z1))\n", "\n", - " ax1.set_title(f\"x = {my_prof.x:.3f}, y = {my_prof.y:.3f}\",size=13)\n", - " ax2.set_title(header['filter'],size=13)\n", + " ax1.set_title(f\"x = {my_prof.x:.3f}, y = {my_prof.y:.3f}\", size=13)\n", + " ax2.set_title(header['filter'], size=13)\n", " ax2.grid(alpha=0.5)\n", " fig.tight_layout()" ] @@ -671,19 +675,20 @@ "\n", "## About this Notebook \n", "\n", - "**Author:** Benjamin Kuhn, WFC3 Instrument\n", - "\n", - "**Updated On:** January 20, 2023\n", + "**Author:** Benjamin Kuhn, WFC3 Instrument
\n", + "**Updated On:** November 21, 2023\n", "\n", "## Citations \n", "\n", "If you use Python packages for published research, please cite the authors. Follow these links for more
\n", - "information about citing packages such as `astropy`, `astroquery`, `matplotlib`, or `photutils`:\n", + "information about citing packages such as `astropy`, `astroquery`, `matplotlib`, `photutils`, etc.:\n", "\n", "* [Citing `astropy`](https://www.astropy.org/acknowledging.html)\n", "* [Citing `astroquery`](https://github.com/astropy/astroquery/blob/main/astroquery/CITATION)\n", "* [Citing `matplotlib`](https://matplotlib.org/stable/users/project/citing.html)\n", + "* [Citing `numpy`](https://numpy.org/citing-numpy/)\n", "* [Citing `photutils`](https://photutils.readthedocs.io/en/stable/citation.html)\n", + "* [Citing `scipy`](https://scipy.org/citing-scipy/)\n", "
\n", "***" ] @@ -699,7 +704,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -713,7 +718,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.5" + "version": "3.11.5" } }, "nbformat": 4,