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-3463: Dark current noise floor #236

Closed
wants to merge 35 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
2bf0d49
Update ols_fit.py
mwregan2 Oct 30, 2023
cd07af9
Update ols_fit.py
mwregan2 Oct 31, 2023
a89a88f
add avg_dark_current parameter
mwregan2 Nov 1, 2023
05de5ac
Update ramp_fit.py
mwregan2 Nov 1, 2023
bebf811
Update ols_fit.py
mwregan2 Nov 1, 2023
87ed46e
Update test_ramp_fitting.py
mwregan2 Nov 1, 2023
df8350e
Update test_ramp_fitting.py
mwregan2 Nov 1, 2023
c3294eb
Update ols_fit.py
mwregan2 Nov 1, 2023
1ac03ac
Update ols_fit.py
mwregan2 Nov 1, 2023
06f2b2d
Update ols_fit.py
mwregan2 Nov 6, 2023
43a0163
Update ols_fit.py
mwregan2 Nov 6, 2023
9e7ba5c
Update ols_fit.py
mwregan2 Nov 6, 2023
5237fd9
add fits
mwregan2 Nov 7, 2023
99faff7
Update ols_fit.py
mwregan2 Nov 8, 2023
af7cac3
updates
mwregan2 Dec 4, 2023
9fb0069
Update ols_fit.py
mwregan2 Dec 27, 2023
b6114da
Merge branch 'dark-current-noise-floor' of https://github.com/mwregan…
mwregan2 Dec 27, 2023
563a0f1
Add Unit test
mwregan2 Dec 27, 2023
b4e6433
Update test_ramp_fitting.py
mwregan2 Dec 28, 2023
0f827d2
Update CHANGES.rst
mwregan2 Dec 28, 2023
dd5974a
Update jump.py
mwregan2 Jan 30, 2024
df7689e
move avg_dark_current to ramp_fit_class
mwregan2 Jan 31, 2024
3ed79cb
Update ramp_fit.py
mwregan2 Jan 31, 2024
4259f39
Update test_jump.py
mwregan2 Feb 1, 2024
9996de4
clean_up_from_moving_avg_dark_current
mwregan2 Feb 1, 2024
c00eac5
Update test_ramp_fitting.py
mwregan2 Feb 1, 2024
76a8422
Update test_ramp_fitting.py
mwregan2 Feb 1, 2024
4ed2a1f
Update CHANGES.rst
mwregan2 Feb 1, 2024
bc33f79
add dark current parameter
mwregan2 Feb 1, 2024
47dc8c7
Update ramp_fit parameter definitions
mwregan2 Feb 5, 2024
af6fbca
clean up parameters
mwregan2 Feb 5, 2024
7c11971
clean up extra parameters
mwregan2 Feb 5, 2024
6155385
Merge branch 'spacetelescope:main' into dark-current-noise-floor
mwregan2 Feb 5, 2024
baa1e45
Update test_ramp_fitting.py
mwregan2 Feb 5, 2024
23e3562
Merge branch 'dark-current-noise-floor' of https://github.com/mwregan…
mwregan2 Feb 5, 2024
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
5 changes: 4 additions & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
1.5.3 (unreleased)
==================

