From d14ecb90b6ea7484b93f948e1fb25caa89151d32 Mon Sep 17 00:00:00 2001 From: Maria Pena-Guerrero Date: Tue, 4 Mar 2025 12:52:34 -0500 Subject: [PATCH 1/3] style changes --- .pre-commit-config.yaml | 1 - .ruff.toml | 2 - jwst/white_light/__init__.py | 4 +- jwst/white_light/tests/test_white_light.py | 38 +++---- jwst/white_light/white_light.py | 110 +++++++++++++-------- jwst/white_light/white_light_step.py | 35 +++++-- 6 files changed, 115 insertions(+), 75 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b01de2a9fc..cf4d484b77 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -70,7 +70,6 @@ repos: jwst/tests/.* | jwst/tso_photometry/.* | jwst/wfs_combine/.* | - jwst/white_light/.* | jwst/conftest.py | .*/tests/.* | setup.py | diff --git a/.ruff.toml b/.ruff.toml index ca2a9e4309..cd2d02c11e 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -55,7 +55,6 @@ exclude = [ "jwst/tests/**.py", "jwst/tso_photometry/**.py", "jwst/wfs_combine/**.py", - "jwst/white_light/**.py", ] [lint] @@ -152,4 +151,3 @@ ignore-fully-untyped = true # Turn off annotation checking for fully untyped co "jwst/tests/**.py" = ["D", "N", "A", "ARG", "B", "C4", "ICN", "INP", "ISC", "LOG", "NPY", "PGH", "PTH", "S", "SLF", "SLOT", "T20", "TRY", "UP", "YTT", "E501"] "jwst/tso_photometry/**.py" = ["D", "N", "A", "ARG", "B", "C4", "ICN", "INP", "ISC", "LOG", "NPY", "PGH", "PTH", "S", "SLF", "SLOT", "T20", "TRY", "UP", "YTT", "E501"] "jwst/wfs_combine/**.py" = ["D", "N", "A", "ARG", "B", "C4", "ICN", "INP", "ISC", "LOG", "NPY", "PGH", "PTH", "S", "SLF", "SLOT", "T20", "TRY", "UP", "YTT", "E501"] -"jwst/white_light/**.py" = ["D", "N", "A", "ARG", "B", "C4", "ICN", "INP", "ISC", "LOG", "NPY", "PGH", "PTH", "S", "SLF", "SLOT", "T20", "TRY", "UP", "YTT", "E501"] diff --git a/jwst/white_light/__init__.py b/jwst/white_light/__init__.py index 4e12c0caf7..b4b19de46b 100644 --- a/jwst/white_light/__init__.py +++ b/jwst/white_light/__init__.py @@ -1,3 +1,5 @@ +"""Sum the spectroscopic flux over all wavelengths in each integration.""" + from .white_light_step import WhiteLightStep -__all__ = ['WhiteLightStep'] +__all__ = ["WhiteLightStep"] diff --git a/jwst/white_light/tests/test_white_light.py b/jwst/white_light/tests/test_white_light.py index 43deded4dc..8beb175fe5 100644 --- a/jwst/white_light/tests/test_white_light.py +++ b/jwst/white_light/tests/test_white_light.py @@ -7,7 +7,7 @@ import pytest -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def make_datamodel(): """Make data for white light tests""" @@ -60,13 +60,13 @@ def make_datamodel(): (2, 58627.5390206, 58627.53907555, 58627.5391305, 0., 0., 0.), (3, 58627.5391305, 58627.53918544, 58627.53924039, 0., 0., 0.)] - integration_table = np.array(integrations, dtype=[('integration_number', 'i4'), - ('int_start_MJD_UTC', 'f8'), - ('int_mid_MJD_UTC', 'f8'), - ('int_end_MJD_UTC', 'f8'), - ('int_start_BJD_TDB', 'f8'), - ('int_mid_BJD_TDB', 'f8'), - ('int_end_BJD_TDB', 'f8')]) + integration_table = np.array(integrations, dtype=[("integration_number", "i4"), + ("int_start_MJD_UTC", "f8"), + ("int_mid_MJD_UTC", "f8"), + ("int_end_MJD_UTC", "f8"), + ("int_start_BJD_TDB", "f8"), + ("int_mid_BJD_TDB", "f8"), + ("int_end_BJD_TDB", "f8")]) model.int_times = integration_table return model @@ -80,19 +80,19 @@ def test_white_light_with_int_tables(make_datamodel): # We know there is only one table, so set we are hardcoding. ntables = 1 - int_num = data.int_times['integration_number'] - mid_utc = data.int_times['int_mid_MJD_UTC'] + int_num = data.int_times["integration_number"] + mid_utc = data.int_times["int_mid_MJD_UTC"] offset = int_start - int_num[0] time_arr = np.zeros(ntables, dtype=np.float64) time_arr[0: 1] = mid_utc[offset: offset + ntables] - int_times = Time(time_arr, format='mjd', scale='utc') + int_times = Time(time_arr, format="mjd", scale="utc") # Sum the fluxes - fluxsums = data.spec[0].spec_table['FLUX'].sum() + fluxsums = data.spec[0].spec_table["FLUX"].sum() - assert result['MJD'] == int_times.mjd - assert result['whitelight_flux'] == fluxsums + assert result["MJD"] == int_times.mjd + assert result["whitelight_flux"] == fluxsums def test_white_light_with_expstart(make_datamodel): @@ -112,12 +112,12 @@ def test_white_light_with_expstart(make_datamodel): # We know there is only one table, so set we are hardcoding. ntables_current = 1 dt_arr[0: 1] = np.arange(1, 1 + ntables_current) * dt - (dt / 2.) - int_dt = TimeDelta(dt_arr, format='sec') + int_dt = TimeDelta(dt_arr, format="sec") - int_times = (Time(data.meta.exposure.start_time, format='mjd') + int_times = (Time(data.meta.exposure.start_time, format="mjd") + int_dt) # Sum the fluxes - fluxsums = data.spec[0].spec_table['FLUX'].sum() + fluxsums = data.spec[0].spec_table["FLUX"].sum() - assert result['MJD'][0] == int_times.mjd[0] - assert result['whitelight_flux'] == fluxsums + assert result["MJD"][0] == int_times.mjd[0] + assert result["whitelight_flux"] == fluxsums diff --git a/jwst/white_light/white_light.py b/jwst/white_light/white_light.py index a1a84462e4..699a520577 100644 --- a/jwst/white_light/white_light.py +++ b/jwst/white_light/white_light.py @@ -1,3 +1,5 @@ +"""Sum the flux over all wavelengths in each integration as a function of time for the target.""" + import logging import numpy as np @@ -9,21 +11,39 @@ log.setLevel(logging.DEBUG) -def white_light(input, min_wave=None, max_wave=None): +def white_light(step_input, min_wave=None, max_wave=None): + """ + Compute the integrated flux over all wavelengths for a multi-integration extracted spectrum. + + Parameters + ---------- + step_input : stdatamodels.jwst.datamodels.multispec.MultiSpecModel + Datamodel containing the multi-integration data + + min_wave : float, optional + Default wavelength minimum for integration. + + max_wave : float, optional + Default wavelength maximum for integration. - ntables = len(input.spec) + Returns + ------- + tbl : astropy.table.table.QTable + Table containing the integrated flux as a function of time. + """ + ntables = len(step_input.spec) fluxsums = [] # The input should contain one row per integration for each spectral # order. NIRISS SOSS data can contain up to three orders. - norders = 0 # number of different spectral orders - sporders = [] # list of spectral order numbers - ntables_order = [] # number of tables for each spectral order + norders = 0 # number of different spectral orders + sporders = [] # list of spectral order numbers + ntables_order = [] # number of tables for each spectral order prev_spectral_order = -999 for i in range(ntables): # The following assumes that all rows for a given spectral order # are contiguous. - spectral_order = input.spec[i].spectral_order + spectral_order = step_input.spec[i].spectral_order if spectral_order is None: norders = 1 sporders = [0] @@ -37,13 +57,17 @@ def white_light(input, min_wave=None, max_wave=None): else: ntables_order[norders - 1] += 1 - log.debug("norders = %d, sporders = %s, ntables_order = %s", - norders, str(sporders), str(ntables_order)) + log.debug( + "norders = %d, sporders = %s, ntables_order = %s", + norders, + str(sporders), + str(ntables_order), + ) # Create a wavelength mask, using cutoffs if specified, then - # compute the flux sum for each integration in the input. - low_cutoff = -1. - high_cutoff = 1.e10 + # compute the flux sum for each integration in the step_input. + low_cutoff = -1.0 + high_cutoff = 1.0e10 if min_wave is not None: low_cutoff = min_wave if max_wave is not None: @@ -51,38 +75,38 @@ def white_light(input, min_wave=None, max_wave=None): for i in range(ntables): wave_mask = np.where( - (input.spec[i].spec_table['WAVELENGTH'] >= low_cutoff) & - (input.spec[i].spec_table['WAVELENGTH'] <= high_cutoff))[0] + (step_input.spec[i].spec_table["WAVELENGTH"] >= low_cutoff) + & (step_input.spec[i].spec_table["WAVELENGTH"] <= high_cutoff) + )[0] - fluxsums.append(np.nansum(input.spec[i].spec_table['FLUX'][wave_mask])) + fluxsums.append(np.nansum(step_input.spec[i].spec_table["FLUX"][wave_mask])) - # Populate meta data for the output table + # Populate metadata for the output table tbl_meta = OrderedDict() - tbl_meta['instrument'] = input.meta.instrument.name - tbl_meta['detector'] = input.meta.instrument.detector - tbl_meta['exp_type'] = input.meta.exposure.type - tbl_meta['subarray'] = input.meta.subarray.name - tbl_meta['filter'] = input.meta.instrument.filter - tbl_meta['pupil'] = input.meta.instrument.pupil - tbl_meta['target_name'] = input.meta.target.catalog_name + tbl_meta["instrument"] = step_input.meta.instrument.name + tbl_meta["detector"] = step_input.meta.instrument.detector + tbl_meta["exp_type"] = step_input.meta.exposure.type + tbl_meta["subarray"] = step_input.meta.subarray.name + tbl_meta["filter"] = step_input.meta.instrument.filter + tbl_meta["pupil"] = step_input.meta.instrument.pupil + tbl_meta["target_name"] = step_input.meta.target.catalog_name # Create the output table tbl = QTable(meta=tbl_meta) - if hasattr(input, 'int_times') and input.int_times is not None: - nrows = len(input.int_times) + if hasattr(step_input, "int_times") and step_input.int_times is not None: + nrows = len(step_input.int_times) else: nrows = 0 if nrows == 0: log.warning("There is no INT_TIMES table in the input file.") if nrows > 0: - int_start = input.meta.exposure.integration_start # one indexed + int_start = step_input.meta.exposure.integration_start # one indexed if int_start is None: int_start = 1 - log.warning("INTSTART not found; assuming a value of %d", - int_start) - int_end = input.meta.exposure.integration_end # one indexed + log.warning("INTSTART not found; assuming a value of %d", int_start) + int_end = step_input.meta.exposure.integration_end # one indexed if int_end is None: # Number of tables for the first (possibly only) spectral order. int_end = ntables_order[0] @@ -90,15 +114,17 @@ def white_light(input, min_wave=None, max_wave=None): # Columns of integration numbers & times of integration from the # INT_TIMES table. - int_num = input.int_times['integration_number'] # one indexed - mid_utc = input.int_times['int_mid_MJD_UTC'] + int_num = step_input.int_times["integration_number"] # one indexed + mid_utc = step_input.int_times["int_mid_MJD_UTC"] offset = int_start - int_num[0] if offset < 0 or int_end > int_num[-1]: - log.warning("Range of integration numbers in science data extends " - "outside the range in INT_TIMES table.") + log.warning( + "Range of integration numbers in science data extends " + "outside the range in INT_TIMES table." + ) log.warning("Can't use INT_TIMES table.") del int_num, mid_utc - nrows = 0 # flag as bad + nrows = 0 # flag as bad else: log.debug("Times are from the INT_TIMES table.") time_arr = np.zeros(ntables, dtype=np.float64) @@ -106,31 +132,29 @@ def white_light(input, min_wave=None, max_wave=None): for k in range(norders): ntables_current = ntables_order[k] j1 = j0 + ntables_current - time_arr[j0: j1] = mid_utc[offset: offset + ntables_current] + time_arr[j0:j1] = mid_utc[offset : offset + ntables_current] j0 += ntables_current - int_times = Time(time_arr, format='mjd', scale='utc') + int_times = Time(time_arr, format="mjd", scale="utc") if nrows == 0: log.debug("Times were computed from EXPSTART and TGROUP.") # Compute the delta time of each integration dt_arr = np.zeros(ntables, dtype=np.float64) - dt = (input.meta.exposure.group_time * - (input.meta.exposure.ngroups + 1)) + dt = step_input.meta.exposure.group_time * (step_input.meta.exposure.ngroups + 1) j0 = 0 for k in range(norders): ntables_current = ntables_order[k] j1 = j0 + ntables_current - dt_arr[j0: j1] = np.arange(1, 1 + ntables_current) * dt - (dt / 2.) + dt_arr[j0:j1] = np.arange(1, 1 + ntables_current) * dt - (dt / 2.0) j0 += ntables_current - int_dt = TimeDelta(dt_arr, format='sec') + int_dt = TimeDelta(dt_arr, format="sec") # Compute the absolute time at the mid-point of each integration - int_times = (Time(input.meta.exposure.start_time, format='mjd') - + int_dt) + int_times = Time(step_input.meta.exposure.start_time, format="mjd") + int_dt # Store the times and flux sums in the table - tbl['MJD'] = int_times.mjd - tbl['whitelight_flux'] = fluxsums + tbl["MJD"] = int_times.mjd + tbl["whitelight_flux"] = fluxsums return tbl diff --git a/jwst/white_light/white_light_step.py b/jwst/white_light/white_light_step.py index 5f4b221a09..712cf97625 100755 --- a/jwst/white_light/white_light_step.py +++ b/jwst/white_light/white_light_step.py @@ -1,4 +1,7 @@ #! /usr/bin/env python + +"""Get integrated flux as a function of time for a multi-integration spectroscopic observation.""" + from stdatamodels.jwst import datamodels from ..stpipe import Step @@ -9,8 +12,11 @@ class WhiteLightStep(Step): """ - WhiteLightStep: Computes integrated flux as a function of time for a - multi-integration spectroscopic observation. + Sum the spectroscopic flux over all wavelengths in each integration. + + Specific to multi-integration extracted spectrum to produce an integrated (“white”) + flux as a function of time for the target. This is to be applied to the _x1dints + product in a spectroscopic Time-Series Observation (TSO). """ class_alias = "white_light" @@ -20,19 +26,30 @@ class WhiteLightStep(Step): max_wavelength = float(default=None) # Default wavelength maximum for integration output_ext = string(default='.ecsv') # Output file type suffix = string(default='whtlt') # Default suffix for output files - """ # noqa: E501 - - def process(self, input): - + """ # noqa: E501 + + def process(self, step_input): + """ + Sum the flux over all wavelengths in each integration as a function of time for the target. + + Parameters + ---------- + step_input : str or stdatamodels.jwst.datamodels.multispec.MultiSpecModel + Either the path to the file or the science data model for the sum. + + Returns + ------- + result : astropy.table.table.QTable + Table containing the integrated flux as a function of time. + """ # Load the input - with datamodels.open(input) as input_model: - + with datamodels.open(step_input) as input_model: # Call the white light curve generation routine result = white_light(input_model, self.min_wavelength, self.max_wavelength) # Write the output catalog if self.save_results: output_path = self.make_output_path() - result.write(output_path, format='ascii.ecsv', overwrite=True) + result.write(output_path, format="ascii.ecsv", overwrite=True) return result From 7a0b95db132171efad32a0d2ad5c6ac8334ed012 Mon Sep 17 00:00:00 2001 From: Maria Pena-Guerrero Date: Wed, 5 Mar 2025 12:21:33 -0500 Subject: [PATCH 2/3] style changes --- jwst/white_light/white_light_step.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jwst/white_light/white_light_step.py b/jwst/white_light/white_light_step.py index 712cf97625..87d168547c 100755 --- a/jwst/white_light/white_light_step.py +++ b/jwst/white_light/white_light_step.py @@ -14,9 +14,9 @@ class WhiteLightStep(Step): """ Sum the spectroscopic flux over all wavelengths in each integration. - Specific to multi-integration extracted spectrum to produce an integrated (“white”) - flux as a function of time for the target. This is to be applied to the _x1dints - product in a spectroscopic Time-Series Observation (TSO). + Produce an integrated (“white”) flux as a function of time for the target. This + is to be applied to the _x1dints product in a spectroscopic + Time-Series Observation (TSO). """ class_alias = "white_light" From a6a5969b426f16803e1e81073749eb2ff9858ff8 Mon Sep 17 00:00:00 2001 From: Maria Pena-Guerrero Date: Fri, 7 Mar 2025 14:57:23 -0500 Subject: [PATCH 3/3] style changes --- jwst/white_light/white_light.py | 46 ++++++++++++++-------------- jwst/white_light/white_light_step.py | 4 +-- 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/jwst/white_light/white_light.py b/jwst/white_light/white_light.py index 699a520577..644f5fe273 100644 --- a/jwst/white_light/white_light.py +++ b/jwst/white_light/white_light.py @@ -11,13 +11,13 @@ log.setLevel(logging.DEBUG) -def white_light(step_input, min_wave=None, max_wave=None): +def white_light(input_model, min_wave=None, max_wave=None): """ Compute the integrated flux over all wavelengths for a multi-integration extracted spectrum. Parameters ---------- - step_input : stdatamodels.jwst.datamodels.multispec.MultiSpecModel + input_model : MultiSpecModel Datamodel containing the multi-integration data min_wave : float, optional @@ -31,7 +31,7 @@ def white_light(step_input, min_wave=None, max_wave=None): tbl : astropy.table.table.QTable Table containing the integrated flux as a function of time. """ - ntables = len(step_input.spec) + ntables = len(input_model.spec) fluxsums = [] # The input should contain one row per integration for each spectral @@ -43,7 +43,7 @@ def white_light(step_input, min_wave=None, max_wave=None): for i in range(ntables): # The following assumes that all rows for a given spectral order # are contiguous. - spectral_order = step_input.spec[i].spectral_order + spectral_order = input_model.spec[i].spectral_order if spectral_order is None: norders = 1 sporders = [0] @@ -65,7 +65,7 @@ def white_light(step_input, min_wave=None, max_wave=None): ) # Create a wavelength mask, using cutoffs if specified, then - # compute the flux sum for each integration in the step_input. + # compute the flux sum for each integration in the input. low_cutoff = -1.0 high_cutoff = 1.0e10 if min_wave is not None: @@ -75,38 +75,38 @@ def white_light(step_input, min_wave=None, max_wave=None): for i in range(ntables): wave_mask = np.where( - (step_input.spec[i].spec_table["WAVELENGTH"] >= low_cutoff) - & (step_input.spec[i].spec_table["WAVELENGTH"] <= high_cutoff) + (input_model.spec[i].spec_table["WAVELENGTH"] >= low_cutoff) + & (input_model.spec[i].spec_table["WAVELENGTH"] <= high_cutoff) )[0] - fluxsums.append(np.nansum(step_input.spec[i].spec_table["FLUX"][wave_mask])) + fluxsums.append(np.nansum(input_model.spec[i].spec_table["FLUX"][wave_mask])) # Populate metadata for the output table tbl_meta = OrderedDict() - tbl_meta["instrument"] = step_input.meta.instrument.name - tbl_meta["detector"] = step_input.meta.instrument.detector - tbl_meta["exp_type"] = step_input.meta.exposure.type - tbl_meta["subarray"] = step_input.meta.subarray.name - tbl_meta["filter"] = step_input.meta.instrument.filter - tbl_meta["pupil"] = step_input.meta.instrument.pupil - tbl_meta["target_name"] = step_input.meta.target.catalog_name + tbl_meta["instrument"] = input_model.meta.instrument.name + tbl_meta["detector"] = input_model.meta.instrument.detector + tbl_meta["exp_type"] = input_model.meta.exposure.type + tbl_meta["subarray"] = input_model.meta.subarray.name + tbl_meta["filter"] = input_model.meta.instrument.filter + tbl_meta["pupil"] = input_model.meta.instrument.pupil + tbl_meta["target_name"] = input_model.meta.target.catalog_name # Create the output table tbl = QTable(meta=tbl_meta) - if hasattr(step_input, "int_times") and step_input.int_times is not None: - nrows = len(step_input.int_times) + if hasattr(input_model, "int_times") and input_model.int_times is not None: + nrows = len(input_model.int_times) else: nrows = 0 if nrows == 0: log.warning("There is no INT_TIMES table in the input file.") if nrows > 0: - int_start = step_input.meta.exposure.integration_start # one indexed + int_start = input_model.meta.exposure.integration_start # one indexed if int_start is None: int_start = 1 log.warning("INTSTART not found; assuming a value of %d", int_start) - int_end = step_input.meta.exposure.integration_end # one indexed + int_end = input_model.meta.exposure.integration_end # one indexed if int_end is None: # Number of tables for the first (possibly only) spectral order. int_end = ntables_order[0] @@ -114,8 +114,8 @@ def white_light(step_input, min_wave=None, max_wave=None): # Columns of integration numbers & times of integration from the # INT_TIMES table. - int_num = step_input.int_times["integration_number"] # one indexed - mid_utc = step_input.int_times["int_mid_MJD_UTC"] + int_num = input_model.int_times["integration_number"] # one indexed + mid_utc = input_model.int_times["int_mid_MJD_UTC"] offset = int_start - int_num[0] if offset < 0 or int_end > int_num[-1]: log.warning( @@ -141,7 +141,7 @@ def white_light(step_input, min_wave=None, max_wave=None): log.debug("Times were computed from EXPSTART and TGROUP.") # Compute the delta time of each integration dt_arr = np.zeros(ntables, dtype=np.float64) - dt = step_input.meta.exposure.group_time * (step_input.meta.exposure.ngroups + 1) + dt = input_model.meta.exposure.group_time * (input_model.meta.exposure.ngroups + 1) j0 = 0 for k in range(norders): ntables_current = ntables_order[k] @@ -151,7 +151,7 @@ def white_light(step_input, min_wave=None, max_wave=None): int_dt = TimeDelta(dt_arr, format="sec") # Compute the absolute time at the mid-point of each integration - int_times = Time(step_input.meta.exposure.start_time, format="mjd") + int_dt + int_times = Time(input_model.meta.exposure.start_time, format="mjd") + int_dt # Store the times and flux sums in the table tbl["MJD"] = int_times.mjd diff --git a/jwst/white_light/white_light_step.py b/jwst/white_light/white_light_step.py index 87d168547c..23b7f641e5 100755 --- a/jwst/white_light/white_light_step.py +++ b/jwst/white_light/white_light_step.py @@ -1,5 +1,3 @@ -#! /usr/bin/env python - """Get integrated flux as a function of time for a multi-integration spectroscopic observation.""" from stdatamodels.jwst import datamodels @@ -34,7 +32,7 @@ def process(self, step_input): Parameters ---------- - step_input : str or stdatamodels.jwst.datamodels.multispec.MultiSpecModel + step_input : str or MultiSpecModel Either the path to the file or the science data model for the sum. Returns