diff --git a/python/desc/imsim/imSim.py b/python/desc/imsim/imSim.py index f84b3d94..b3dbb2b8 100644 --- a/python/desc/imsim/imSim.py +++ b/python/desc/imsim/imSim.py @@ -536,45 +536,6 @@ def parsePhoSimInstanceFile(fileName, sensor_list, numRows=None): ([], gs_object_dict)) -class GsObjectDict: - """ - Dictionary-like class to provide access to lists of - GalSimCelestialObjects from an instance catalog on a per-sensor - basis. This class uses InstCatTrimmer to downselect the object - entries via acceptance cones centered on the sensor of interest - and to defer the creation of the GalSimCelestialObjects until - the data for the specified sensor are requested. - """ - def __init__(self, instcat_trimmer, phot_params, radius=0.18): - """ - Parameters - ---------- - instcat_trimmer: InstCatTrimmer - This object manages the GalSimCelestialObject creation. - phot_params: PhotometricParameters - Photometric parameter info for the visit. - radius: float [0.18] - Acceptance cone radius, in degrees, for downselecting objects - for a single CCD. - """ - self.instcat_trimmer = instcat_trimmer - self.phot_params = phot_params - self.radius = radius - - def __iter__(self): - for detector in self.instcat_trimmer._camera: - yield detector.getName() - - def __getitem__(self, chip_name): - object_lines \ - = self.instcat_trimmer.get_object_entries(chip_name, - radius=self.radius) - obs_md = self.instcat_trimmer.obs_md - file_name = self.instcat_trimmer.instcat_file - return GsObjectList(object_lines, obs_md, self.phot_params, file_name, - chip_name=chip_name) - - class GsObjectList: """ List-like class to provide access to lists of objects from an diff --git a/python/desc/imsim/trim.py b/python/desc/imsim/trim.py index e44e7ba2..c24834a6 100644 --- a/python/desc/imsim/trim.py +++ b/python/desc/imsim/trim.py @@ -1,6 +1,7 @@ """ Function to apply chip-centered acceptance cones on instance catalogs. """ +from collections import defaultdict import numpy as np import lsst.sims.coordUtils from lsst.sims.utils import _angularSeparation @@ -94,23 +95,14 @@ def get_object_entries(self, chip_name, radius=0.18, sort_magnorm=True): Returns ------- - list: list of object entries from the original instance catalog. + list, int: (list of object entries from the original instance catalog, + number of sersic objects for minsource application) - Notes - ----- - This function applies the 'minsource' criterion to the sersic - galaxies in the instance catalog if 'minsource' is included in - the instance catalog commands. """ ra0, dec0 = self.compute_chip_center(chip_name) seps = degrees_separation(ra0, dec0, self._ra, self._dec) index = np.where(seps < radius) - if (self.trimmer.minsource is not None and - sum(self._sersic[index]) < self.trimmer.minsource): - # Apply the minsource criterion. - return [] - # Collect the selected objects. selected = [self.object_lines[i] for i in index[0]] if sort_magnorm: @@ -118,7 +110,7 @@ def get_object_entries(self, chip_name, radius=0.18, sort_magnorm=True): sorted_index = np.argsort(self._magnorm[index]) selected = [selected[i] for i in sorted_index] - return selected + return selected, sum(self._sersic[index]) class InstCatTrimmer(dict): """ @@ -137,7 +129,7 @@ class InstCatTrimmer(dict): """ def __init__(self, instcat, sensor_list, chunk_size=int(3e5), - radius=0.18, numRows=None): + radius=0.18, numRows=None, minsource=None): """ Parameters ---------- @@ -155,10 +147,15 @@ def __init__(self, instcat, sensor_list, chunk_size=int(3e5), sensor. numRows: int [None] Maximum number of rows to read in from the instance catalog. + minsource: int [None] + Minimum number of galaxies in a given sensor acceptance cone. + If not None, this overrides the value in the instance catalog. """ super(InstCatTrimmer, self).__init__() self.instcat_file = instcat self._read_commands() + if minsource is not None: + self.minsource = minsource self._process_objects(sensor_list, chunk_size, radius=radius, numRows=numRows) @@ -170,11 +167,13 @@ def _process_objects(self, sensor_list, chunk_size, radius=0.18, for each sensor using the Disaggregator class to apply the acceptance cone cut centered on each sensor. """ + num_gals = defaultdict(lambda: 0) self.update({sensor: [] for sensor in sensor_list}) with desc.imsim.fopen(self.instcat_file, mode='rt') as fd: nread = 0 while numRows is None or nread < numRows: object_lines = [] + ichunk = 0 for ichunk, line in zip(range(chunk_size), fd): nread += 1 if not line.startswith('object'): @@ -182,11 +181,16 @@ def _process_objects(self, sensor_list, chunk_size, radius=0.18, object_lines.append(line) disaggregator = Disaggregator(object_lines, self) for sensor in self: - obj_list = disaggregator.get_object_entries(sensor, - radius=radius) + obj_list, nsersic = disaggregator\ + .get_object_entries(sensor, radius=radius) self[sensor].extend(obj_list) + num_gals[sensor] += nsersic if ichunk < chunk_size - 1: break + for sensor in self: + # Apply minsource criterion on galaxies. + if self.minsource is not None and num_gals[sensor] < self.minsource: + self[sensor] = [] def _read_commands(self): """Read in the commands from the instance catalog.""" diff --git a/tests/test_trimmer.py b/tests/test_trimmer.py new file mode 100644 index 00000000..67633808 --- /dev/null +++ b/tests/test_trimmer.py @@ -0,0 +1,39 @@ +""" +Unit tests for InstCatTrimmer class. +""" +import os +import unittest +import desc.imsim + + +class InstCatTrimmerTestCase(unittest.TestCase): + """ + TestCase class for InstCatTrimmer. + """ + def setUp(self): + pass + + def tearDown(self): + pass + + def test_InstCatTrimmer(self): + """Unit test for InstCatTrimmer class.""" + instcat = os.path.join(os.environ['IMSIM_DIR'], 'tests', + 'tiny_instcat.txt') + sensor = 'R:2,2 S:1,1' + + # Check the application of minsource. + objs = desc.imsim.InstCatTrimmer(instcat, [sensor], minsource=10) + self.assertEqual(len(objs[sensor]), 24) + + objs = desc.imsim.InstCatTrimmer(instcat, [sensor], minsource=12) + self.assertEqual(len(objs[sensor]), 0) + + # Check various values of chunk_size. + for chunk_size in (5, 10, 100): + objs = desc.imsim.InstCatTrimmer(instcat, [sensor], minsource=None, + chunk_size=chunk_size) + self.assertEqual(len(objs[sensor]), 24) + +if __name__ == '__main__': + unittest.main()