-
ramp_fitting
------------
- Added the avg_dark_current parameter that is used to include the
dark current in the calculation of the Poisson noise [#236]

1.5.2 (2023-12-13)
==================
Expand Down
11 changes: 7 additions & 4 deletions src/stcal/jump/jump.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import logging
import multiprocessing
import time

import cv2 as cv
import numpy as np
from astropy import stats
from astropy.convolution import Ring2DKernel, convolve
import cv2 as cv
import astropy.stats as stats

from astropy.convolution import Ring2DKernel
from astropy.convolution import convolve


from . import constants
from . import twopoint_difference as twopt
Expand Down Expand Up @@ -848,6 +850,7 @@ def find_faint_extended(
Total number of showers detected.

"""
log.info('Flagging Showers')
read_noise_2 = readnoise_2d**2
data = indata.copy()
data[gdq == sat_flag] = np.nan
Expand Down
47 changes: 33 additions & 14 deletions src/stcal/ramp_fitting/ols_fit.py
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only places in ols_fit.py that avg_dark_current should appear is at lines 1157 and 1199.

Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
log.setLevel(logging.DEBUG)


def ols_ramp_fit_multi(ramp_data, buffsize, save_opt, readnoise_2d, gain_2d, weighting, max_cores):
def ols_ramp_fit_multi(
ramp_data, buffsize, save_opt, readnoise_2d, gain_2d, weighting, max_cores):
"""
Setup the inputs to ols_ramp_fit with and without multiprocessing. The
inputs will be sliced into the number of cores that are being used for
Expand Down Expand Up @@ -57,6 +58,9 @@
'half', and 'all'. This is the fraction of cores to use for multi-proc. The
total number of cores includes the SMT cores (Hyper Threading for Intel).

avg_dark_current: float (electrons)
hbushouse marked this conversation as resolved.
Show resolved Hide resolved
The average dark current for this detector

Returns
-------
image_info : tuple
Expand Down Expand Up @@ -96,15 +100,16 @@
if number_slices == 1:
# Single threaded computation
image_info, integ_info, opt_info = ols_ramp_fit_single(
ramp_data, buffsize, save_opt, readnoise_2d, gain_2d, weighting
)
ramp_data, buffsize, save_opt, readnoise_2d, gain_2d, weighting)
if image_info is None or integ_info is None:
return None, None, None

return image_info, integ_info, opt_info

# Call ramp fitting for multi-processor (multiple data slices) case
image_info, integ_info, opt_info = ols_ramp_fit_multiprocessing(

else:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This else is unnecessary.

image_info, integ_info, opt_info = ols_ramp_fit_multiprocessing(

Check warning on line 112 in src/stcal/ramp_fitting/ols_fit.py

View check run for this annotation

Codecov / codecov/patch

src/stcal/ramp_fitting/ols_fit.py#L112

Added line #L112 was not covered by tests
ramp_data, buffsize, save_opt, readnoise_2d, gain_2d, weighting, number_slices
)

Expand Down Expand Up @@ -144,6 +149,8 @@
number_slices: int
The number of slices to partition the data into for multiprocessing.

avg_dark_current: float
hbushouse marked this conversation as resolved.
Show resolved Hide resolved
The average dark current in electrons
Return
------
image_info: tuple
Expand All @@ -157,8 +164,8 @@
"""
log.info("Number of processors used for multiprocessing: %s", number_slices)
slices, rows_per_slice = compute_slices_for_starmap(
ramp_data, buffsize, save_opt, readnoise_2d, gain_2d, weighting, number_slices
)
ramp_data, buffsize, save_opt,
readnoise_2d, gain_2d, weighting, number_slices)

pool = Pool(processes=number_slices)
pool_results = pool.starmap(ols_ramp_fit_single, slices)
Expand Down Expand Up @@ -432,8 +439,8 @@


def compute_slices_for_starmap(
ramp_data, buffsize, save_opt, readnoise_2d, gain_2d, weighting, number_slices
):
ramp_data, buffsize, save_opt,
readnoise_2d, gain_2d, weighting, avg_dark_current, number_slices):
hbushouse marked this conversation as resolved.
Show resolved Hide resolved
"""
Creates the slices needed for each process for multiprocessing. The slices
for the arguments needed for ols_ramp_fit_single.
Expand Down Expand Up @@ -473,9 +480,11 @@
start_row = 0
for k in range(len(rslices)):
ramp_slice = slice_ramp_data(ramp_data, start_row, rslices[k])
rnoise_slice = readnoise_2d[start_row : start_row + rslices[k], :].copy()
gain_slice = gain_2d[start_row : start_row + rslices[k], :].copy()
slices.insert(k, (ramp_slice, buffsize, save_opt, rnoise_slice, gain_slice, weighting))

rnoise_slice = readnoise_2d[start_row:start_row + rslices[k], :].copy()
gain_slice = gain_2d[start_row:start_row + rslices[k], :].copy()
slices.insert(

Check warning on line 486 in src/stcal/ramp_fitting/ols_fit.py

View check run for this annotation

Codecov / codecov/patch

src/stcal/ramp_fitting/ols_fit.py#L484-L486

Added lines #L484 - L486 were not covered by tests
k, (ramp_slice, buffsize, save_opt, rnoise_slice, gain_slice, weighting, avg_dark_current))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The avg_dark_current is not needed as a part of this tuple. I am surprised this doesn't cause problems with the multiprocessing, since this tuple is a list of parameters to be used by ols_ramp_fit_single.

start_row = start_row + rslices[k]

return slices, rslices
Expand Down Expand Up @@ -618,7 +627,8 @@
ramp_data.one_groups_time = (ramp_data.nframes + 1) * ramp_data.frame_time / 2


def ols_ramp_fit_single(ramp_data, buffsize, save_opt, readnoise_2d, gain_2d, weighting):
def ols_ramp_fit_single(
ramp_data, buffsize, save_opt, readnoise_2d, gain_2d, weighting):
"""
Fit a ramp using ordinary least squares. Calculate the count rate for each
pixel in all data cube sections and all integrations, equal to the weighted
Expand All @@ -645,6 +655,9 @@
weighting : str
'optimal' is the only valid value

avg_dark_current : float (electrons)
hbushouse marked this conversation as resolved.
Show resolved Hide resolved
The average dark current for this detector

Return
------
image_info : tuple
Expand Down Expand Up @@ -1049,6 +1062,8 @@
fit_slopes_ans : tuple
Contains intermediate values computed in the first pass over the data.

avg_dark_current : float (electrons)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The avg_dark_current parameter needs to be removed from the docstring.

The average dark current for this detector
Return
------
var_p3 : ndarray
Expand Down Expand Up @@ -1138,7 +1153,8 @@
# Suppress harmless arithmetic warnings for now
warnings.filterwarnings("ignore", ".*invalid value.*", RuntimeWarning)
warnings.filterwarnings("ignore", ".*divide by zero.*", RuntimeWarning)
var_p4[num_int, :, rlo:rhi, :] = den_p3 * med_rates[rlo:rhi, :]
var_p4[num_int, :, rlo:rhi, :] = ((den_p3 * med_rates[rlo:rhi, :]) +
ramp_data.avg_dark_current)

# Find the segment variance due to read noise and convert back to DN
var_r4[num_int, :, rlo:rhi, :] = num_r3 * den_r3 / gain_sect**2
Expand Down Expand Up @@ -1178,7 +1194,10 @@
var_p3[:, med_rates <= 0.0] = 0.0
warnings.resetwarnings()

var_p4[num_int, :, med_rates <= 0.0] = 0.0
# For pixels with zero or negative rates set the Poisson variance to the
# dark current rate.
var_p4[num_int, :, med_rates <= 0.0] = ramp_data.avg_dark_current

var_both4[num_int, :, :, :] = var_r4[num_int, :, :, :] + var_p4[num_int, :, :, :]
inv_var_both4[num_int, :, :, :] = 1.0 / var_both4[num_int, :, :, :]

Expand Down
27 changes: 13 additions & 14 deletions src/stcal/ramp_fitting/ramp_fit.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
BUFSIZE = 1024 * 300000 # 300Mb cache size for data section


def create_ramp_fit_class(model, dqflags=None, suppress_one_group=False):
def create_ramp_fit_class(model, dqflags=None, suppress_one_group=False, avg_dark_current=0):
"""
Create an internal ramp fit class from a data model.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The avg_dark_current needs to be added to the docstring.


Expand Down Expand Up @@ -77,6 +77,7 @@
ramp_data.num_rows = ramp_data.data.shape[2]

ramp_data.suppress_one_group_ramps = suppress_one_group
ramp_data.avg_dark_current = avg_dark_current

Check warning on line 80 in src/stcal/ramp_fitting/ramp_fit.py

View check run for this annotation

Codecov / codecov/patch

src/stcal/ramp_fitting/ramp_fit.py#L80

Added line #L80 was not covered by tests

return ramp_data

Expand All @@ -92,7 +93,8 @@
max_cores,
dqflags,
suppress_one_group=False,
):
avg_dark_current=0.0,
):
"""
Calculate the count rate for each pixel in all data cube sections and all
integrations, equal to the slope for all sections (intervals between
Expand Down Expand Up @@ -141,6 +143,9 @@
Find ramps with only one good group and treat it like it has zero good
groups.

avg_dark_current : flt
The average dark current for this detector in units of electrons per second.

Returns
-------
image_info : tuple
Expand All @@ -164,16 +169,15 @@
# Create an instance of the internal ramp class, using only values needed
# for ramp fitting from the to remove further ramp fitting dependence on
# data models.
ramp_data = create_ramp_fit_class(model, dqflags, suppress_one_group)
ramp_data = create_ramp_fit_class(model, dqflags, suppress_one_group, avg_dark_current)

Check warning on line 172 in src/stcal/ramp_fitting/ramp_fit.py

View check run for this annotation

Codecov / codecov/patch

src/stcal/ramp_fitting/ramp_fit.py#L172

Added line #L172 was not covered by tests

return ramp_fit_data(
ramp_data, buffsize, save_opt, readnoise_2d, gain_2d, algorithm, weighting, max_cores, dqflags
)
ramp_data, buffsize, save_opt, readnoise_2d, gain_2d,
algorithm, weighting, max_cores, dqflags, avg_dark_current)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The avg_dark_current needs to be removed from the function parameters, since it is now a member parameter in ramp_data.



def ramp_fit_data(
ramp_data, buffsize, save_opt, readnoise_2d, gain_2d, algorithm, weighting, max_cores, dqflags
):
def ramp_fit_data(ramp_data, buffsize, save_opt, readnoise_2d, gain_2d,
algorithm, weighting, max_cores):
"""
This function begins the ramp fit computation after the creation of the
RampData class. It determines the proper path for computation to take
Expand Down Expand Up @@ -211,10 +215,6 @@
to use for multi-proc. The total number of cores includes the SMT cores
(Hyper Threading for Intel).

