Пример #1
0
def GetRNG(config, base, logger=None, tag=''):
    """Get the appropriate current rng according to whatever the current index_key is.

    If a logger is provided, then it will emit a warning if there is no current rng setup.

    @param config           The configuration dict for the current item being worked on.
    @param base             The base configuration dict.
    @param logger           If given, a logger object to log progress. [default: None]
    @param tag              If given, an appropriate name for the current item to use in the
                            warning message. [default: '']

    @returns either the appropriate rng for the current index_key or None
    """
    logger = LoggerWrapper(logger)
    index, index_key = GetIndex(config, base)
    logger.debug("GetRNG for %s: %s", index_key, index)

    rng_num = config.get('rng_num', 0)
    if rng_num != 0:
        if int(rng_num) != rng_num:
            raise galsim.GalSimConfigValueError("rng_num must be an integer",
                                                rng_num)
        rngs = base.get(index_key + '_rngs', None)
        if rngs is None:
            raise galsim.GalSimConfigError(
                "rng_num is only allowed when image.random_seed is a list")
        if rng_num < 0 or rng_num > len(rngs):
            raise galsim.GalSimConfigError(
                "rng_num is invalid.  Must be in [0,%d]" % (len(rngs)))
        rng = rngs[int(rng_num)]
    else:
        rng = base.get(index_key + '_rng', None)

    if rng is None:
        logger.debug("No index_key_rng.  Use base[rng]")
        rng = base.get('rng', None)

    if rng is None and logger:
        # Only report the warning the first time.
        rng_tag = tag + '_reported_no_rng'
        if rng_tag not in base:
            base[rng_tag] = True
            logger.warning(
                "No base['rng'] available for %s.  Using /dev/urandom.", tag)

    return rng
Пример #2
0
def SetupConfigImageNum(config, image_num, obj_num, logger=None):
    """Do the basic setup of the config dict at the image processing level.

    Includes:
    - Set config['image_num'] = image_num
    - Set config['obj_num'] = obj_num
    - Set config['index_key'] = 'image_num'
    - Make sure config['image'] exists
    - Set default config['image']['type'] to 'Single' if not specified
    - Check that the specified image type is valid.

    Parameters:
        config:         The configuration dict.
        image_num:      The current image number.
        obj_num:        The first object number in the image.
        logger:         If given, a logger object to log progress. [default: None]
    """
    logger = galsim.config.LoggerWrapper(logger)
    config['image_num'] = image_num
    config['obj_num'] = obj_num
    config['index_key'] = 'image_num'

    # Make config['image'] exist if it doesn't yet.
    if 'image' not in config:
        config['image'] = {}
    image = config['image']
    if not isinstance(image, dict):
        raise galsim.GalSimConfigError("config.image is not a dict.")

    if 'file_num' not in config:
        config['file_num'] = 0

    if 'type' not in image:
        image['type'] = 'Single'
    image_type = image['type']
    if image_type not in valid_image_types:
        raise galsim.GalSimConfigValueError("Invalid image.type.", image_type,
                                            valid_image_types)

    # In case this hasn't been done yet.
    galsim.config.SetupInput(config, logger)

    # Build the rng to use at the image level.
    seed = galsim.config.SetupConfigRNG(config, logger=logger)
    logger.debug('image %d: seed = %d', image_num, seed)
Пример #3
0
def GetNObjForImage(config, image_num):
    """
    Get the number of objects that will be made for the image number image_num based on
    the information in the config dict.

    Parameters:
        config:         The configuration dict.
        image_num:      The current image number.

    Returns:
        the number of objects
    """
    image = config.get('image', {})
    image_type = image.get('type', 'Single')
    if image_type not in valid_image_types:
        raise galsim.GalSimConfigValueError("Invalid image.type.", image_type,
                                            valid_image_types)
    return valid_image_types[image_type].getNObj(image, config, image_num)
Пример #4
0
def GetNObjForFile(config, file_num, image_num):
    """
    Get the number of objects that will be made for each image built as part of the file file_num,
    which starts at image number image_num, based on the information in the config dict.

    @param config           The configuration dict.
    @param file_num         The current file number.
    @param image_num        The current image number.

    @returns a list of the number of objects in each image [ nobj0, nobj1, nobj2, ... ]
    """
    output = config.get('output', {})
    output_type = output.get('type', 'Fits')
    if output_type not in valid_output_types:
        raise galsim.GalSimConfigValueError("Invalid output.type.",
                                            output_type, valid_output_types)
    return valid_output_types[output_type].getNObjPerImage(
        output, config, file_num, image_num)
Пример #5
0
def GetNImagesForFile(config, file_num):
    """
    Get the number of images that will be made for the file number file_num, based on the
    information in the config dict.

    Parameters:
        config:     The configuration dict.
        file_num:   The current file number.

    Returns:
        the number of images
    """
    output = config.get('output', {})
    output_type = output.get('type', 'Fits')
    if output_type not in valid_output_types:
        raise galsim.GalSimConfigValueError("Invalid output.type.",
                                            output_type, valid_output_types)
    return valid_output_types[output_type].getNImages(output, config, file_num)
