Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main' into flag-low-pix
Browse files Browse the repository at this point in the history
  • Loading branch information
jdavies-st committed Jul 1, 2024
2 parents 3863892 + 2958c6b commit cf8a930
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 69 deletions.
35 changes: 12 additions & 23 deletions CITATION.cff
Original file line number Diff line number Diff line change
@@ -1,26 +1,15 @@
---
cff-version: 1.2.0
title: snowblind
message: If you use this software, please cite as below.
# date-released: 2023-10-29
url: 'https://github.com/mpi-astronomy/snowblind'
# version: 0.1.2
type: software
title: snowblind
abstract: Mask cosmic ray showers (snowballs) in JWST data
authors:
- family-names: Davies
given-names: James
email: [email protected]
affiliation: MPIA
orcid: 'https://orcid.org/0000-0002-5079-9098'

# For citing software, see:

# http://journals.aas.org/authors/references.html

# GitHub has Zenodo DOI integration:
# https://guides.github.com/activities/citable-code/

# What is a CITATION.cff file:
# https://citation-file-format.github.io/

# This CITATION.cff file was generated with cffinit.
# Visit https://bit.ly/cffinit to generate yours today!
- name: James Davies
email: [email protected]
orcid: https://orcid.org/0000-0002-5079-9098
version: 0.2.1
date-released: '2024-02-08'
type: software
license: BSD-3-Clause
url: https://github.com/mpi-astronomy/snowblind
...
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Algorithms for cleaning JWST data.
- `SnowblindStep`: mask cosmic ray showers and snowballs
- `JumpPlusStep`: Propagate JUMP_DET and SATURATED flags in GROUPDQ properly for frame-averaged groups
- `PersistenceFlagStep`: flag pixels effected by persistence exposure-to-exposure
- `RcSelfCalStep`: flag new hot pixels, open pixels or RC pixels
- `OpenPixelStep`: flag new open pixels, hot pixels, or open adjacent pixels


## Installation
Expand Down Expand Up @@ -31,9 +31,10 @@ In Python, we can insert `SnowblindStep` and `JumpPlusStep` after `JumpStep` as
steps = {
"jump": {
"save_results": True,
"flag_large_events": False,
"post_hooks": [
"snowblind.SnowblindStep",
"snowblind.JumpPlusStep",
SnowblindStep,
JumpPlusStep,
],
},
}
Expand Down
36 changes: 26 additions & 10 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
[project]
name = "snowblind"
authors = [{name = "James Davies", email = "[email protected]"}]
authors = [
{name = "James Davies", email = "[email protected]"},
]
description = "Mask cosmic ray showers (snowballs) in JWST data"
readme = "README.md"
license = { file = 'LICENSE' }
license = {file = "LICENSE"}
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: BSD License",
Expand Down Expand Up @@ -31,28 +33,42 @@ test = [
"codecov",
"pytest-cov",
]
cff = [
"cff-from-621",
]

[project.entry-points]
"stpipe.steps" ={jwst = "snowblind:_get_steps"}
"stpipe.steps" ={snowblind = "snowblind:_get_steps"}

[project.urls]
"Bug Tracker" = "https://github.com/mpi-astronomy/snowblind/issues"
"Source Code" = "https://github.com/mpi-astronomy/snowblind"

Homepage = "https://github.com/mpi-astronomy/snowblind"

[build-system]
requires = [
"setuptools>=60",
"setuptools_scm[toml] >=3.4",
"setuptools>=64",
"setuptools_scm>=8",
"wheel",
]
build-backend = "setuptools.build_meta"

[tool.setuptools.packages.find]
where = ["src"]

[tool.setuptools_scm]
write_to = "src/snowblind/_version.py"

[tool.setuptools.packages.find]
where = ["src"]
[tool.cff-from-621]
order = ["cff-version", "message", "title", "abstract", "authors", "urls", "version", "date-released", "type", "keywords"]

[tool.cff-from-621.static]
message = "If you use this software, please cite as below."
license = "BSD-3-Clause"
authors = [
{"name" = "James Davies", "email" = "[email protected]", "orcid" = "https://orcid.org/0000-0002-5079-9098", "affiliation" = "Max Planck Institute for Astronomy"},
]

[tool.setuptools.dynamic]
version = {attr = "snowblind._version.version"}

[tool.flake8]
max-line-length = 130
Expand Down
20 changes: 13 additions & 7 deletions src/snowblind/__init__.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
from . import _version
from importlib.metadata import version, PackageNotFoundError

from .snowblind import SnowblindStep
from .jump_plus import JumpPlusStep
from .rc_selfcal import RcSelfCalStep
from .selfcal import OpenPixelStep
from .persist import PersistenceFlagStep


try:
__version__ = _version.version
except Exception:
__version__ = version(__package__ or __name__)
except PackageNotFoundError:
__version__ = "dev"


__all__ = ['SnowblindStep', 'JumpPlusStep', 'RcSelfCalStep', 'PersistenceFlagStep',
'__version__']
__all__ = [
'__version__',
'SnowblindStep',
'JumpPlusStep',
'OpenPixelStep',
'PersistenceFlagStep',
]


