|
| 1 | +import coord |
| 2 | +import galsim |
| 3 | + |
| 4 | + |
| 5 | +class CoaddMixedSceneBuilder(galsim.config.StampBuilder): |
| 6 | + """ |
| 7 | + This class reproduces https://github.com/esheldon/galsim_extra/blob/master/galsim_extra/mixed_scene.py |
| 8 | + with a patch for using the coadd WCS of a DES tile (rather than the SE WCS) |
| 9 | + """ |
| 10 | + |
| 11 | + def setup(self, config, base, xsize, ysize, ignore, logger): |
| 12 | + if 'objects' not in config: |
| 13 | + raise AttributeError('objets field is required for CoaddMixedScene stamp type') |
| 14 | + objects = config['objects'] |
| 15 | + |
| 16 | + # Propagate any stamp rng_num or index_key into the various object fields: |
| 17 | + objects.pop('rng_num', None) # Also remove them from here if necessary. |
| 18 | + objects.pop('index_key', None) |
| 19 | + if not config.get('_propagated_rng_index', False): |
| 20 | + config['_propagated_rng_index'] = True |
| 21 | + rng_num = config.get('rng_num', None) |
| 22 | + index_key = config.get('index_key', None) |
| 23 | + for key in objects.keys(): |
| 24 | + galsim.config.PropagateIndexKeyRNGNum(base[key], index_key, rng_num) |
| 25 | + |
| 26 | + rng = galsim.config.GetRNG(config, base) |
| 27 | + ud = galsim.UniformDeviate(rng) |
| 28 | + p = ud() # A random number between 0 and 1. |
| 29 | + |
| 30 | + # If the user is careful, this will be 1, but if not, renormalize for them. |
| 31 | + norm = float(sum(objects.values())) |
| 32 | + |
| 33 | + if 'obj_type' in config: |
| 34 | + obj_type = galsim.config.ParseValue(config, 'obj_type', base, str)[0] |
| 35 | + obj_type_index = list(objects.keys()).index(obj_type) |
| 36 | + else: |
| 37 | + # Figure out which object field to use |
| 38 | + obj_type = None # So we can check that it was set to something. |
| 39 | + obj_type_index = 0 |
| 40 | + for key, value in objects.items(): |
| 41 | + p1 = value / norm |
| 42 | + if p < p1: |
| 43 | + # Use this object |
| 44 | + obj_type = key |
| 45 | + break |
| 46 | + else: |
| 47 | + p -= p1 |
| 48 | + obj_type_index += 1 |
| 49 | + if obj_type is None: |
| 50 | + # This shouldn't happen, but maybe possible from rounding errors. Use the last one. |
| 51 | + obj_type = list(objects.keys())[-1] |
| 52 | + obj_type_index -= 1 |
| 53 | + logger.error("Error in CoaddMixedScene. Didn't pick an object to use. Using %s", obj_type) |
| 54 | + |
| 55 | + # Save this in the dict so it can be used by e.g. the truth catalog or to do something |
| 56 | + # different depending on which kind of object we have. |
| 57 | + base['current_obj_type'] = obj_type |
| 58 | + base['current_obj_type_index'] = obj_type_index |
| 59 | + base['current_obj'] = None |
| 60 | + |
| 61 | + # Add objects field to the ignore list |
| 62 | + # Also ignore magnify and shear, which we allow here for convenience to act on whichever |
| 63 | + # object ends up being chosen. |
| 64 | + ignore = ignore + ['objects', 'magnify', 'shear', 'obj_type', 'shear_scene'] |
| 65 | + |
| 66 | + stamp_xsize, stamp_ysize, image_pos, world_pos = super( |
| 67 | + CoaddMixedSceneBuilder, |
| 68 | + self |
| 69 | + ).setup(config, base, xsize, ysize, ignore, logger) |
| 70 | + |
| 71 | + if 'shear_scene' in config: |
| 72 | + shear_scene = galsim.config.ParseValue(config, 'shear_scene', base, bool)[0] |
| 73 | + else: |
| 74 | + shear_scene = False |
| 75 | + |
| 76 | + # option to shear the full scene. |
| 77 | + if shear_scene: |
| 78 | + shear = galsim.config.ParseValue(config, 'shear', base, galsim.Shear)[0] |
| 79 | + # Find the center (tangent point) of the scene in RA, DEC. |
| 80 | + scene_center = base["coadd_wcs"].center |
| 81 | + wcs = base['wcs'] |
| 82 | + # world_pos might not be defined yet, so if necessary get it from image_pos. |
| 83 | + if image_pos is not None and world_pos is None: |
| 84 | + world_pos = wcs.toWorld(image_pos) |
| 85 | + if wcs.isCelestial(): |
| 86 | + u, v = scene_center.project(world_pos, projection='gnomonic') |
| 87 | + pos = galsim.PositionD(u.rad, v.rad) |
| 88 | + sheared_pos = pos.shear(shear) |
| 89 | + u2 = sheared_pos.x * coord.radians |
| 90 | + v2 = sheared_pos.y * coord.radians |
| 91 | + world_pos = scene_center.deproject(u2, v2, projection='gnomonic') |
| 92 | + else: |
| 93 | + world_pos = (world_pos - scene_center).shear(shear) + scene_center |
| 94 | + image_pos = wcs.toImage(world_pos) |
| 95 | + |
| 96 | + # Now go on and do the rest of the normal setup. |
| 97 | + return stamp_xsize, stamp_ysize, image_pos, world_pos |
| 98 | + |
| 99 | + def buildProfile(self, config, base, psf, gsparams, logger): |
| 100 | + obj_type = base['current_obj_type'] |
| 101 | + |
| 102 | + # Make the appropriate object using the obj_type field |
| 103 | + obj = galsim.config.BuildGSObject(base, obj_type, gsparams=gsparams, logger=logger)[0] |
| 104 | + # Also save this in case useful for some calculation. |
| 105 | + base['current_obj'] = obj |
| 106 | + |
| 107 | + # Only shear and magnify are allowed, but this general TransformObject function will |
| 108 | + # work to implement those. |
| 109 | + obj, safe = galsim.config.TransformObject(obj, config, base, logger) |
| 110 | + |
| 111 | + if psf: |
| 112 | + if obj: |
| 113 | + return galsim.Convolve(obj, psf) |
| 114 | + else: |
| 115 | + return psf |
| 116 | + else: |
| 117 | + if obj: |
| 118 | + return obj |
| 119 | + else: |
| 120 | + return None |
| 121 | + |
| 122 | + |
| 123 | +galsim.config.stamp.RegisterStampType('CoaddMixedScene', CoaddMixedSceneBuilder()) |
0 commit comments