Пример #6
0
def AddExtraOutputHDUs(config, main_data, logger=None):
    """Write the extra output objects to either HDUS or images as appropriate and add them
    to the existing data.

    This gets run at the end of the functions for building the regular output files.

    Note: the extra items must have hdu numbers ranging continuously (in any order) starting
    at len(data).  Typically first = 1, since the main image is the primary HDU, numbered 0.

    @param config       The configuration dict.
    @param main_data    The main file data as a list of images.  Usually just [image] where
                        image is the primary image to be written to the output file.
    @param logger       If given, a logger object to log progress. [default: None]

    @returns data with additional hdus added
    """
    output = config['output']
    hdus = {}
    for key in (k for k in valid_extra_outputs.keys() if k in output):
        field = output[key]
        if 'hdu' in field:
            hdu = galsim.config.ParseValue(field,'hdu',config,int)[0]
        else:  # pragma: no cover  (it is covered, but codecov wrongly thinks it isn't.)
            # If no hdu, then probably writing to file
            continue
        if hdu <= 0 or hdu in hdus:
            raise galsim.GalSimConfigValueError("hdu is invalid or a duplicate.",hdu)

        builder = config['extra_builder'][key]

        # Do any final processing that needs to happen.
        builder.ensureFinalized(field, config, main_data, logger)

        # Build the HDU for this output object.
        hdus[hdu] = builder.writeHdu(field,config,logger)

    first = len(main_data)
    for h in range(first,len(hdus)+first):
        if h not in hdus:
            raise galsim.GalSimConfigError("Cannot skip hdus.  No output found for hdu %d"%h)
    # Turn hdus into a list (in order)
    hdulist = [ hdus[k] for k in range(first,len(hdus)+first) ]
    return main_data + hdulist
Пример #7
0
def AddNoise(config, im, current_var=0., logger=None):
    """
    Add noise to an image according to the noise specifications in the noise dict.

    Parameters:
        config:         The configuration dict
        im:             The image onto which to add the noise
        current_var:    The current noise variance present in the image already [default: 0]
        logger:         If given, a logger object to log progress. [default: None]
    """
    logger = galsim.config.LoggerWrapper(logger)
    if 'noise' in config['image']:
        noise = config['image']['noise']
    else:  # No noise.
        return
    if not isinstance(noise, dict):
        raise galsim.GalSimConfigError("image.noise is not a dict.")

    # Default is Poisson
    noise_type = noise.get('type', 'Poisson')
    if noise_type not in valid_noise_types:
        raise galsim.GalSimConfigValueError("Invalid noise.type.", noise_type,
                                            valid_noise_types)

    # We need to use image_num for the index_key, but if we are running this from the stamp
    # building phase, then we want to use obj_num_rng for the noise rng.  So get the rng now
    # before we change config['index_key'].
    index, orig_index_key = galsim.config.GetIndex(noise, config)
    rng = galsim.config.GetRNG(noise, config)

    # This makes sure draw_method is properly copied over and given a default value.
    galsim.config.stamp.SetupConfigObjNum(config, config.get('obj_num', 0),
                                          logger)
    draw_method = galsim.config.GetCurrentValue('draw_method', config['stamp'],
                                                str, config)

    builder = valid_noise_types[noise_type]
    config['index_key'] = 'image_num'
    var = builder.addNoise(noise, config, im, rng, current_var, draw_method,
                           logger)
    config['index_key'] = orig_index_key

    return var
Пример #8
0
    def setup(self, config, base, xsize, ysize, ignore, logger):
        """Do the initialization and setup for the Ring type.

        Most of the work is done by SetupBasic, but we do need to check that the required parameters
        are present, and also that no additional parameters are present.

        Parameters:
            config:     The configuration dict for the stamp field.
            base:       The base configuration dict.
            xsize:      The xsize of the image to build (if known).
            ysize:      The ysize of the image to build (if known).
            ignore:     A list of parameters that are allowed to be in config that we can
                        ignore here. i.e. it won't be an error if these parameters are present.
            logger:     If given, a logger object to log progress.

        Returns:
            xsize, ysize, image_pos, world_pos
        """
        req = { 'num' : int }
        opt = { 'full_rotation' : galsim.Angle , 'index' : int }
        # Ignore the transformation specifications that are allowed in stamp for Ring types.
        ignore = ignore + [
            'dilate', 'dilation', 'ellip', 'rotate', 'rotation', 'scale_flux',
            'magnify', 'magnification', 'shear', 'shift' ]

        params = galsim.config.GetAllParams(config, base, req=req, opt=opt, ignore=ignore)[0]

        num = params['num']
        if num <= 0:
            raise galsim.GalSimConfigValueError("Attribute num for gal.type == Ring must be > 0",
                                                num)

        # Setup the indexing sequence if it hasn't been specified using the number of items.
        galsim.config.SetDefaultIndex(config, num)

        # Set the default full_rotation to pi radians
        if 'full_rotation' not in params:
            import math
            config['full_rotation'] = math.pi * galsim.radians

        # Now go on and do the base class setup.
        ignore = ignore + list(req) + list(opt)
        return super(RingBuilder, self).setup(config, base, xsize, ysize, ignore, logger)
