Skip to content

Commit

Permalink
Merge pull request #446 from LSSTDESC/u/jchiang/bleed_trail_neg_pixels
Browse files Browse the repository at this point in the history
Fix negative pixels that could happen from bleed trails
  • Loading branch information
jchiang87 authored Feb 10, 2024
2 parents 50249cd + 57ec5c3 commit 366a252
Show file tree
Hide file tree
Showing 6 changed files with 26 additions and 6 deletions.
2 changes: 1 addition & 1 deletion imsim/bleed_trails.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ def __call__(self, ypix):
# Off the bottom end, the charge escapes into the electronics.
# We can reduce the excess charge by one full-well-worth.
# These electrons are not added to any pixel though.
self.excess_charge -= self.full_well
self.excess_charge -= min(self.full_well, self.excess_charge)
else:
# Electrons do not escape off the top end, so excess charge is not reduced
# when trying to bleed past the end of the channel.
Expand Down
7 changes: 6 additions & 1 deletion imsim/lsst_image.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import numpy as np
import hashlib
import logging
import galsim
from galsim.config import RegisterImageType, GetAllParams, GetSky, AddNoise
Expand Down Expand Up @@ -298,11 +299,15 @@ def addNoise(self, image, config, base, image_num, obj_num, current_var, logger)
camera = get_camera(self.camera_name)
det_name = base['det_name']
serial_number = camera[det_name].getSerial()
# Note: the regular Python hash function is non-deterministic, which is not good.
# Instead we use hashlib.sha256, which is deterministic and convert that to an integer.
# https://stackoverflow.com/questions/27954892/deterministic-hashing-in-python-3
seed = int(hashlib.sha256(serial_number.encode('UTF-8')).hexdigest(), 16) & 0xFFFFFFFF
# Only apply fringing to e2v sensors.
if serial_number[:3] == 'E2V':
ccd_fringing = CCD_Fringing(true_center=image.wcs.toWorld(image.true_center),
boresight=self.boresight,
seed=hash(serial_number), spatial_vary=True)
seed=seed, spatial_vary=True)
ny, nx = sky.array.shape
xarr, yarr = np.meshgrid(range(nx), range(ny))
logger.info("Apply fringing")
Expand Down
2 changes: 1 addition & 1 deletion imsim/stamp.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ def _getGoodPhotImageSize1(self, obj, keep_sb_level, pixel_scale):
else:
obj_list.append(item)
obj = galsim.Add(obj_list)
elif isinstance(obj.original, galsim.RandomKnots):
elif hasattr(obj, 'original') and isinstance(obj.original, galsim.RandomKnots):
# Handle RandomKnots object directly
obj = obj.original._profile

Expand Down
Binary file added tests/data/neg_pixel_bleed.pickle
Binary file not shown.
13 changes: 13 additions & 0 deletions tests/test_bleed_trails.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
Unit tests for bleed trail implementation.
"""
import unittest
from pathlib import Path
import pickle
import numpy as np
import galsim
import imsim
Expand Down Expand Up @@ -64,6 +66,17 @@ def test_bleed_channel(self):
# Check that bleed trail is centered on original star.
self.assertLessEqual(np.abs(star_center - (nmin + nmax)/2), 1)

def test_bleed_channel_no_negative_pixels(self):
"""This is a reproducer for a case found from running the
code for ComCam simulations."""
# Read in the channel data and full well used.
DATA_DIR = Path(__file__).parent / 'data'
neg_pixel_data = str(DATA_DIR / 'neg_pixel_bleed.pickle')
with open(neg_pixel_data, "rb") as fobj:
channel_data, full_well = pickle.load(fobj)
bled_channel = imsim.bleed_channel(channel_data, full_well)
self.assertTrue(all(bled_channel > 0))

def test_find_channels_with_saturation(self):
"""
Test the function to find channels in an image that have pixels
Expand Down
8 changes: 5 additions & 3 deletions tests/test_fringing.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import numpy as np
import hashlib
import pytest
from imsim import make_batoid_wcs, CCD_Fringing, get_camera
import galsim
Expand All @@ -18,6 +19,7 @@ def test_fringing():
camera = get_camera()
det_name = 'R22_S11'
serial_num = camera[det_name].getSerial()
seed = int(hashlib.sha256(serial_num.encode('UTF-8')).hexdigest(), 16) & 0xFFFFFFFF

xarr, yarr = np.meshgrid(range(4096), range(4004))

Expand All @@ -41,7 +43,7 @@ def test_fringing():

ccd_fringing = CCD_Fringing(true_center=image.wcs.toWorld(image.true_center),
boresight=world_center,
seed=hash(serial_num), spatial_vary=True)
seed=seed, spatial_vary=True)
# Test zero value error
with pytest.raises(ValueError):
ccd_fringing.calculate_fringe_amplitude(xarr, yarr, amplitude=0)
Expand Down Expand Up @@ -91,7 +93,7 @@ def test_fringing():

ccd_fringing_1 = CCD_Fringing(true_center=image.wcs.toWorld(image.true_center),
boresight=world_center,
seed=hash(serial_num), spatial_vary=False)
seed=seed, spatial_vary=False)
fringe_map1 = ccd_fringing_1.calculate_fringe_amplitude(xarr,yarr)

# Try another random location on the focal plane.
Expand All @@ -101,7 +103,7 @@ def test_fringing():

ccd_fringing_2 = CCD_Fringing(true_center=image.wcs.toWorld(image.true_center),
boresight=world_center,
seed=hash(serial_num), spatial_vary=False)
seed=seed, spatial_vary=False)
fringe_map2 = ccd_fringing_2.calculate_fringe_amplitude(xarr,yarr)
# Check if the two fringing maps are indentical.
if np.array_equal(fringe_map1,fringe_map2) != True:
Expand Down

0 comments on commit 366a252

Please sign in to comment.