Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JP-3858-white_light #9241

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ repos:
jwst/tests/.* |
jwst/tso_photometry/.* |
jwst/wfs_combine/.* |
jwst/white_light/.* |
docs/.* |
jwst/regtest/test_.* |
.*/tests/test_.*
Expand Down
2 changes: 0 additions & 2 deletions .ruff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ exclude = [
"jwst/superbias/**.py",
"jwst/tso_photometry/**.py",
"jwst/wfs_combine/**.py",
"jwst/white_light/**.py",
]

[lint]
Expand Down Expand Up @@ -150,4 +149,3 @@ ignore-fully-untyped = true # Turn off annotation checking for fully untyped co
"jwst/superbias/**.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"]
4 changes: 3 additions & 1 deletion jwst/white_light/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Sum the spectroscopic flux over all wavelengths in each integration."""

from .white_light_step import WhiteLightStep

__all__ = ['WhiteLightStep']
__all__ = ["WhiteLightStep"]
38 changes: 19 additions & 19 deletions jwst/white_light/tests/test_white_light.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import pytest


@pytest.fixture(scope='module')
@pytest.fixture(scope="module")
def make_datamodel():
"""Make data for white light tests"""

Expand Down Expand Up @@ -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
Expand All @@ -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):
Expand All @@ -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
110 changes: 67 additions & 43 deletions jwst/white_light/white_light.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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]
Expand All @@ -37,100 +57,104 @@
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:
high_cutoff = max_wave

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)

Check warning on line 108 in jwst/white_light/white_light.py

View check run for this annotation

Codecov / codecov/patch

jwst/white_light/white_light.py#L108

Added line #L108 was not covered by tests
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]
log.warning("INTEND not found; assuming a value of %d", int_end)

# 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)
j0 = 0
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
35 changes: 26 additions & 9 deletions jwst/white_light/white_light_step.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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"
Expand All @@ -20,19 +26,30 @@
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:

Check warning on line 46 in jwst/white_light/white_light_step.py

View check run for this annotation

Codecov / codecov/patch

jwst/white_light/white_light_step.py#L46

Added line #L46 was not covered by tests
# 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)

Check warning on line 53 in jwst/white_light/white_light_step.py

View check run for this annotation

Codecov / codecov/patch

jwst/white_light/white_light_step.py#L53

Added line #L53 was not covered by tests

return result