Пример #9
0
def SetupConfigFileNum(config, file_num, image_num, obj_num, logger=None):
    """Do the basic setup of the config dict at the file processing level.

    Includes:
    - Set config['file_num'] = file_num
    - Set config['image_num'] = image_num
    - Set config['obj_num'] = obj_num
    - Set config['index_key'] = 'file_num'
    - Set config['start_image_num'] = image_num
    - Set config['start_obj_num'] = obj_num
    - Make sure config['output'] exists
    - Set default config['output']['type'] to 'Fits' if not specified
    - Check that the specified output type is valid.

    Parameters:
        config:     A configuration dict.
        file_num:   The current file_num. (If file_num=None, then don't set file_num or
                    start_obj_num items in the config dict.)
        image_num:  The current image_num.
        obj_num:    The current obj_num.
        logger:     If given, a logger object to log progress. [default: None]
    """
    logger = galsim.config.LoggerWrapper(logger)
    config['file_num'] = file_num
    config['start_obj_num'] = obj_num
    config['start_image_num'] = image_num
    config['image_num'] = image_num
    config['obj_num'] = obj_num
    config['index_key'] = 'file_num'

    if 'output' not in config:
        config['output'] = {}
    if 'type' not in config['output']:
        config['output']['type'] = 'Fits'

    # Check that the type is valid
    output_type = config['output']['type']
    if output_type not in valid_output_types:
        raise galsim.GalSimConfigValueError("Invalid output.type.",
                                            output_type, valid_output_types)
Пример #10
0
def _GenerateFromPowerSpectrumMagnification(config, base, value_type):
    """@brief Return a magnification calculated from a PowerSpectrum object.
    """
    power_spectrum = galsim.config.GetInputObj('power_spectrum', config, base,
                                               'PowerSpectrumMagnification')
    logger = power_spectrum.logger

    if 'world_pos' not in base:
        raise galsim.GalSimConfigError(
            "PowerSpectrumMagnification requested, but no position defined.")
    pos = base['world_pos']

    opt = {'max_mu': float, 'num': int}
    kwargs = galsim.config.GetAllParams(config, base, opt=opt)[0]

    with warnings.catch_warnings(record=True) as w:
        mu = power_spectrum.getMagnification(pos)
    if len(w) > 0:
        log_level = (logging.WARNING if 'current_image' in base
                     and base['current_image'].outer_bounds.includes(pos) else
                     logging.DEBUG)
        for ww in w:
            logger.log(log_level, 'obj %d: %s', base['obj_num'], ww.message)

    max_mu = kwargs.get('max_mu', 25.)
    if not max_mu > 0.:
        raise galsim.GalSimConfigValueError(
            "Invalid max_mu for type = PowerSpectrumMagnification (must be > 0)",
            max_mu)

    if mu < 0 or mu > max_mu:
        logger.warning(
            'obj %d: Warning: PowerSpectrum mu = %f means strong lensing. ' %
            (base['obj_num'], mu) + 'Using mu=%f' % max_mu)
        mu = max_mu

    logger.debug('obj %d: PowerSpectrum mu = %s', base['obj_num'], mu)
    return mu, False
Пример #11
0
def AddNoiseVariance(config, im, include_obj_var=False, logger=None):
    """
    Add the noise variance to an image according to the noise specifications in the noise dict.
    Typically, this is used for building a weight map, which is typically the inverse variance.

    Parameters:
        config:             The configuration dict
        im:                 The image onto which to add the variance values
        include_obj_var:    Whether to add the variance from the object photons for noise
                            models that have a component based on the number of photons.
                            Note: if this is True, the returned variance will not include this
                            contribution to the noise variance.  [default: False]
        logger:             If given, a logger object to log progress. [default: None]

    Returns:
        the variance in the image
    """
    logger = galsim.config.LoggerWrapper(logger)
    if 'noise' in config['image']:
        noise = config['image']['noise']
    else:  # No noise.
        return
    if not isinstance(noise, dict):
        raise galsim.GalSimConfigError("image.noise is not a dict.")

    noise_type = noise.get('type', 'Poisson')
    if noise_type not in valid_noise_types:
        raise galsim.GalSimConfigValueError("Invalid noise.type.", noise_type,
                                            valid_noise_types)

    index, orig_index_key = galsim.config.GetIndex(noise, config)
    config['index_key'] = 'image_num'

    builder = valid_noise_types[noise_type]
    builder.addNoiseVariance(noise, config, im, include_obj_var, logger)
    config['index_key'] = orig_index_key
