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

Add option to skip failures in stamp processing. #1238

Merged
merged 5 commits into from
Aug 23, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ jobs:
#CXX: g++

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
# Need this for git commands to work right.
# Also for codecov upload apparently.
Expand All @@ -67,7 +67,7 @@ jobs:
echo 'event.after: ${{ github.event.after }}'

- name: Set up Python ${{ matrix.py }}
uses: actions/setup-python@v2
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.py }}

Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Config Updates
stamp field, as it should. (#1190)
- Added a new ``initial_image`` input type that lets you read in an existing image file
and draw onto it. (#1237)
- Added skip_failures option in stamp fields. (#1238)


New Features
Expand Down
3 changes: 2 additions & 1 deletion docs/config_stamp.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ Some attributes that are allowed for all stamp types are:

* ``offset`` = *pos_value* (optional) An offset in chip coordinates (i.e. pixel units) to apply when drawing the object on the postage stamp.
* ``gsparams`` = *dict* (optional) A dict of (non-default) GSParams items that you want applied to the constructed object.
* ``retry_failures`` = *int_value* (default = 0) How many times to retry the construction of a GSObject if there is any kind of failure. For example, you might have a random shear value that technically may come back with :math:`|g| > 1`, but it should be very rare. So you might set it to retry once or twice in that case. If this is > 0, then after a failure, the code will wait 1 second (in case the failure was related to memory usage on the machine), and then try again up to this many times.
* ``retry_failures`` = *int_value* (default = 0) How many times to retry the construction of a GSObject if there is any kind of failure. For example, you might have a random shear value that technically may come back with :math:`|g| > 1`, but it should be very rare. So you might set it to retry once or twice in that case. If this is > 0, then after a failure, the code will try again up to this many times.
* ``skip_failures`` = *bool_value* (default = False) Whether to skip an object if some aspect of the processing failes (i.e. an exception is raised). This is similar in spirit to the above ``retry_failures``, but more appropriate when the input is not a random value that sometimes causes problems, but rather an input catalog that might have invalid values.
* ``world_pos`` = *pos_value* or *sky_value* (only one of ``world_pos`` and ``image_pos`` is allowed) The position in world coordinates at which to center the object. This is often defined in the ``image`` field, but it can be overridden in the ``stamp`` field.
* ``image_pos`` = *pos_value* (only one of ``world_pos`` and ``image_pos`` is allowed) The position on the full image at which to center the object. This is often defined in the ``image`` field, but it can be overridden in the ``stamp`` field. Note: the object is always centered as nearly as possible on the postage stamp being drawn (unless an explicit ``offset`` is given), but the ``image_pos`` or ``world_pos`` determines where in the larger image this stamp is placed.
* ``sky_pos`` = *sky_value* (default = ``world_pos``) Normally this is just ``world_pos``, but if you are using a Euclidean WCS, then this allows for the ability to specify a location on the sky in case some other type needs it for a calculation.
Expand Down
22 changes: 21 additions & 1 deletion galsim/config/stamp.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,8 +273,18 @@ def BuildStamp(config, obj_num=0, xsize=0, ysize=0, do_noise=True, logger=None):

builder.setupRNG(stamp, config, logger)

if 'skip_failures' in stamp:
skip_failures = ParseValue(stamp, 'skip_failures', config, bool)[0]
else:
skip_failures = False

if 'retry_failures' in stamp:
ntries = ParseValue(stamp,'retry_failures',config,int)[0]
if skip_failures and ntries: # pragma: no cover
# This is definitely covered by the tests.
# I can't figure out why codecov doesn't think so.
raise GalSimConfigValueError(
"Cannot use retry_failures when skip_failures=True", ntries)
# This is how many _re_-tries. Do at least 1, so ntries is 1 more than this.
ntries = ntries + 1
elif ('reject' in stamp or 'min_flux_frac' in stamp or
Expand Down Expand Up @@ -388,7 +398,17 @@ def BuildStamp(config, obj_num=0, xsize=0, ysize=0, do_noise=True, logger=None):
return im, 0.

except Exception as e:
if itry >= ntries:
if skip_failures:
logger.info('Object %d: Caught exception %s',obj_num,str(e))
import traceback
tr = traceback.format_exc()
logger.debug('obj %d: Traceback = %s',obj_num,tr)
logger.info('Skipping this object')
# Now same as SkipThisObject case above.
im = builder.makeStamp(stamp, config, xsize, ysize, logger)
ProcessExtraOutputsForStamp(config, True, logger)
return im, 0.
elif itry >= ntries:
# Then this was the last try. Just re-raise the exception.
logger.info('Object %d: Caught exception %s',obj_num,str(e))
if ntries > 1:
Expand Down
25 changes: 25 additions & 0 deletions tests/test_config_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,7 @@ def HighN(config, base, value_type):
}
}
galsim.config.ProcessInput(config)
orig_config = config.copy()

if False:
logger = logging.getLogger('test_reject')
Expand Down Expand Up @@ -652,6 +653,30 @@ def HighN(config, base, value_type):
logger = galsim.config.LoggerWrapper(None)
galsim.config.BuildFiles(nimages, config, logger=logger)

# Now go back to the original config, and switch to skip_failures rather than retry.
config = orig_config
config['stamp']['skip_failures'] = True

# With this and retry_failures, we get an error.
with assert_raises(galsim.GalSimConfigValueError):
galsim.config.BuildStamps(nimages, config, do_noise=False, logger=logger)

del config['stamp']['retry_failures']
im_list = galsim.config.BuildStamps(nimages, config, do_noise=False, logger=logger)[0]
fluxes = [im.array.sum(dtype=float) if im is not None else 0 for im in im_list]
# Everything gets skipped here.
np.testing.assert_almost_equal(fluxes, 0, decimal=0)

# Dial back some of the rejections and skips to get some images drawn.
del config['stamp']['reject']
del config['stamp']['min_flux_frac']
del config['stamp']['max_snr']
del config['stamp']['skip']
del config['stamp']['quick_skip']
im_list = galsim.config.BuildStamps(nimages, config, do_noise=False, logger=logger)[0]
fluxes = [im.array.sum(dtype=float) if im is not None else 0 for im in im_list]
expected_fluxes = [0, 76673, 0, 0, 24074, 0, 0, 9124, 0, 0, 0]
np.testing.assert_almost_equal(fluxes, expected_fluxes, decimal=0)

@timer
def test_snr():
Expand Down
2 changes: 1 addition & 1 deletion tests/test_photon_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -877,7 +877,7 @@ def test_dcr():
err_msg="base_psf + dcr in photon_ops didn't match")

# Including the wavelength sampler with chromatic drawing is not necessary, but is allowed.
# (Mostly in case someone wants to do something a little different w.r.t. wavelenght sampling.
# (Mostly in case someone wants to do something a little different w.r.t. wavelength sampling.
photon_ops = [wave_sampler, base_PSF, dcr]
star.drawImage(bandpass, image=im8, method='phot', rng=rng, photon_ops=photon_ops)
printval(im8, im6, show=False)
Expand Down