def _get_steps():
Expand All @@ -21,6 +27,6 @@ def _get_steps():
return [
("snowblind.SnowblindStep", 'snowblind', False),
("snowblind.JumpPlusStep", 'jump_plus', False),
("snowblind.RcSelfCalStep", 'rc_selfcal', False),
("snowblind.OpenPixelStep", 'open_pixel', False),
("snowblind.PersistenceFlagStep", 'persist', False),
]
36 changes: 17 additions & 19 deletions src/snowblind/rc_selfcal.py → src/snowblind/selfcal.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
from os.path import commonprefix
import warnings

from astropy.stats import sigma_clipped_stats
from astropy.io import fits
import numpy as np
from jwst import datamodels
from jwst.stpipe import Step


RC = datamodels.dqflags.pixel["RC"]
OPEN = datamodels.dqflags.pixel["OPEN"]
ADJ_OPEN = datamodels.dqflags.pixel["ADJ_OPEN"]
DO_NOT_USE = datamodels.dqflags.pixel["DO_NOT_USE"]


class RcSelfCalStep(Step):
"""Removes cross-shaped and other defects caused by RC-type bad pixels in NIR detectors
class OpenPixelStep(Step):
"""Flags cross-shaped and hot pixel defects caused by open pixels in NIR detectors
Input is an assocation (or glob pattern of files) of all images in visit or program ID
on which ones wishes to do a self-cal. These are split into separate detector stacks,
Expand All @@ -21,20 +22,20 @@ class RcSelfCalStep(Step):
stage 3 pipelines such as tweakreg, skymatch and outlier detection.
Like outlier_detection, the input and output science images are the same, and only the
data quality (DQ) array has new pixels flagged as DO_NOT_USE and RC.
data quality (DQ) array has new pixels flagged as DO_NOT_USE and ADJ_OPEN.
This should be run after flatfielding is finished in image2 pipeline. It is fine to
insert it anywhere in the level3 pipeline before resample.
"""
spec = """
threshold = float(default=3.0) # threshold in sigma to flag hot pixels above median
save_mask = boolean(default=False) # write out per-detector bad-pixel masks
threshold = float(default=3.0) # threshold in sigma to flag hot pixels above local background
save_mask = boolean(default=False) # write out per-detector bad-pixel mask and median
output_use_model = boolean(default=True)
output_use_index = boolean(default=False)
flag_low_signal_pix = boolean(default=False)
"""

class_alias = "rc_selfcal"
class_alias = "open_pixel"

def process(self, input_data):
with datamodels.open(input_data) as images:
Expand All @@ -56,20 +57,17 @@ def process(self, input_data):
mask, median = self.create_hotpixel_mask(image_stack)
self.log.info(f"Flagged {mask.sum()} pixels with {self.threshold} sigma")
if self.save_mask:
fits.HDUList(
fits.PrimaryHDU(
data=mask.astype(np.uint8)
)
).writeto(f"{detector.lower()}_rcflag_mask.fits", overwrite=True)
fits.HDUList(
fits.PrimaryHDU(
data=median
)
).writeto(f"{detector.lower()}_rcflag_median.fits", overwrite=True)
filename_prefix = f"{commonprefix([f.meta.filename for f in models])}_{detector.lower()}_{self.class_alias}"
mask_model = datamodels.MaskModel(data=mask.astype(np.uint8))
mask_model.meta.filename = f"{filename_prefix}.fits"
self.save_model(mask_model, suffix="mask", force=True)
median_model = datamodels.ImageModel(data=median)
median_model.meta.filename = f"{filename_prefix}.fits"
self.save_model(median_model, suffix="median", force=True)

for result in results:
if result.meta.instrument.detector == detector:
result.dq |= (mask * (DO_NOT_USE | RC)).astype(np.uint32)
result.dq |= (mask * (DO_NOT_USE | ADJ_OPEN)).astype(np.uint32)

return results

Expand Down
14 changes: 7 additions & 7 deletions tests/test_rc_selfcal.py → tests/test_selfcal.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import numpy as np
from jwst import datamodels

from snowblind import RcSelfCalStep
from snowblind import OpenPixelStep


RC = datamodels.dqflags.pixel["RC"]
ADJ_OPEN = datamodels.dqflags.pixel["ADJ_OPEN"]
DO_NOT_USE = datamodels.dqflags.pixel["DO_NOT_USE"]
GOOD = datamodels.dqflags.pixel["GOOD"]


def test_init():
step = RcSelfCalStep(threshold=4.5)
step = OpenPixelStep(threshold=4.5)

assert step.threshold == 4.5

Expand Down Expand Up @@ -41,11 +41,11 @@ def test_call():
image.data[8, 8] += 3 * stddev

# Run the step and see if they're recovered
results = RcSelfCalStep.call(images, threshold=3.0)
results = OpenPixelStep.call(images, threshold=3.0)

for result in results:
assert result.dq[2, 2] == RC | DO_NOT_USE
assert result.dq[3, 5] == RC | DO_NOT_USE
assert result.dq[8, 8] == RC | DO_NOT_USE
assert result.dq[2, 2] == ADJ_OPEN | DO_NOT_USE
assert result.dq[3, 5] == ADJ_OPEN | DO_NOT_USE
assert result.dq[8, 8] == ADJ_OPEN | DO_NOT_USE

assert result.dq[5, 5] == GOOD

0 comments on commit cf8a930

Please sign in to comment.