Пример #12
0
def BuildStamp(config,
               obj_num=0,
               xsize=0,
               ysize=0,
               do_noise=True,
               logger=None):
    """
    Build a single stamp image using the given config file

    Parameters:
        config:         A configuration dict.
        obj_num:        If given, the current obj_num [default: 0]
        xsize:          The xsize of the stamp to build (if known). [default: 0]
        ysize:          The ysize of the stamp to build (if known). [default: 0]
        do_noise:       Whether to add noise to the image (according to config['noise']).
                        [default: True]
        logger:         If given, a logger object to log progress. [default: None]

    Returns:
        the tuple (image, current_var)
    """
    logger = galsim.config.LoggerWrapper(logger)
    SetupConfigObjNum(config, obj_num, logger)

    stamp = config['stamp']
    stamp_type = stamp['type']
    if stamp_type not in valid_stamp_types:
        raise galsim.GalSimConfigValueError("Invalid stamp.type.", stamp_type,
                                            valid_stamp_types)
    builder = valid_stamp_types[stamp_type]

    # Add 1 to the seed here so the first object has a different rng than the file or image.
    seed = galsim.config.SetupConfigRNG(config, seed_offset=1, logger=logger)
    logger.debug('obj %d: seed = %d', obj_num, seed)

    if 'retry_failures' in stamp:
        ntries = galsim.config.ParseValue(stamp, 'retry_failures', config,
                                          int)[0]
        # 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 'min_snr' in stamp
          or 'max_snr' in stamp):
        # Still impose a maximum number of tries to prevent infinite loops.
        ntries = 20
    else:
        ntries = 1

    itry = 0
    while True:
        itry += 1  # itry increases from 1..ntries at which point we just reraise the exception.

        # The rest of the stamp generation stage is wrapped in a try/except block.
        # If we catch an exception, we continue the for loop to try again.
        # On the last time through, we reraise any exception caught.
        # If no exception is thrown, we simply break the loop and return.
        try:

            # Do the necessary initial setup for this stamp type.
            xsize, ysize, image_pos, world_pos = builder.setup(
                stamp, config, xsize, ysize, stamp_ignore, logger)

            # Save these values for possible use in Evals or other modules
            SetupConfigStampSize(config, xsize, ysize, image_pos, world_pos,
                                 logger)
            stamp_center = config['stamp_center']
            if xsize:
                logger.debug('obj %d: xsize,ysize = %s,%s', obj_num, xsize,
                             ysize)
            if image_pos:
                logger.debug('obj %d: image_pos = %s', obj_num, image_pos)
            if world_pos:
                logger.debug('obj %d: world_pos = %s', obj_num, world_pos)
            if stamp_center:
                logger.debug('obj %d: stamp_center = %s', obj_num,
                             stamp_center)

            # Get the global gsparams kwargs.  Individual objects can add to this.
            gsparams = {}
            if 'gsparams' in stamp:
                gsparams = galsim.config.UpdateGSParams(
                    gsparams, stamp['gsparams'], config)

            # Note: Skip is different from Reject.
            #       Skip means we return None for this stamp image and continue on.
            #       Reject means we retry this object using the same obj_num.
            #       This has implications for the total number of objects as well as
            #       things like ring tests that rely on objects being made in pairs.
            #
            #       Skip is also different from prof = None.
            #       If prof is None, then the user indicated that no object should be
            #       drawn on this stamp, but that a noise image is still desired.
            if 'skip' in stamp:
                skip = galsim.config.ParseValue(stamp, 'skip', config, bool)[0]
                logger.info('Skipping object %d', obj_num)
            else:
                skip = False

            if not skip:
                try:
                    psf = galsim.config.BuildGSObject(config,
                                                      'psf',
                                                      gsparams=gsparams,
                                                      logger=logger)[0]
                    prof = builder.buildProfile(stamp, config, psf, gsparams,
                                                logger)
                except galsim.config.gsobject.SkipThisObject as e:
                    logger.debug('obj %d: Caught SkipThisObject: e = %s',
                                 obj_num, e.msg)
                    logger.info('Skipping object %d', obj_num)
                    skip = True

            im = builder.makeStamp(stamp, config, xsize, ysize, logger)

            if not skip:
                method = galsim.config.ParseValue(stamp, 'draw_method', config,
                                                  str)[0]
                if method not in valid_draw_methods:
                    raise galsim.GalSimConfigValueError(
                        "Invalid draw_method.", method, valid_draw_methods)

                offset = config['stamp_offset']
                if 'offset' in stamp:
                    offset += galsim.config.ParseValue(stamp, 'offset', config,
                                                       galsim.PositionD)[0]
                logger.debug('obj %d: stamp_offset = %s, offset = %s', obj_num,
                             config['stamp_offset'], offset)

                skip = builder.updateSkip(prof, im, method, offset, stamp,
                                          config, logger)

            if not skip:
                im = builder.draw(prof, im, method, offset, stamp, config,
                                  logger)

                scale_factor = builder.getSNRScale(im, stamp, config, logger)
                im, prof = builder.applySNRScale(im, prof, scale_factor,
                                                 method, logger)

            # Set the origin appropriately
            if im is None:
                # Note: im might be None here if the stamp size isn't given and skip==True.
                pass
            elif stamp_center:
                im.setCenter(stamp_center)
            else:
                im.setOrigin(config.get('image_origin', galsim.PositionI(1,
                                                                         1)))

            # Store the current stamp in the base-level config for reference
            config['current_stamp'] = im
            # This is also information that the weight image calculation needs
            config['do_noise_in_stamps'] = do_noise

            # Check if this object should be rejected.
            if not skip:
                reject = builder.reject(stamp, config, prof, psf, im, logger)
                if reject:
                    if itry < ntries:
                        logger.warning(
                            'Object %d: Rejecting this object and rebuilding',
                            obj_num)
                        builder.reset(config, logger)
                        continue
                    else:
                        raise galsim.GalSimConfigError(
                            "Rejected an object %d times. If this is expected, "
                            "you should specify a larger stamp.retry_failures."
                            % (ntries))

            galsim.config.ProcessExtraOutputsForStamp(config, skip, logger)

            # We always need to do the whiten step here in the stamp processing
            if not skip:
                current_var = builder.whiten(prof, im, stamp, config, logger)
                if current_var != 0.:
                    logger.debug(
                        'obj %d: whitening noise brought current var to %f',
                        config['obj_num'], current_var)
            else:
                current_var = 0.

            # Sometimes, depending on the image type, we go on to do the rest of the noise as well.
            if do_noise and not skip:
                im, current_var = builder.addNoise(stamp, config, im, skip,
                                                   current_var, logger)

            return im, current_var

        except KeyboardInterrupt:
            raise
        except Exception as e:
            if 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:
                    logger.error(
                        'Object %d: Too many exceptions/rejections for this object. Aborting.',
                        obj_num)
                raise
            else:
                logger.info('Object %d: Caught exception %s', obj_num, str(e))
                logger.info('This is try %d/%d, so trying again.', itry,
                            ntries)
                import traceback
                tr = traceback.format_exc()
                logger.debug('obj %d: Traceback = %s', obj_num, tr)
                # Need to remove the "current"s from the config dict.  Otherwise,
                # the value generators will do a quick return with the cached value.
                builder.reset(config, logger)
                continue