dqflags : dict
A dictionary with at least the following keywords:
DO_NOT_USE, SATURATED, JUMP_DET, NO_GAIN_VALUE, UNRELIABLE_SLOPE

Returns
-------
image_info : tuple
Expand Down Expand Up @@ -247,8 +247,7 @@

# Compute ramp fitting using ordinary least squares.
image_info, integ_info, opt_info = ols_fit.ols_ramp_fit_multi(
ramp_data, buffsize, save_opt, readnoise_2d, gain_2d, weighting, max_cores
)
ramp_data, buffsize, save_opt, readnoise_2d, gain_2d, weighting, max_cores)
gls_opt_info = None

return image_info, integ_info, opt_info, gls_opt_info
Expand Down
1 change: 1 addition & 0 deletions src/stcal/ramp_fitting/ramp_fit_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ def __init__(self):
self.groupgap = None
self.nframes = None
self.drop_frames1 = None
self.avg_dark_current = 0.0

# Data quality flags
self.flags_do_not_use = None
Expand Down
27 changes: 25 additions & 2 deletions tests/test_jump.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import numpy as np
import pytest

from astropy.io import fits
from stcal.jump.jump import (
calc_num_slices,
extend_saturation,
Expand All @@ -11,6 +11,7 @@
detect_jumps,
)


