Skip to content

Commit 366a252

Browse files
authored
Merge pull request #446 from LSSTDESC/u/jchiang/bleed_trail_neg_pixels
Fix negative pixels that could happen from bleed trails
2 parents 50249cd + 57ec5c3 commit 366a252

6 files changed

Lines changed: 26 additions & 6 deletions

File tree

imsim/bleed_trails.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ def __call__(self, ypix):
144144
# Off the bottom end, the charge escapes into the electronics.
145145
# We can reduce the excess charge by one full-well-worth.
146146
# These electrons are not added to any pixel though.
147-
self.excess_charge -= self.full_well
147+
self.excess_charge -= min(self.full_well, self.excess_charge)
148148
else:
149149
# Electrons do not escape off the top end, so excess charge is not reduced
150150
# when trying to bleed past the end of the channel.

imsim/lsst_image.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import numpy as np
2+
import hashlib
23
import logging
34
import galsim
45
from galsim.config import RegisterImageType, GetAllParams, GetSky, AddNoise
@@ -298,11 +299,15 @@ def addNoise(self, image, config, base, image_num, obj_num, current_var, logger)
298299
camera = get_camera(self.camera_name)
299300
det_name = base['det_name']
300301
serial_number = camera[det_name].getSerial()
302+
# Note: the regular Python hash function is non-deterministic, which is not good.
303+
# Instead we use hashlib.sha256, which is deterministic and convert that to an integer.
304+
# https://stackoverflow.com/questions/27954892/deterministic-hashing-in-python-3
305+
seed = int(hashlib.sha256(serial_number.encode('UTF-8')).hexdigest(), 16) & 0xFFFFFFFF
301306
# Only apply fringing to e2v sensors.
302307
if serial_number[:3] == 'E2V':
303308
ccd_fringing = CCD_Fringing(true_center=image.wcs.toWorld(image.true_center),
304309
boresight=self.boresight,
305-
seed=hash(serial_number), spatial_vary=True)
310+
seed=seed, spatial_vary=True)
306311
ny, nx = sky.array.shape
307312
xarr, yarr = np.meshgrid(range(nx), range(ny))
308313
logger.info("Apply fringing")

imsim/stamp.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ def _getGoodPhotImageSize1(self, obj, keep_sb_level, pixel_scale):
303303
else:
304304
obj_list.append(item)
305305
obj = galsim.Add(obj_list)
306-
elif isinstance(obj.original, galsim.RandomKnots):
306+
elif hasattr(obj, 'original') and isinstance(obj.original, galsim.RandomKnots):
307307
# Handle RandomKnots object directly
308308
obj = obj.original._profile
309309

tests/data/neg_pixel_bleed.pickle

15.8 KB
Binary file not shown.

tests/test_bleed_trails.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
Unit tests for bleed trail implementation.
33
"""
44
import unittest
5+
from pathlib import Path
6+
import pickle
57
import numpy as np
68
import galsim
79
import imsim
@@ -64,6 +66,17 @@ def test_bleed_channel(self):
6466
# Check that bleed trail is centered on original star.
6567
self.assertLessEqual(np.abs(star_center - (nmin + nmax)/2), 1)
6668

69+
def test_bleed_channel_no_negative_pixels(self):
70+
"""This is a reproducer for a case found from running the
71+
code for ComCam simulations."""
72+
# Read in the channel data and full well used.
73+
DATA_DIR = Path(__file__).parent / 'data'
74+
neg_pixel_data = str(DATA_DIR / 'neg_pixel_bleed.pickle')
75+
with open(neg_pixel_data, "rb") as fobj:
76+
channel_data, full_well = pickle.load(fobj)
77+
bled_channel = imsim.bleed_channel(channel_data, full_well)
78+
self.assertTrue(all(bled_channel > 0))
79+
6780
def test_find_channels_with_saturation(self):
6881
"""
6982
Test the function to find channels in an image that have pixels

tests/test_fringing.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import numpy as np
2+
import hashlib
23
import pytest
34
from imsim import make_batoid_wcs, CCD_Fringing, get_camera
45
import galsim
@@ -18,6 +19,7 @@ def test_fringing():
1819
camera = get_camera()
1920
det_name = 'R22_S11'
2021
serial_num = camera[det_name].getSerial()
22+
seed = int(hashlib.sha256(serial_num.encode('UTF-8')).hexdigest(), 16) & 0xFFFFFFFF
2123

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

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

4244
ccd_fringing = CCD_Fringing(true_center=image.wcs.toWorld(image.true_center),
4345
boresight=world_center,
44-
seed=hash(serial_num), spatial_vary=True)
46+
seed=seed, spatial_vary=True)
4547
# Test zero value error
4648
with pytest.raises(ValueError):
4749
ccd_fringing.calculate_fringe_amplitude(xarr, yarr, amplitude=0)
@@ -91,7 +93,7 @@ def test_fringing():
9193

9294
ccd_fringing_1 = CCD_Fringing(true_center=image.wcs.toWorld(image.true_center),
9395
boresight=world_center,
94-
seed=hash(serial_num), spatial_vary=False)
96+
seed=seed, spatial_vary=False)
9597
fringe_map1 = ccd_fringing_1.calculate_fringe_amplitude(xarr,yarr)
9698

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

102104
ccd_fringing_2 = CCD_Fringing(true_center=image.wcs.toWorld(image.true_center),
103105
boresight=world_center,
104-
seed=hash(serial_num), spatial_vary=False)
106+
seed=seed, spatial_vary=False)
105107
fringe_map2 = ccd_fringing_2.calculate_fringe_amplitude(xarr,yarr)
106108
# Check if the two fringing maps are indentical.
107109
if np.array_equal(fringe_map1,fringe_map2) != True:

0 commit comments

Comments
 (0)