Пример #13
0
def BuildWCS(config, key, base, logger=None):
    """Read the wcs parameters from config[key] and return a constructed wcs object.

    @param config       A dict with the configuration information. (usually base['image'])
    @param key          The key name in config indicating which object to build.
    @param base         The base dict of the configuration.
    @param logger       Optionally, provide a logger for logging debug statements. [default: None]

    @returns a BaseWCS instance
    """
    logger = galsim.config.LoggerWrapper(logger)
    logger.debug('image %d: Start BuildWCS key = %s', base.get('image_num', 0),
                 key)

    try:
        param = config[key]
    except KeyError:
        # Default if no wcs is to use PixelScale
        if 'pixel_scale' in config:
            scale = galsim.config.ParseValue(config, 'pixel_scale', base,
                                             float)[0]
        else:
            scale = 1.0
        return galsim.PixelScale(scale)

    # Check for direct value, else get the wcs type
    if isinstance(param, galsim.BaseWCS):
        return param
    elif param == str(param) and (param[0] == '$' or param[0] == '@'):
        return galsim.config.ParseValue(config, key, base, None)[0]
    elif not isinstance(param, dict):
        raise galsim.GalSimConfigError(
            "wcs must be either a BaseWCS or a dict")
    elif 'type' in param:
        wcs_type = param['type']
    else:
        wcs_type = 'PixelScale'

    # For these two, just do the usual ParseValue function.
    if wcs_type in ('Eval', 'Current'):
        return galsim.config.ParseValue(config, key, base, None)[0]

    # Check if we can use the current cached object
    _, orig_index_key = galsim.config.GetIndex(param, base)
    base['index_key'] = 'image_num'
    index, _ = galsim.config.GetIndex(param, base)
    if 'current' in param:
        cwcs, csafe, cvalue_type, cindex, cindex_key = param['current']
        if cindex == index:
            logger.debug('image %d: The wcs object is already current',
                         base.get('image_num', 0))
            return cwcs

    # Special case: origin == center means to use image_center for the wcs origin
    if 'origin' in param and param['origin'] == 'center':
        origin = base['image_center']
        param['origin'] = origin

    if wcs_type not in valid_wcs_types:
        raise galsim.GalSimConfigValueError("Invalid image.wcs.type.",
                                            wcs_type, valid_wcs_types)
    logger.debug('image %d: Building wcs type %s', base.get('image_num', 0),
                 wcs_type)
    builder = valid_wcs_types[wcs_type]
    wcs = builder.buildWCS(param, base, logger)
    logger.debug('image %d: wcs = %s', base.get('image_num', 0), wcs)

    param['current'] = wcs, False, None, index, 'image_num'
    base['index_key'] = orig_index_key

    return wcs
Пример #14
0
def _GenerateFromSequence(config, base, value_type):
    """@brief Return next in a sequence of integers
    """
    ignore = ['default']
    opt = {
        'first': value_type,
        'last': value_type,
        'step': value_type,
        'repeat': int,
        'nitems': int,
        'index_key': str
    }
    kwargs, safe = GetAllParams(config, base, opt=opt, ignore=ignore)

    step = kwargs.get('step', 1)
    first = kwargs.get('first', 0)
    repeat = kwargs.get('repeat', 1)
    last = kwargs.get('last', None)
    nitems = kwargs.get('nitems', None)

    if repeat <= 0:
        raise galsim.GalSimConfigValueError(
            "Invalid repeat for type = Sequence (must be > 0)", repeat)
    if last is not None and nitems is not None:
        raise galsim.GalSimConfigError(
            "At most one of the attributes last and nitems is allowed for type = Sequence"
        )

    index, index_key = galsim.config.GetIndex(kwargs, base, is_sequence=True)
    #print('in GenFromSequence: index = ',index,index_key)

    if value_type is bool:
        # Then there are only really two valid sequences: Either 010101... or 101010...
        # Aside from the repeat value of course.
        if first:
            first = 1
            step = -1
            nitems = 2
        else:
            first = 0
            step = 1
            nitems = 2

    elif value_type is float:
        if last is not None:
            nitems = int((last - first) / step + 0.5) + 1
    else:
        if last is not None:
            nitems = (last - first) // step + 1
    #print('nitems = ',nitems)
    #print('repeat = ',repeat)

    index = index // repeat
    #print('index => ',index)

    if nitems is not None and nitems > 0:
        index = index % nitems
        #print('index => ',index)

    value = first + index * step
    #print(base[index_key],'Sequence index = %s + %d*%s = %s'%(first,index,step,value))
    return value, False