DQFLAGS = {"JUMP_DET": 4, "SATURATED": 2, "DO_NOT_USE": 1, "GOOD": 0, "NO_GAIN_VALUE": 8}


Expand Down Expand Up @@ -445,4 +446,26 @@
assert calc_num_slices(n_rows, "all", max_available_cores) == 10
assert calc_num_slices(n_rows, "3/4", max_available_cores) == 1
n_rows = 9
assert calc_num_slices(n_rows, "21", max_available_cores) == 9
assert (calc_num_slices(n_rows, '21', max_available_cores) == 9)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parentheses are unnecessary and should be removed.


@pytest.mark.skip(reason="local test only")
def test_shower_slowmode():
data = fits.getdata("shower_input_data.fits")
gdq = fits.getdata("shower_input_gdq.fits")
nint = data.shape[0]
ngrps = data.shape[1]
ncols = data.shape[2]
nrows = data.shape[3]
gain = 5
readnoise = fits.getdata("/grp/crds/jwst/references/jwst/jwst_miri_readnoise_0086.fits")

Check warning on line 460 in tests/test_jump.py

View check run for this annotation

Codecov / codecov/patch

tests/test_jump.py#L453-L460

Added lines #L453 - L460 were not covered by tests
# readnoise = np.ones(shape=(nrows, ncols), dtype=np.float32) * 6.0 * gain
rng = np.random.default_rng(12345)

Check warning on line 462 in tests/test_jump.py

View check run for this annotation

Codecov / codecov/patch

tests/test_jump.py#L462

Added line #L462 was not covered by tests
# data[0, 1:, 14:20, 15:20] = 6 * gain * 1.7
# data = data + rng.normal(size=(nint, ngrps, nrows, ncols)) * readnoise
gdq, num_showers = find_faint_extended(data, gdq, readnoise, 1, 100,

Check warning on line 465 in tests/test_jump.py

View check run for this annotation

Codecov / codecov/patch

tests/test_jump.py#L465

Added line #L465 was not covered by tests
snr_threshold=1.3,
min_shower_area=40, inner=1,
outer=2, sat_flag=2, jump_flag=4,
ellipse_expand=1.1, num_grps_masked=3)
print("number of showers", num_showers)
fits.writeto("outgdq.fits", gdq, overwrite=True)

Check warning on line 471 in tests/test_jump.py

View check run for this annotation

Codecov / codecov/patch

tests/test_jump.py#L470-L471

Added lines #L470 - L471 were not covered by tests
Loading
Loading