Skip to content

Commit 461c767

Browse files
committed
Convert to myst markdown
1 parent bdb19bd commit 461c767

1 file changed

Lines changed: 52 additions & 48 deletions

File tree

tutorials/spherex/spherex_sdt/sdt_irsa.md

Lines changed: 52 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
---
2-
jupyter:
3-
jupytext:
4-
text_representation:
5-
extension: .md
6-
format_name: markdown
7-
format_version: '1.3'
8-
jupytext_version: 1.19.1
9-
kernelspec:
10-
display_name: std_conda
11-
language: python
12-
name: std_conda
2+
jupytext:
3+
text_representation:
4+
extension: .md
5+
format_name: myst
6+
format_version: 0.13
7+
jupytext_version: 1.18.1
8+
kernelspec:
9+
display_name: std_conda
10+
language: python
11+
name: std_conda
1312
---
1413

1514
# SPHEREx Source Discovery Tool IRSA Demo
@@ -27,6 +26,7 @@ This notebook demonstrates how to use it.
2726

2827
* Select sources interactively and extract their SPHEREx spectra
2928

29+
+++
3030

3131
## 2. SPHEREx Overview
3232

@@ -42,9 +42,11 @@ The community will also mine SPHEREx data and combine it with synergistic data s
4242

4343
More information is available in the [SPHEREx Explanatory Supplement](https://irsa.ipac.caltech.edu/data/SPHEREx/docs/SPHEREx_Expsupp_QR.pdf).
4444

45+
+++
4546

4647
## 3. Requirements
4748

49+
+++
4850

4951
### 3.1 On local machines
5052

@@ -84,10 +86,11 @@ Note that we use the `--user` option here, which will keep the environment avail
8486

8587
To use the environment in your Jupyter Lab notebooks on Fornax, directly select the environment `sdt_env` in your Jupyter Notebook using the dropdown on the upper left.
8688

89+
+++
8790

8891
## 4. Imports
8992

90-
```python
93+
```{code-cell} ipython3
9194
# Standard library imports
9295
import concurrent.futures
9396
import copy
@@ -125,7 +128,7 @@ from spx_sdt.sdt_utils import (
125128
from spx_sdt.source_extraction import run_sextractor, get_sextractor_file
126129
```
127130

128-
```python
131+
```{code-cell} ipython3
129132
# Suppress logging temporarily to prevent astropy
130133
# from repeatedly printing out warning notices related to alternate WCSs
131134
import logging
@@ -147,7 +150,7 @@ The query parameters below are valid as of **February 2026**, but will need to b
147150
Specifically, the ephemeral query parameters _will_ change for future SPHEREx data releases.
148151
```
149152

150-
```python
153+
```{code-cell} ipython3
151154
# General
152155
data_release = "qr2"
153156
@@ -165,20 +168,21 @@ sapm_s3 = [
165168

166169
## 6. Search for SPHEREx Spectral Images
167170

171+
+++
168172

169173
We search first search for all the available SPHEREx images around a given position on the sky.
170174

171175
Here, we choose the random position at R.A. = 150.7817877 degrees and Decl. = 3.3502204 degrees, which is close to the COSMOS field. We also choose a search radius of 50 arc-seconds.
172176

173-
```python
177+
```{code-cell} ipython3
174178
coord = SkyCoord(150.7817877, 3.3502204, unit='deg')
175179
search_radius = 50 * u.arcsec
176180
```
177181

178182
We then search for the SPHEREx spectral images that overlap with the position in the search radius defined above.
179183
For this we use the [IRSA module in astroquery](https://astroquery.readthedocs.io/en/latest/ipac/irsa/irsa.html) and the Simple Image Access (SIA) API.
180184

181-
```python
185+
```{code-cell} ipython3
182186
results = Irsa.query_sia(pos=(coord, search_radius), collection=sia_collection)
183187
results_summary(results)
184188
```
@@ -194,7 +198,7 @@ The IRSA SIA collections can be listed using using the ``list_collections`` meth
194198

195199
+++
196200

197-
201+
+++
198202

199203
Next, we set up the _Firefly_ client and use it to visualize one image with the search coordinates (and search radius) indicated.
200204

@@ -212,7 +216,7 @@ If you open _Firefly_ in a new Jupyter Notebook tab, you can drag the _Firefly_
212216

213217
Below, we give the two options to open _Firefly_.
214218

215-
```python
219+
```{code-cell} ipython3
216220
# Set up Firefly client to open in a new browser tab
217221
#fc = FireflyClient.make_client(url="https://irsa.ipac.caltech.edu/irsaviewer")
218222
@@ -235,7 +239,7 @@ We can use two different methods to obtain the images:
235239

236240
We show both methods below, the first one for the detector 2 spectral image and the second one for the detector 4 spectral image.
237241

238-
```python
242+
```{code-cell} ipython3
239243
# Using a boolean mask
240244
d2_result = results[results['energy_bandpassname'] == 'SPHEREx-D2'][0] # first D2 image
241245
@@ -251,7 +255,7 @@ Next, we download the two SPHEREx spectral images. We download them directly fro
251255

252256
For this, we first define a handy function to obtain the S3 cloud path.
253257

254-
```python
258+
```{code-cell} ipython3
255259
# Function to access data from cloud
256260
def get_s3_fpath(cloud_access):
257261
cloud_info = json.loads(cloud_access) # converts str to dict
@@ -262,7 +266,7 @@ def get_s3_fpath(cloud_access):
262266

263267
Next we download the images.
264268

265-
```python
269+
```{code-cell} ipython3
266270
# HDUs for D2 image
267271
s3_fpath_d2 = get_s3_fpath(d2_result['cloud_access'])
268272
with fits.open(f's3://{s3_fpath_d2}', fsspec_kwargs={"anon": True}) as hdul2:
@@ -290,6 +294,7 @@ If you want to download the SPHEREx spectral images from IPAC directly (not from
290294
291295
```
292296

297+
+++
293298

294299
## 8. Remove Local Background, Create Masks, and Reproject
295300

@@ -307,8 +312,7 @@ You can find more calibration products for the SPHEREx spectral images on the S3
307312
308313
```
309314

310-
311-
```python
315+
```{code-cell} ipython3
312316
s3_fpath_sapm2 = sapm_s3[1] # list idx 1 is value for D2 path
313317
with fits.open(f's3://{s3_fpath_sapm2}', fsspec_kwargs={"anon": True}) as hdul_sapm2:
314318
sapm_data2 = hdul_sapm2["IMAGE"].data
@@ -322,7 +326,7 @@ with fits.open(f"s3://{s3_fpath_sapm4}", fsspec_kwargs={"anon": True}) as hdul_s
322326

323327
After downloading the solid angle pixel map calibration products for each detector, we convert the images from brightness (MJy/sr) to spectral flux density ($\mu$Jy) and update the corresponding units in the headers of the imates.
324328

325-
```python
329+
```{code-cell} ipython3
326330
print(f"Spectral image BUNIT: {img_hdr2['BUNIT']}")
327331
print(f"Solid angle pixel map BUNIT: {sapm_hdr2['BUNIT']}")
328332
@@ -345,7 +349,7 @@ Next, we remove the background from both images. We estimate the background usin
345349
before feeding it to `sep`.
346350
```
347351

348-
```python
352+
```{code-cell} ipython3
349353
bkg2 = sep.Background(img_data2.astype(img_data2.dtype.newbyteorder("=")), bw=64, bh=64, fw=11, fh=11)
350354
img_data2 = img_data2 - bkg2
351355
@@ -359,7 +363,7 @@ Note that we ignore the `OVERFLOW` flag, which is coincident with the `SUR_ERROR
359363
We therefore choose these as good pixels and flag the other ones.
360364
For simplicity, we set the bad pixels to `np.nan`.
361365

362-
```python
366+
```{code-cell} ipython3
363367
mask2 = np.zeros(flags_data2.shape) * np.nan
364368
mask2[(flags_data2 == 2097152) | (flags_data2 == 2) | (flags_data2 == 0)] = 1
365369
@@ -374,7 +378,7 @@ Finally, we reproject the images and masks to the same pixel grid. We reproject
374378
- Due to the bilinear method applied in `reproject_interp`, single-pixel NaNs get conservatively extended into multi-pixel NaN regions.
375379
```
376380

377-
```python
381+
```{code-cell} ipython3
378382
img_data2_reproj, footprint = reproject_exact(input_data=(img_data2, img_hdr2), output_projection=img_hdr4)
379383
footprint[footprint == 0] = np.nan # Set zero values to NaN
380384
mask2_reproj, _ = reproject_interp(input_data=(mask2, img_hdr2), output_projection=img_hdr4)
@@ -386,7 +390,7 @@ We use _bokeh_ to visualize the two detector images as well as the reprojected D
386390
The initial bokeh plot has a ~20 second rendering penalty due to static asset loading. Subsequent bokeh plots should take less time to display.
387391
```
388392

389-
```python
393+
```{code-cell} ipython3
390394
# Plot data
391395
plot1_info = {
392396
"img_data": img_data4,
@@ -409,15 +413,17 @@ plot_overlap_trio(plot1_info, plot2_info, plot3_info, clip=True)
409413

410414
## 9. Generate Cutouts and Subtracted Color Image
411415

416+
+++
412417

413418
For this example, we only use parts of the image. For this, we first generate cutouts of the D4 image as well as the reprojected D2 image.
414419

420+
+++
415421

416422
Next, we subtract the images. For keeping track of the wavelength and file names, we create two handy dictionaries.
417423

418424
Finally, we use _bokeh_ to show interactive representation of the two images as well as the subtracted image _inside_ this Jupyter Notebook.
419425

420-
```python
426+
```{code-cell} ipython3
421427
# Set up data directory and cutout paths
422428
data_dir = './data/'
423429
if not os.path.exists(data_dir):
@@ -443,7 +449,7 @@ cut4_hdu.writeto(cut_path4, overwrite=True)
443449
cut4_mask = Cutout2D(mask4, position=coord, size=(150, 150), wcs=WCS(img_hdu4.header))
444450
```
445451

446-
```python
452+
```{code-cell} ipython3
447453
d2_range = get_lambda_range(d2_result)
448454
d2_info = {
449455
"img_data": cut2.data,
@@ -479,7 +485,7 @@ A use case could be to search for red sources such as galaxies with an Active Ga
479485

480486
In the following, we run [<code>SExtractor</code>](https://sextractor.readthedocs.io/en/latest/Introduction.html) on all the images (reprojected D2, D4, and subtracted image) to identify sources. Before that, however, we have to save the subtracted image to disk (note that the D2 and D4 cutouts are already saved to disk).
481487

482-
```python
488+
```{code-cell} ipython3
483489
# Save subtracted image to fits file for source extraction
484490
subtracted_cut_path = os.path.join(data_dir , f"subtracted_{get_exp_id(img_hdu2)}_{get_exp_id(img_hdu4)}.fits")
485491
sub_hdu = copy.deepcopy(cut2_hdu) # make copy to avoid modifying original
@@ -490,7 +496,7 @@ sub_hdu.writeto(subtracted_cut_path, overwrite=True)
490496
Now, we run <code>SExtractor</code>. This software needs a few parameter files such as a configuration and parameter files as well as some others (for example a convolution filter). These files are provided in the `spx_sdt` directory. For <code>SExtractor</code> to find them, we have to give it absolute path names to these files. We use `os.path.realpath()` in the following to translate relative paths to absolute paths.
491497
Finally, we run <code>SExtractor</code> on the last lines.
492498

493-
```python
499+
```{code-cell} ipython3
494500
sxt_config = get_sextractor_file("default_sdt.sex", package="spx_sdt")
495501
sxt_params = get_sextractor_file("default_sdt.param", package="spx_sdt")
496502
sxt_nnw = get_sextractor_file("default.nnw", package="spx_sdt")
@@ -511,7 +517,7 @@ In the following, we show two ways to visualize the extracted sources on the ima
511517

512518
First, we read in the <code>SExtractor</code> catalog. Note that we want to change `ALPHA_J2000` and `DELTA_J2000` to the more universal `ra` and `dec` column names. This is especially important for Firefly to be able to read the table out of the box and mark the source in the table on the images.
513519

514-
```python
520+
```{code-cell} ipython3
515521
extracted = Table.read(subtracted_cat , format="ascii")
516522
extracted.rename_column("ALPHA_J2000", "ra")
517523
extracted.rename_column("DELTA_J2000", "dec")
@@ -526,13 +532,13 @@ First, we demonstrate how we can use _bokeh_ to create an interactive dashboard
526532
Currently, _bokeh_ tables cannot be displayed in Fornax. Therefore the example below will not work. The user is encouraged to use _Firefly_, which use is demonstrated in Section 10.2 below.
527533
```
528534

529-
```python
535+
```{code-cell} ipython3
530536
plot_apertures(subtracted_cut_path, source_tab=extracted, label="Extracted Sources", clip=True)
531537
```
532538

533539
Alternatively, the sources can be easily selected in Python itself by defining a selection function.
534540

535-
```python
541+
```{code-cell} ipython3
536542
# Create custom selection function
537543
def select_sources(sxt_tab):
538544
"""Selects the 25 brightest sources from the SExtractor-generated catalog.
@@ -563,7 +569,7 @@ plot_apertures(subtracted_cut_path, source_tab=selected, label="25 Brightest Sou
563569
Alternatively to _bokeh_, we can use the _Firefly_ application to show the image including the data table. First we reinitiate _Firefly_, which will again open a new tab called "Firefly Viewer".
564570
As mentioned above, we give again two options for initiating _Firefly_; in a new browser tab or a new JupterLab tab.
565571

566-
```python
572+
```{code-cell} ipython3
567573
# Set up Firefly client in browser tab
568574
#fc = FireflyClient.make_client(url="https://irsa.ipac.caltech.edu/irsaviewer")
569575
@@ -590,8 +596,7 @@ You can align and lock the images by their WCS by adding
590596
591597
```
592598

593-
594-
```python
599+
```{code-cell} ipython3
595600
# Load images
596601
fc.show_fits_image(
597602
file_input=cut_path2,
@@ -626,7 +631,7 @@ Finally, we compute the SPHEREx spectra for selected sources. This includes down
626631

627632
Here we select one source to proceed (multiple sources can be measured by looping over the code below). Note that we here directly give it the unique source ID that we defined in the catalog column `NUMBER`.
628633

629-
```python
634+
```{code-cell} ipython3
630635
selected_ids = [65]
631636
matched_stars = extracted[extracted['NUMBER'] == selected_ids[0]]["NUMBER", "ra", "dec"].to_pandas()
632637
```
@@ -636,7 +641,7 @@ We then set up the TAP service as well as the cutout size to run a query for all
636641
Note that the following procedure follows the one shown in the SPHEREx [Image Cutout tutorial notebook](https://caltech-ipac.github.io/irsa-tutorials/spherex-cutouts/).
637642
```
638643

639-
```python
644+
```{code-cell} ipython3
640645
# Define the service endpoint for IRSA's Table Access Protocol (TAP)
641646
# so that we can query SPHEREx metadata tables.
642647
service = pyvo.dal.TAPService("https://irsa.ipac.caltech.edu/TAP")
@@ -667,7 +672,7 @@ print("Number of images found: {}".format(len(tap_results)))
667672

668673
We then define a handy function that downloads the images.
669674

670-
```python
675+
```{code-cell} ipython3
671676
def process_cutout(row, ra, dec, cache, retries=3, delay=5):
672677
'''
673678
Downloads the cutouts given in a row of the table including all SPHEREx images overlapping with a position.
@@ -720,7 +725,7 @@ def process_cutout(row, ra, dec, cache, retries=3, delay=5):
720725

721726
We add some placeholders and other information to the table so we can keep track of things.
722727

723-
```python
728+
```{code-cell} ipython3
724729
# Add columns to hold relevant cutout information
725730
results_table = tap_results.to_table()
726731
results_table["cutout_index"] = range(1, len(results_table) + 1)
@@ -735,7 +740,7 @@ Finally, we can run the download of the cutouts in parallel via the function cre
735740
The cell below can take a couple of minutes to run depending on the number of observations available at a given sky coordinate position.
736741
```
737742

738-
```python
743+
```{code-cell} ipython3
739744
# Process cutouts in parallel
740745
t1 = time.time()
741746
n_timeouts = 0
@@ -765,8 +770,7 @@ print("Time to create cutouts in serial mode: {:2.2f} minutes.".format((time.tim
765770
Having obtained the cutouts, we can now perform aperture photometry on the cutouts and measure the fluxes as a function of wavelength.
766771
See `spx_sdt/aperture_photometry.py` for more information on the functions used below.
767772

768-
769-
```python
773+
```{code-cell} ipython3
770774
phot_path = os.path.realpath( "./data/ap_phot_sources.pkl" ) # this is where the photometry results are saved.
771775
aperture_radii = [3.0, 4.0, 5.0] # aperture radii in pixels
772776
matched_stars = matched_stars.reset_index(drop=True)
@@ -777,15 +781,15 @@ build_phot_table(results_table, matched_stars, aperture_radii, phot_path, sapm_s
777781

778782
Load the photometry file we just created.
779783

780-
```python
784+
```{code-cell} ipython3
781785
if os.path.exists(phot_path):
782786
with open(phot_path, 'rb') as f:
783787
all_stars_band_phot = pickle.load(f)
784788
```
785789

786790
Apply flagging to the extracted 1D-Spectrum.
787791

788-
```python
792+
```{code-cell} ipython3
789793
# Define bad photometry flags
790794
bad_flags = ['TRANSIENT', 'OVERFLOW', 'SUR_ERROR', 'PHANTOM', 'REFERENCE', 'NONFUNC', 'DICHROIC',
791795
'MISSING_DATA', 'HOT', 'COLD', 'FULLSAMPLE', 'PHANMISS', 'NONLINEAR', 'PERSIST', 'OUTLIER']
@@ -797,7 +801,7 @@ wvl, bdw, abmags, abmag_errs, flx, var, n_bad = \
797801

798802
Finally visualize the spectrum using _bokeh_ in flux and magnitudes!
799803

800-
```python
804+
```{code-cell} ipython3
801805
plot_spectrum(wvl, bdw, flx, var, abmags, abmag_errs, n_bad, type="flux",
802806
source_id=all_stars_band_phot['NUMBER'][targ], ap_size=3.0)
803807
plot_spectrum(wvl, bdw, flx, var, abmags, abmag_errs, n_bad, type="magnitude",

0 commit comments

Comments
 (0)