Пример #15
0
def ParseValue(config, key, base, value_type):
    """@brief Read or generate a parameter value from config.

    @returns the tuple (value, safe).
    """
    param = config[key]
    #print('ParseValue for key = ',key,', value_type = ',str(value_type))
    #print('param = ',param)
    #print('nums = ',base.get('file_num',0), base.get('image_num',0), base.get('obj_num',0))

    if isinstance(param, dict):

        type_name = param.get('type', None)
        #print('type = ',type_name)
        #print(param['type'], value_type)

        # Check what index key we want to use for this value.
        index, index_key = galsim.config.GetIndex(
            param, base, is_sequence=(type_name == 'Sequence'))
        #print('index, index_key = ',index,index_key)

        if '_gen_fn' in param:
            generate_func = param['_gen_fn']

            if 'current' in param:
                cval, csafe, cvalue_type, cindex, cindex_key = param['current']
                if 'repeat' in param:
                    repeat = galsim.config.ParseValue(param, 'repeat', base,
                                                      int)[0]
                    use_current = (cindex // repeat == index // repeat)
                else:
                    use_current = (cindex == index)
                if use_current:
                    if (value_type is not None and cvalue_type is not None
                            and cvalue_type != value_type):
                        raise galsim.GalSimConfigError(
                            "Attempt to parse %s multiple times with different value types: "
                            "%s and %s" % (key, value_type, cvalue_type))
                    #print(index,'Using current value of ',key,' = ',param['current'][0])
                    return cval, csafe
        else:
            # Only need to check this the first time.
            if 'type' not in param:
                raise galsim.GalSimConfigError(
                    "%s.type attribute required when providing a dict." %
                    (key))

            # Check if the value_type is valid.
            # (See valid_value_types defined at the top of the file.)
            if type_name not in valid_value_types:
                raise galsim.GalSimConfigValueError(
                    "Unrecognized %s.type" % (key), type_name,
                    valid_value_types)

            # Get the generating function and the list of valid types for it.
            generate_func, valid_types = valid_value_types[type_name]

            if value_type not in valid_types:
                raise galsim.GalSimConfigValueError(
                    "Invalid value_type specified for parameter %s with type=%s."
                    % (key, type_name), value_type, valid_types)

            param['_gen_fn'] = generate_func

        #print('generate_func = ',generate_func)
        val_safe = generate_func(param, base, value_type)
        #print('returned val, safe = ',val_safe)
        if isinstance(val_safe, tuple):
            val, safe = val_safe
        else:  # pragma: no cover
            # If a user-defined type forgot to return safe, just assume safe = False
            # It's an easy mistake to make and the TypeError that gets emitted isn't
            # terribly informative about what the error is.
            val = val_safe
            safe = False

        # Make sure we really got the right type back.  (Just in case...)
        if value_type is not None and not isinstance(
                val, value_type) and val is not None:
            val = value_type(val)

        # Save the current value for possible use by the Current type
        param['current'] = (val, safe, value_type, index, index_key)
        #print(key,' = ',val)

        return val, safe

    else:  # Not a dict

        # Check for some special markup on string items and convert them to normal dicts.
        if isinstance(param, basestring):
            if param[0] == '$':
                config[key] = {'type': 'Eval', 'str': str(param[1:])}
                return ParseValue(config, key, base, value_type)
            if param[0] == '@':
                config[key] = {'type': 'Current', 'key': str(param[1:])}
                return ParseValue(config, key, base, value_type)

        # See if it's already the right kind of object, in which case we can just return it.
        if value_type is None or isinstance(param, value_type):
            #print(key,' = ',param)
            return param, True

        # Convert lists to dicts with type=List
        if isinstance(param, list) and value_type is not list:
            config[key] = {'type': 'List', 'items': param}
            return ParseValue(config, key, base, value_type)

        # The rest of these are special processing options for specific value_types:
        if value_type is galsim.Angle:
            # Angle is a special case.  Angles are specified with a final string to
            # declare what unit to use.
            val = _GetAngleValue(param)
        elif value_type is bool:
            # For bool, we allow a few special string conversions
            val = _GetBoolValue(param)
        elif value_type is galsim.PositionD:
            # For PositionD, we allow a string of x,y
            val = _GetPositionValue(param)
        elif value_type is None or param is None:
            # If no value_type is given, just return whatever we have in the dict and hope
            # for the best.
            val = param
        else:
            # If none of the above worked, just try a normal value_type initialization.
            # This makes sure strings are converted to float (or other type) if necessary.
            # In particular things like 1.e6 aren't converted to float automatically
            # by the yaml reader. (Although I think this is a bug.)
            try:
                val = value_type(param)
            except (ValueError, TypeError):
                raise galsim.GalSimConfigError("Could not parse %s as a %s" %
                                               (param, value_type))
        #print(key,' = ',val)

        # Save the converted type for next time so it will hit the first if statement here
        # instead of recalculating the value.
        config[key] = val
        return val, True
Пример #16
0
    def buildImage(self, config, base, image_num, obj_num, logger):
        """Build an Image containing multiple objects placed at arbitrary locations.

        Parameters:
            config:     The configuration dict for the image field.
            base:       The base configuration dict.
            image_num:  The current image number.
            obj_num:    The first object number in the image.
            logger:     If given, a logger object to log progress.

        Returns:
            the final image and the current noise variance in the image as a tuple
        """
        full_xsize = base['image_xsize']
        full_ysize = base['image_ysize']
        wcs = base['wcs']

        full_image = galsim.ImageF(full_xsize, full_ysize)
        full_image.setOrigin(base['image_origin'])
        full_image.wcs = wcs
        full_image.setZero()
        base['current_image'] = full_image

        if 'image_pos' in config and 'world_pos' in config:
            raise galsim.GalSimConfigValueError(
                "Both image_pos and world_pos specified for Scattered image.",
                (config['image_pos'], config['world_pos']))

        if 'image_pos' not in config and 'world_pos' not in config:
            xmin = base['image_origin'].x
            xmax = xmin + full_xsize-1
            ymin = base['image_origin'].y
            ymax = ymin + full_ysize-1
            config['image_pos'] = {
                'type' : 'XY' ,
                'x' : { 'type' : 'Random' , 'min' : xmin , 'max' : xmax },
                'y' : { 'type' : 'Random' , 'min' : ymin , 'max' : ymax }
            }

        stamps, current_vars = galsim.config.BuildStamps(
                self.nobjects, base, logger=logger, obj_num=obj_num, do_noise=False)

        base['index_key'] = 'image_num'

        for k in range(self.nobjects):
            # This is our signal that the object was skipped.
            if stamps[k] is None: continue
            bounds = stamps[k].bounds & full_image.bounds
            logger.debug('image %d: full bounds = %s',image_num,str(full_image.bounds))
            logger.debug('image %d: stamp %d bounds = %s',image_num,k,str(stamps[k].bounds))
            logger.debug('image %d: Overlap = %s',image_num,str(bounds))
            if bounds.isDefined():
                full_image[bounds] += stamps[k][bounds]
            else:
                logger.info(
                    "Object centered at (%d,%d) is entirely off the main image, "
                    "whose bounds are (%d,%d,%d,%d)."%(
                        stamps[k].center.x, stamps[k].center.y,
                        full_image.bounds.xmin, full_image.bounds.xmax,
                        full_image.bounds.ymin, full_image.bounds.ymax))

        # Bring the image so far up to a flat noise variance
        current_var = galsim.config.FlattenNoiseVariance(
                base, full_image, stamps, current_vars, logger)

        return full_image, current_var
Пример #17
0
def BuildGSObject(config, key, base=None, gsparams={}, logger=None):
    """Build a GSObject from the parameters in config[key].

    Parameters:
        config:     A dict with the configuration information.
        key:        The key name in config indicating which object to build.
        base:       The base dict of the configuration. [default: config]
        gsparams:   Optionally, provide non-default GSParams items.  Any ``gsparams`` specified
                    at this level will be added to the list.  This should be a dict with
                    whatever kwargs should be used in constructing the GSParams object.
                    [default: {}]
        logger:     Optionally, provide a logger for logging debug statements.
                    [default: None]

    Returns:
        the tuple (gsobject, safe), where ``gsobject`` is the built object, and ``safe`` is
        a bool that says whether it is safe to use this object again next time.
    """
    logger = galsim.config.LoggerWrapper(logger)
    if base is None:
        base = config

    logger.debug('obj %d: Start BuildGSObject %s',base.get('obj_num',0),key)

    # If key isn't in config, then just return None.
    try:
        param = config[key]
    except KeyError:
        return None, True

    # Check what index key we want to use for this object.
    # Note: this call will also set base['index_key'] and base['rng'] to the right values
    index, index_key = galsim.config.GetIndex(param, base)

    # Get the type to be parsed.
    if not 'type' in param:
        raise galsim.GalSimConfigError("type attribute required in config.%s"%key)
    type_name = param['type']

    # If we are repeating, then we get to use the current object for repeat times.
    if 'repeat' in param:
        repeat = galsim.config.ParseValue(param, 'repeat', base, int)[0]
    else:
        repeat = 1

    # Check if we need to skip this object
    if 'skip' in param:
        skip = galsim.config.ParseValue(param, 'skip', base, bool)[0]
        if skip:
            logger.debug('obj %d: Skipping because field skip=True',base.get('obj_num',0))
            raise SkipThisObject()

    # Check if we can use the current cached object
    if 'current' in param:
        # NB. "current" tuple is (obj, safe, None, index, index_type)
        cobj, csafe, cvalue_type, cindex, cindex_type = param['current']
        if csafe or cindex//repeat == index//repeat:
            # If logging, explain why we are using the current object.
            if logger:
                if csafe:
                    logger.debug('obj %d: current is safe',base.get('obj_num',0))
                elif repeat > 1:
                    logger.debug('obj %d: repeat = %d, index = %d, use current object',
                                base.get('obj_num',0),repeat,index)
                else:
                    logger.debug('obj %d: This object is already current', base.get('obj_num',0))

            return cobj, csafe

    # Set up the initial default list of attributes to ignore while building the object:
    ignore = [
        'dilate', 'dilation', 'ellip', 'rotate', 'rotation', 'scale_flux',
        'magnify', 'magnification', 'shear', 'shift',
        'gsparams', 'skip',
        'current', 'index_key', 'repeat'
    ]
    # There are a few more that are specific to which key we have.
    if key == 'gal':
        ignore += [ 'resolution', 'signal_to_noise', 'redshift', 're_from_res' ]
    elif key == 'psf':
        ignore += [ 'saved_re' ]
    else:
        # As long as key isn't psf, allow resolution.
        # Ideally, we'd like to check that it's something within the gal hierarchy, but
        # I don't know an easy way to do that.
        ignore += [ 'resolution' , 're_from_res' ]

    # Allow signal_to_noise for PSFs only if there is not also a galaxy.
    if 'gal' not in base and key == 'psf':
        ignore += [ 'signal_to_noise']

    # If we are specifying the size according to a resolution, then we
    # need to get the PSF's half_light_radius.
    if 'resolution' in param:
        if 'psf' not in base:
            raise galsim.GalSimConfigError("Cannot use gal.resolution if no psf is set.")
        if 'saved_re' not in base['psf']:
            raise galsim.GalSimConfigError(
                'Cannot use gal.resolution with psf.type = %s'%base['psf']['type'])
        psf_re = base['psf']['saved_re']
        resolution = galsim.config.ParseValue(param, 'resolution', base, float)[0]
        gal_re = resolution * psf_re
        if 're_from_res' not in param:
            # The first time, check that half_light_radius isn't also specified.
            if 'half_light_radius' in param:
                raise galsim.GalSimConfigError(
                    'Cannot specify both gal.resolution and gal.half_light_radius')
            param['re_from_res'] = True
        param['half_light_radius'] = gal_re

    # Make sure the PSF gets flux=1 unless explicitly overridden by the user.
    if key == 'psf' and 'flux' not in param and 'signal_to_noise' not in param:
        param['flux'] = 1

    if 'gsparams' in param:
        gsparams = UpdateGSParams(gsparams, param['gsparams'], base)

    # See if this type is registered as a valid type.
    if type_name in valid_gsobject_types:
        build_func = valid_gsobject_types[type_name]
    elif type_name in galsim.__dict__:
        build_func = eval("galsim."+type_name)
    else:
        raise galsim.GalSimConfigValueError("Unrecognised gsobject type", type_name)

    if inspect.isclass(build_func) and issubclass(build_func, galsim.GSObject):
        gsobject, safe = _BuildSimple(build_func, param, base, ignore, gsparams, logger)
    else:
        gsobject, safe = build_func(param, base, ignore, gsparams, logger)

    # If this is a psf, try to save the half_light_radius in case gal uses resolution.
    if key == 'psf':
        try:
            param['saved_re'] = gsobject.half_light_radius
        except (AttributeError, NotImplementedError, TypeError):
            pass

    # Apply any dilation, ellip, shear, etc. modifications.
    gsobject, safe1 = TransformObject(gsobject, param, base, logger)
    safe = safe and safe1

    param['current'] = gsobject, safe, None, index, index_key

    return gsobject, safe
Пример #18
0
    def buildImage(self, config, base, image_num, obj_num, logger):
        """
        Build an Image consisting of a tiled array of postage stamps.

        @param config       The configuration dict for the image field.
        @param base         The base configuration dict.
        @param image_num    The current image number.
        @param obj_num      The first object number in the image.
        @param logger       If given, a logger object to log progress.

        @returns the final image and the current noise variance in the image as a tuple
        """
        full_xsize = base['image_xsize']
        full_ysize = base['image_ysize']
        wcs = base['wcs']

        full_image = galsim.ImageF(full_xsize, full_ysize)
        full_image.setOrigin(base['image_origin'])
        full_image.wcs = wcs
        full_image.setZero()
        base['current_image'] = full_image

        nobjects = self.nx_tiles * self.ny_tiles

        # Make a list of ix,iy values according to the specified order:
        if 'order' in config:
            order = galsim.config.ParseValue(config,'order',base,str)[0].lower()
        else:
            order = 'row'
        if order.startswith('row'):
            ix_list = [ ix for iy in range(self.ny_tiles) for ix in range(self.nx_tiles) ]
            iy_list = [ iy for iy in range(self.ny_tiles) for ix in range(self.nx_tiles) ]
        elif order.startswith('col'):
            ix_list = [ ix for ix in range(self.nx_tiles) for iy in range(self.ny_tiles) ]
            iy_list = [ iy for ix in range(self.nx_tiles) for iy in range(self.ny_tiles) ]
        elif order.startswith('rand'):
            ix_list = [ ix for ix in range(self.nx_tiles) for iy in range(self.ny_tiles) ]
            iy_list = [ iy for ix in range(self.nx_tiles) for iy in range(self.ny_tiles) ]
            rng = galsim.config.GetRNG(config, base, logger, 'TiledImage, order = '+order)
            galsim.random.permute(rng, ix_list, iy_list)
        else:
            raise galsim.GalSimConfigValueError("Invalid order.", order, ('row', 'col', 'random'))

        # Define a 'image_pos' field so the stamps can set their position appropriately in case
        # we need it for PowerSpectum or NFWHalo.
        x0 = (self.stamp_xsize-1)/2. + base['image_origin'].x
        y0 = (self.stamp_ysize-1)/2. + base['image_origin'].y
        dx = self.stamp_xsize + self.xborder
        dy = self.stamp_ysize + self.yborder
        config['image_pos'] = {
            'type' : 'XY',
            'x' : { 'type' : 'List',
                    'items' : [ x0 + ix*dx for ix in ix_list ]
                  },
            'y' : { 'type' : 'List',
                    'items' : [ y0 + iy*dy for iy in iy_list ]
                  }
        }

        stamps, current_vars = galsim.config.BuildStamps(
                nobjects, base, logger=logger, obj_num=obj_num,
                xsize=self.stamp_xsize, ysize=self.stamp_ysize, do_noise=self.do_noise_in_stamps)

        base['index_key'] = 'image_num'

        for k in range(nobjects):
            logger.debug('image %d: full bounds = %s',image_num,str(full_image.bounds))
            logger.debug('image %d: stamp %d bounds = %s',image_num,k,str(stamps[k].bounds))
            assert full_image.bounds.includes(stamps[k].bounds)
            b = stamps[k].bounds
            full_image[b] += stamps[k]

        # Bring the noise in the image so far up to a flat noise variance
        # Save the resulting noise variance as self.current_var.
        current_var = 0
        if not self.do_noise_in_stamps:
            current_var = galsim.config.FlattenNoiseVariance(
                    base, full_image, stamps, current_vars, logger)
        return full_image, current_var