def create(self):
        """MAIN FUNCTION"""
        self.logger = logging.getLogger('mirage.wfss_simulator')
        self.logger.info('\n\nRunning wfss_simulator....\n')
        self.logger.info('using parameter files: ')
        for pfile in self.paramfiles:
            self.logger.info('{}'.format(pfile))

        # Loop over the yaml files and create
        # a direct seed image for each
        imseeds = []
        ptsrc_seeds = []
        galaxy_seeds = []
        extended_seeds = []
        for pfile in self.paramfiles:
            self.logger.info('Running catalog_seed_image for {}'.format(pfile))
            cat = catalog_seed_image.Catalog_seed(offline=self.offline)
            cat.paramfile = pfile
            cat.make_seed()
            imseeds.append(cat.seed_file)
            ptsrc_seeds.append(cat.ptsrc_seed_filename)
            galaxy_seeds.append(cat.galaxy_seed_filename)
            extended_seeds.append(cat.extended_seed_filename)

            # If Mirage is going to produce an hdf5 file of spectra,
            # then we only need a single direct seed image. Note that
            # find_param_info() has reordered the list such that the
            # wfss mode yaml file will be examined first.
            if self.create_continuum_seds:
                # Be sure to include any Mirage-created source catalogs containing
                # ghost sources in the list of catalogs whose sources will be included
                # in the hdf5 file
                if len(cat.ghost_catalogs) > 0:
                    self.catalog_files.extend(cat.ghost_catalogs)
                break

        # Create hdf5 file with spectra of all sources if requested.
        if self.create_continuum_seds:
            det_name = cat.params['Readout']['array_name'].split('_')[0]
            self.SED_file = spectra_from_catalog.make_all_spectra(
                self.catalog_files,
                input_spectra=self.SED_dict,
                input_spectra_file=self.SED_file,
                extrapolate_SED=self.extrapolate_SED,
                output_filename=self.final_SED_file,
                normalizing_mag_column=self.SED_normalizing_catalog_column,
                module=self.module,
                detector=det_name)

        # Location of the configuration files needed for dispersion
        loc = os.path.join(
            self.datadir,
            "{}/GRISM_{}/current".format(self.instrument,
                                         self.instrument.upper()))
        if not os.path.isdir(loc):
            raise ValueError((
                "{} directory is not present. GRISM_NIRCAM and/or GRISM_NIRISS portion of Mirage reference "
                "file collection is out of date or not set up correctly. See "
                "https://mirage-data-simulator.readthedocs.io/en/latest/reference_files.html foe details."
                .format(loc)))
        self.logger.info("Retrieving WFSS config files from: {}".format(loc))

        # Determine the name of the background file to use, as well as the
        # orders to disperse.
        if self.instrument == 'nircam':
            dmode = 'mod{}_{}'.format(self.module, self.dispersion_direction)
        elif self.instrument == 'niriss':
            dmode = 'GR150{}'.format(self.dispersion_direction)

        # Find the 1d spectrum of the background, based on date or 'low', 'medium', 'high'
        back_wave, back_sig = backgrounds.get_1d_background_spectrum(
            self.params, self.detector, self.module)

        # Default to extracting all orders
        orders = None

        # Call the disperser separately for each type of object: point sources
        # galaxies, extended objects
        disp_seed = np.zeros((cat.ffsize, cat.ffsize))
        background_done = False

        for seed_files in [ptsrc_seeds, galaxy_seeds, extended_seeds]:
            if seed_files[0] is not None:
                dispersed_objtype_seed = Grism_seed(
                    seed_files,
                    self.crossing_filter,
                    dmode,
                    config_path=loc,
                    instrument=self.instrument.upper(),
                    extrapolate_SED=self.extrapolate_SED,
                    SED_file=self.SED_file,
                    SBE_save=self.source_stamps_file)
                dispersed_objtype_seed.observation(orders=orders)
                dispersed_objtype_seed.disperse(orders=orders)
                # Only include the background in one of the object type seed images
                if not background_done:
                    background_image = dispersed_objtype_seed.disperse_background_1D(
                        [back_wave, back_sig])
                    scaling_factor = np.max(background_image)
                    dispersed_objtype_seed.finalize(
                        BackLevel=scaling_factor,
                        tofits=self.background_image_filename)
                    background_done = True
                else:
                    dispersed_objtype_seed.finalize()
                disp_seed += dispersed_objtype_seed.final

        # Disperser output is always full frame. Remove the signal from
        # the refrence pixels now since we know exactly where they are
        disp_seed[0:4, :] = 0.
        disp_seed[2044:, :] = 0.
        disp_seed[:, 0:4] = 0.
        disp_seed[:, 2044:] = 0.

        # Crop to the requested subarray if necessary
        if cat.params['Readout']['array_name'] not in self.fullframe_apertures:
            self.logger.info("Subarray bounds: {}".format(cat.subarray_bounds))
            self.logger.info("Dispersed seed image size: {}".format(
                disp_seed.shape))
            disp_seed = self.crop_to_subarray(disp_seed, cat.subarray_bounds)

        # Save the dispersed seed image if requested
        # Save in units of e/s, under the idea that this should be a
        # "perfect" noiseless view of the scene that does not depend on
        # detector effects, such as gain.
        if self.save_dispersed_seed:
            self.save_dispersed_seed_image(disp_seed)

        # Convert seed image to ADU/sec to be consistent
        # with other simulator outputs
        if self.instrument == 'niriss':
            gain = MEAN_GAIN_VALUES['niriss']
        elif self.instrument == 'nircam':
            gain = MEAN_GAIN_VALUES['nircam']['lw{}'.format(
                self.module.lower())]

        disp_seed /= gain

        # Update seed image header to reflect the
        # division by the gain
        cat.seedinfo['units'] = 'ADU/sec'

        # Prepare dark current exposure if
        # needed.
        if self.override_dark is None:
            d = dark_prep.DarkPrep(offline=self.offline)
            d.paramfile = self.wfss_yaml
            d.prepare()

            if len(d.dark_files) == 1:
                obslindark = d.prepDark
            else:
                obslindark = d.dark_files
        else:
            self.logger.info(
                '\n\noverride_dark has been set. Skipping dark_prep.')
            if isinstance(self.override_dark, str):
                self.read_dark_product()
                obslindark = self.prepDark
            elif isinstance(self.override_dark, list):
                obslindark = self.override_dark

        # Combine into final observation
        obs = obs_generator.Observation(offline=self.offline)
        obs.linDark = obslindark
        obs.seed = disp_seed
        obs.segmap = cat.seed_segmap
        obs.seedheader = cat.seedinfo
        #obs.paramfile = y.outname
        obs.paramfile = self.wfss_yaml
        obs.create()
Beispiel #2
0
    def create(self):
        """MAIN FUNCTION"""
        self.logger = logging.getLogger('mirage.wfss_simulator')
        self.logger.info('\n\nRunning wfss_simulator....\n')
        self.logger.info('using parameter files: ')
        for pfile in self.paramfiles:
            self.logger.info('{}'.format(pfile))

        # Loop over the yaml files and create
        # a direct seed image for each
        imseeds = []
        ptsrc_seeds = []
        galaxy_seeds = []
        extended_seeds = []
        for pfile in self.paramfiles:
            self.logger.info('Running catalog_seed_image for {}'.format(pfile))
            cat = catalog_seed_image.Catalog_seed(offline=self.offline)
            cat.paramfile = pfile
            cat.make_seed()
            imseeds.append(cat.seed_file)
            ptsrc_seeds.append(cat.ptsrc_seed_filename)
            galaxy_seeds.append(cat.galaxy_seed_filename)
            extended_seeds.append(cat.extended_seed_filename)

            # If Mirage is going to produce an hdf5 file of spectra,
            # then we only need a single direct seed image. Note that
            # find_param_info() has reordered the list such that the
            # wfss mode yaml file will be examined first.
            if self.create_continuum_seds:
                # Be sure to include any Mirage-created source catalogs containing
                # ghost sources in the list of catalogs whose sources will be included
                # in the hdf5 file
                if len(cat.ghost_catalogs) > 0:
                    self.catalog_files.extend(cat.ghost_catalogs)
                break

        # Create hdf5 file with spectra of all sources if requested.
        if self.create_continuum_seds:
            det_name = cat.params['Readout']['array_name'].split('_')[0]
            self.SED_file = spectra_from_catalog.make_all_spectra(self.catalog_files, input_spectra=self.SED_dict,
                                                                  input_spectra_file=self.SED_file,
                                                                  extrapolate_SED=self.extrapolate_SED,
                                                                  output_filename=self.final_SED_file,
                                                                  normalizing_mag_column=self.SED_normalizing_catalog_column,
                                                                  module=self.module, detector=det_name)

        # Location of the configuration files needed for dispersion
        loc = os.path.join(self.datadir, "{}/GRISM_{}/".format(self.instrument,
                                                               self.instrument.upper()))

        # Determine the name of the background file to use, as well as the
        # orders to disperse.
        if self.instrument == 'nircam':
            dmode = 'mod{}_{}'.format(self.module, self.dispersion_direction)
            if self.params['simSignals']['use_dateobs_for_background']:
                self.logger.info("Generating background spectrum for observation date: {}".format(self.params['Output']['date_obs']))
                back_wave, back_sig = backgrounds.day_of_year_background_spectrum(self.params['Telescope']['ra'],
                                                                                  self.params['Telescope']['dec'],
                                                                                  self.params['Output']['date_obs'])
            else:
                if isinstance(self.params['simSignals']['bkgdrate'], str):
                    if self.params['simSignals']['bkgdrate'].lower() in ['low', 'medium', 'high']:
                        self.logger.info("Generating background spectrum based on requested level of: {}".format(self.params['simSignals']['bkgdrate']))
                        back_wave, back_sig = backgrounds.low_med_high_background_spectrum(self.params, self.detector,
                                                                                           self.module)
                    else:
                        raise ValueError("ERROR: Unrecognized background rate. Must be one of 'low', 'medium', 'high'")
                else:
                    raise ValueError(("ERROR: WFSS background rates must be one of 'low', 'medium', 'high', "
                                      "or use_dateobs_for_background must be True "))

        elif self.instrument == 'niriss':
            dmode = 'GR150{}'.format(self.dispersion_direction)
            background_file = "{}_{}_medium_background.fits".format(self.crossing_filter.lower(),
                                                                    dmode.lower())

            if isinstance(self.params['simSignals']['bkgdrate'], str):
                if self.params['simSignals']['bkgdrate'].lower() in ['low', 'medium', 'high']:
                    siaf_instance = pysiaf.Siaf('niriss')[self.params['Readout']['array_name']]
                    vegazp, photflam, photfnu, pivot_wavelength = fluxcal_info(self.params['Reffiles']['flux_cal'], self.instrument,
                                                                               self.params['Readout']['filter'], self.params['Readout']['pupil'],
                                                                               self.detector, self.module)

                    if os.path.split(self.params['Reffiles']['filter_throughput'])[1] == 'placeholder.txt' or self.params['Reffiles']['filter_throughput'] == 'config':
                        filter_file = get_filter_throughput_file(self.instrument, 'CLEAR', self.params['Readout']['pupil'])
                    else:
                        filter_file = self.params['Reffiles']['filter_throughput']

                    scaling_factor = backgrounds.calculate_background(self.params['Telescope']['ra'],
                                                                      self.params['Telescope']['dec'],
                                                                      filter_file,
                                                                      self.params['simSignals']['use_dateobs_for_background'],
                                                                      MEAN_GAIN_VALUES['niriss'], siaf_instance,
                                                                      level=self.params['simSignals']['bkgdrate'])

                    # Having the grism in the beam reduces the throughput by 20%.
                    # Mulitply that into the scaling factor
                    scaling_factor *= NIRISS_GRISM_THROUGHPUT_FACTOR

                    # Translate from ADU/sec/pix to e-/sec/pix since that is
                    # what the disperser works with
                    scaling_factor *= MEAN_GAIN_VALUES['niriss']

                else:
                    raise ValueError("ERROR: Unrecognized background rate. String value must be one of 'low', 'medium', 'high'")
            elif np.isreal(self.params['simSignals']['bkgdrate']):
                # The bkgdrate entry in the input yaml file is described as
                # the desired signal in ADU/sec/pixel IN A DIRECT IMAGE
                # Since we want e-/sec/pixel here for the disperser, multiply
                # by the gain as well as the throughput factor for the grism.
                scaling_factor = self.params['simSignals']['bkgdrate'] * MEAN_GAIN_VALUES['niriss'] * NIRISS_GRISM_THROUGHPUT_FACTOR

        # Default to extracting all orders
        orders = None

        # Call the disperser separately for each type of object: point sources
        # galaxies, extended objects
        disp_seed = np.zeros((cat.ffsize, cat.ffsize))
        background_done = False
        for seed_files in [ptsrc_seeds, galaxy_seeds, extended_seeds]:
            if seed_files[0] is not None:
                dispersed_objtype_seed = Grism_seed(seed_files, self.crossing_filter,
                                                    dmode, config_path=loc, instrument=self.instrument.upper(),
                                                    extrapolate_SED=self.extrapolate_SED, SED_file=self.SED_file,
                                                    SBE_save=self.source_stamps_file)
                dispersed_objtype_seed.observation(orders=orders)
                dispersed_objtype_seed.disperse(orders=orders)
                # Only include the background in one of the object type seed images
                if not background_done:
                    if self.instrument == 'nircam':
                        background_image = dispersed_objtype_seed.disperse_background_1D([back_wave, back_sig])
                        dispersed_objtype_seed.finalize(Back=background_image, BackLevel=None)
                    else:
                        # BackLevel is used as such: background / max(background) * BackLevel
                        # So we need to either set BackLevel equal to the requested level
                        # NOT THE RATIO OF THAT TO MEDIUM, or we need to open the background
                        # file and multiply it by the ratio of the requested level to medium.
                        # The former isn't quite correct because it'll be scaling the maximum
                        # value in the image to "low" or "high", rather than the median
                        full_background_file = os.path.join(loc, background_file)
                        background_image = fits.getdata(full_background_file)

                        # Before scaling the background image by the scaling_factor
                        # we need to normalize by the sigma-clipped mean value. This is
                        # because the background files were produced and scaled to the
                        # ETC "medium" level at some arbirtrary pointing, but the
                        # "medium" level is pointing-dependent. Current background files
                        # are scaled such that the "medium" value from the ETC is the
                        # sigma-clipped mean value.
                        clip, lo, hi = sigmaclip(background_image, low=3, high=3)
                        background_mean = np.mean(clip)
                        background_image = background_image / background_mean * scaling_factor
                        dispersed_objtype_seed.finalize(Back=background_image, BackLevel=None)

                    background_done = True

                    # Save the background image to a fits file
                    hprime = fits.PrimaryHDU()
                    himg = fits.ImageHDU(background_image)
                    himg.header['EXTNAME'] = 'BACKGRND'
                    himg.header['UNITS'] = 'e/s'
                    hlist = fits.HDUList([hprime, himg])
                    hlist.writeto(self.background_image_filename, overwrite=True)
                else:
                    dispersed_objtype_seed.finalize()
                disp_seed += dispersed_objtype_seed.final

        # Disperser output is always full frame. Remove the signal from
        # the refrence pixels now since we know exactly where they are
        disp_seed[0:4, :] = 0.
        disp_seed[2044:, :] = 0.
        disp_seed[:, 0:4] = 0.
        disp_seed[:, 2044:] = 0.

        # Crop to the requested subarray if necessary
        if cat.params['Readout']['array_name'] not in self.fullframe_apertures:
            self.logger.info("Subarray bounds: {}".format(cat.subarray_bounds))
            self.logger.info("Dispersed seed image size: {}".format(disp_seed.shape))
            disp_seed = self.crop_to_subarray(disp_seed, cat.subarray_bounds)

        # Save the dispersed seed image if requested
        # Save in units of e/s, under the idea that this should be a
        # "perfect" noiseless view of the scene that does not depend on
        # detector effects, such as gain.
        if self.save_dispersed_seed:
            self.save_dispersed_seed_image(disp_seed)

        # Convert seed image to ADU/sec to be consistent
        # with other simulator outputs
        if self.instrument == 'niriss':
            gain = MEAN_GAIN_VALUES['niriss']
        elif self.instrument == 'nircam':
            gain = MEAN_GAIN_VALUES['nircam']['lw{}'.format(self.module.lower())]

        disp_seed /= gain

        # Update seed image header to reflect the
        # division by the gain
        cat.seedinfo['units'] = 'ADU/sec'

        # Prepare dark current exposure if
        # needed.
        if self.override_dark is None:
            d = dark_prep.DarkPrep(offline=self.offline)
            d.paramfile = self.wfss_yaml
            d.prepare()

            if len(d.dark_files) == 1:
                obslindark = d.prepDark
            else:
                obslindark = d.dark_files
        else:
            self.logger.info('\n\noverride_dark has been set. Skipping dark_prep.')
            if isinstance(self.override_dark, str):
                self.read_dark_product()
                obslindark = self.prepDark
            elif isinstance(self.override_dark, list):
                obslindark = self.override_dark

        # Combine into final observation
        obs = obs_generator.Observation(offline=self.offline)
        obs.linDark = obslindark
        obs.seed = disp_seed
        obs.segmap = cat.seed_segmap
        obs.seedheader = cat.seedinfo
        #obs.paramfile = y.outname
        obs.paramfile = self.wfss_yaml
        obs.create()
    def create(self):
        """MAIN FUNCTION"""

        # Loop over the yaml files and create
        # a direct seed image for each
        imseeds = []
        for pfile in self.paramfiles:
            print('Running catalog_seed_image for {}'.format(pfile))
            cat = catalog_seed_image.Catalog_seed(offline=self.offline)
            cat.paramfile = pfile
            cat.make_seed()
            imseeds.append(cat.seed_file)
            # If Mirage is going to produce an hdf5 file of spectra,
            # then we only need a single direct seed image.
            if self.create_continuum_seds:
                break

        # Create hdf5 file with spectra of all sources if requested.
        if self.create_continuum_seds:
            self.SED_file = spectra_from_catalog.make_all_spectra(
                self.catalog_files,
                input_spectra=self.SED_dict,
                input_spectra_file=self.SED_file,
                extrapolate_SED=self.extrapolate_SED,
                output_filename=self.final_SED_file,
                normalizing_mag_column=self.SED_normalizing_catalog_column)

        # Location of the configuration files needed for dispersion
        loc = os.path.join(
            self.datadir, "{}/GRISM_{}/".format(self.instrument,
                                                self.instrument.upper()))

        # Determine the name of the background file to use, as well as the
        # orders to disperse.
        if self.instrument == 'nircam':
            dmode = 'mod{}_{}'.format(self.module, self.dispersion_direction)
            background_file = ("{}_{}_back.fits".format(
                self.crossing_filter, dmode))
        elif self.instrument == 'niriss':
            dmode = 'GR150{}'.format(self.dispersion_direction)
            background_file = "{}_{}_medium_background.fits".format(
                self.crossing_filter.lower(), dmode.lower())
            print('Background file is {}'.format(background_file))

        # Default to extracting all orders
        orders = None

        # Create dispersed seed image from the direct images
        disp_seed = Grism_seed(imseeds,
                               self.crossing_filter,
                               dmode,
                               config_path=loc,
                               instrument=self.instrument.upper(),
                               extrapolate_SED=self.extrapolate_SED,
                               SED_file=self.SED_file,
                               SBE_save=self.source_stamps_file)
        disp_seed.observation(orders=orders)
        disp_seed.disperse(orders=orders)
        disp_seed.finalize(Back=background_file)

        # Get gain map
        gainfile = cat.params['Reffiles']['gain']
        gain, gainheader = self.read_gain_file(gainfile)

        # Disperser output is always full frame. Remove the signal from
        # the refrence pixels now since we know exactly where they are
        disp_seed.final[0:4, :] = 0.
        disp_seed.final[2044:, :] = 0.
        disp_seed.final[:, 0:4] = 0.
        disp_seed.final[:, 2044:] = 0.

        # Crop to the requested subarray if necessary
        if cat.params['Readout']['array_name'] not in self.fullframe_apertures:
            print("Subarray bounds: {}".format(cat.subarray_bounds))
            print("Dispersed seed image size: {}".format(
                disp_seed.final.shape))
            disp_seed.final = self.crop_to_subarray(disp_seed.final,
                                                    cat.subarray_bounds)
            gain = self.crop_to_subarray(gain, cat.subarray_bounds)

            # Segmentation map will be centered in a frame that is larger
            # than full frame by a factor of sqrt(2), so crop appropriately
            print("Need to make this work for subarrays...")
            segy, segx = cat.seed_segmap.shape
            dx = int((segx - 2048) / 2)
            dy = int((segy - 2048) / 2)
            segbounds = [
                cat.subarray_bounds[0] + dx, cat.subarray_bounds[1] + dy,
                cat.subarray_bounds[2] + dx, cat.subarray_bounds[3] + dy
            ]
            cat.seed_segmap = self.crop_to_subarray(cat.seed_segmap, segbounds)

        # Save the dispersed seed image if requested
        # Save in units of e/s, under the idea that this should be a
        # "perfect" noiseless view of the scene that does not depend on
        # detector effects, such as gain.
        if self.save_dispersed_seed:
            self.save_dispersed_seed_image(disp_seed.final)

        # Convert seed image to ADU/sec to be consistent
        # with other simulator outputs
        disp_seed.final /= gain

        # Update seed image header to reflect the
        # division by the gain
        cat.seedinfo['units'] = 'ADU/sec'

        # Prepare dark current exposure if
        # needed.
        if self.override_dark is None:
            d = dark_prep.DarkPrep(offline=self.offline)
            d.paramfile = self.wfss_yaml
            d.prepare()
            obslindark = d.prepDark
        else:
            self.read_dark_product()
            obslindark = self.darkPrep

        # Using the first of the imaging seed image yaml
        # files as a base, adjust to create the yaml file
        # for the creation of the final dispersed
        # integration

        # I think we won't need this anymore assuming that
        # one of the input yaml files is for wfss mode
        #y = yaml_update.YamlUpdate()
        #y.file = self.paramfiles[0]
        #if self.instrument == 'nircam':
        #    y.filter = self.crossing_filter
        #    y.pupil = 'GRISM' + self.direction
        #elif self.instrument == 'niriss':
        #    y.filter = 'GR150' + self.direction
        #    y.pupil = self.crossing_filter
        #y.outname = ("wfss_dispersed_{}_{}.yaml"
        #             .format(dmode, self.crossing_filter))
        #y.run()

        # Combine into final observation
        obs = obs_generator.Observation(offline=self.offline)
        obs.linDark = obslindark
        obs.seed = disp_seed.final
        obs.segmap = cat.seed_segmap
        obs.seedheader = cat.seedinfo
        #obs.paramfile = y.outname
        obs.paramfile = self.wfss_yaml
        obs.create()
    def run_disperser(self,
                      direct_file,
                      orders=["+1", "+2"],
                      add_background=True,
                      background_waves=None,
                      background_fluxes=None,
                      cache=False,
                      finalize=False):
        """Run the disperser on the given direct seed image.

        Parameters
        ----------
        direct_file : str
            Name of file containing direct seed image

        orders : list
            Orders to include in the dispersed image.

        add_background : bool
            If True, a 2D dispersed background image is created and
            added

        background_waves : numpy.ndarray
            1D array of wavelengths (in mocrons) to be used when creating
            the background image

        background_fluxes : numpy.ndarray
            1D array of fluxes (in units of cgs f_lambda) to be used
            when creating the background image

        cache : bool
            Whether or not to cache the dispersed object. If it is cached,
            then it can be used later

        finalize : bool
            If True, call the finalize function of the disperser in order to
            create the final dispersed image of the object

        Returns
        -------
        disp_seed : numpy.ndarray
            2D array containing the dispersed seed image
        """
        # Location of the configuration files needed for dispersion
        loc = os.path.join(
            self.datadir, "{}/GRISM_{}/".format(self.instrument,
                                                self.instrument.upper()))

        # Determine the name of the background file to use, as well as the
        # orders to disperse.
        dmode = 'mod{}_{}'.format(self.module, self.dispersion_direction)
        orders = self.orders

        # Create dispersed seed image from the direct images
        disp_seed = Grism_seed([direct_file],
                               self.crossing_filter,
                               dmode,
                               config_path=loc,
                               instrument=self.instrument.upper(),
                               extrapolate_SED=self.extrapolate_SED,
                               SED_file=self.final_SED_file,
                               SBE_save=self.source_stamps_file)
        disp_seed.observation(orders=orders)

        # Cache the dispersion if requested. This will allow you to
        #disperse the same object again later with a different lightcurve
        if cache:
            for order in orders:
                disp_seed.this_one[order].disperse_all(cache=True)
        else:
            disp_seed.disperse(orders=orders)

        # Only finalize and/or add the background if requested.
        if finalize:
            if add_background:
                background_image = disp_seed.disperse_background_1D(
                    [background_waves, background_fluxes])
                disp_seed.finalize(Back=background_image, BackLevel=None)
            else:
                disp_seed.finalize(Back=None, BackLevel=None)
        return disp_seed
Beispiel #5
0
    def create(self):
        """MAIN FUNCTION"""

        # Loop over the yaml files and create
        # a direct seed image for each
        imseeds = []
        ptsrc_seeds = []
        galaxy_seeds = []
        extended_seeds = []
        for pfile in self.paramfiles:
            print('Running catalog_seed_image for {}'.format(pfile))
            cat = catalog_seed_image.Catalog_seed(offline=self.offline)
            cat.paramfile = pfile
            cat.make_seed()
            imseeds.append(cat.seed_file)
            ptsrc_seeds.append(cat.ptsrc_seed_filename)
            galaxy_seeds.append(cat.galaxy_seed_filename)
            extended_seeds.append(cat.extended_seed_filename)

            # Save the flat field file associated with the wfss yaml so
            # that we can apply it to the dispersed seed image if requested
            if pfile == self.wfss_yaml:
                self.flatfield = cat.flatfield

            # If Mirage is going to produce an hdf5 file of spectra,
            # then we only need a single direct seed image. Note that
            # find_param_info() has reordered the list such that the
            # wfss mode yaml file will be examined first.
            if self.create_continuum_seds:
                break

        # Create hdf5 file with spectra of all sources if requested.
        if self.create_continuum_seds:
            det_name = cat.params['Readout']['array_name'].split('_')[0]
            self.SED_file = spectra_from_catalog.make_all_spectra(
                self.catalog_files,
                input_spectra=self.SED_dict,
                input_spectra_file=self.SED_file,
                extrapolate_SED=self.extrapolate_SED,
                output_filename=self.final_SED_file,
                normalizing_mag_column=self.SED_normalizing_catalog_column,
                module=self.module,
                detector=det_name)

        # Location of the configuration files needed for dispersion
        loc = os.path.join(
            self.datadir, "{}/GRISM_{}/".format(self.instrument,
                                                self.instrument.upper()))

        # Determine the name of the background file to use, as well as the
        # orders to disperse.
        if self.instrument == 'nircam':
            dmode = 'mod{}_{}'.format(self.module, self.dispersion_direction)
            if self.params['simSignals']['use_dateobs_for_background']:
                print(
                    "Generating background spectrum for observation date: {}".
                    format(self.params['Output']['date_obs']))
                back_wave, back_sig = backgrounds.day_of_year_background_spectrum(
                    self.params['Telescope']['ra'],
                    self.params['Telescope']['dec'],
                    self.params['Output']['date_obs'])
            else:
                if isinstance(self.params['simSignals']['bkgdrate'], str):
                    if self.params['simSignals']['bkgdrate'].lower() in [
                            'low', 'medium', 'high'
                    ]:
                        print(
                            "Generating background spectrum based on requested level of: {}"
                            .format(self.params['simSignals']['bkgdrate']))
                        back_wave, back_sig = backgrounds.low_med_high_background_spectrum(
                            self.params, self.detector, self.module)
                    else:
                        raise ValueError(
                            "ERROR: Unrecognized background rate. Must be one of 'low', 'medium', 'high'"
                        )
                else:
                    raise ValueError((
                        "ERROR: WFSS background rates must be one of 'low', 'medium', 'high', "
                        "or use_dateobs_for_background must be True "))

        elif self.instrument == 'niriss':
            dmode = 'GR150{}'.format(self.dispersion_direction)
            background_file = "{}_{}_medium_background.fits".format(
                self.crossing_filter.lower(), dmode.lower())

            if isinstance(self.params['simSignals']['bkgdrate'], str):
                if self.params['simSignals']['bkgdrate'].lower() in [
                        'low', 'medium', 'high'
                ]:
                    #scaling_factor = backgrounds.niriss_background_scaling(self.params, self.detector, self.module)

                    usefilt = 'pupil'

                    siaf_instance = pysiaf.Siaf('niriss')[
                        self.params['Readout']['array_name']]
                    vegazp, photflam, photfnu, pivot_wavelength = fluxcal_info(
                        self.params, usefilt, self.detector, self.module)

                    if os.path.split(
                            self.params['Reffiles']['filter_throughput']
                    )[1] == 'placeholder.txt' or self.params['Reffiles'][
                            'filter_throughput'] == 'config':
                        filter_file = get_filter_throughput_file(
                            self.instrument, self.params['Readout'][usefilt])
                    else:
                        filter_file = self.params['Reffiles'][
                            'filter_throughput']

                    scaling_factor = backgrounds.calculate_background(
                        self.params['Telescope']['ra'],
                        self.params['Telescope']['dec'],
                        filter_file,
                        self.params['simSignals']
                        ['use_dateobs_for_background'],
                        MEAN_GAIN_VALUES['niriss'],
                        siaf_instance,
                        level=self.params['simSignals']['bkgdrate'])

                    # Having the grism in the beam reduces the throughput by 20%.
                    # Mulitply that into the scaling factor
                    scaling_factor *= NIRISS_GRISM_THROUGHPUT_FACTOR

                    # Translate from ADU/sec/pix to e-/sec/pix since that is
                    # what the disperser works with
                    scaling_factor *= MEAN_GAIN_VALUES['niriss']

                else:
                    raise ValueError(
                        "ERROR: Unrecognized background rate. String value must be one of 'low', 'medium', 'high'"
                    )
            elif np.isreal(self.params['simSignals']['bkgdrate']):
                # The bkgdrate entry in the input yaml file is described as
                # the desired signal in ADU/sec/pixel IN A DIRECT IMAGE
                # Since we want e-/sec/pixel here for the disperser, multiply
                # by the gain as well as the throughput factor for the grism.
                scaling_factor = self.params['simSignals'][
                    'bkgdrate'] * MEAN_GAIN_VALUES[
                        'niriss'] * NIRISS_GRISM_THROUGHPUT_FACTOR

        # Default to extracting all orders
        orders = None

        # Call the disperser separately for each type of object: point sources
        # galaxies, extended objects
        disp_seed = np.zeros((cat.ffsize, cat.ffsize))
        background_done = False
        for seed_files in [ptsrc_seeds, galaxy_seeds, extended_seeds]:
            if seed_files[0] is not None:
                dispersed_objtype_seed = Grism_seed(
                    seed_files,
                    self.crossing_filter,
                    dmode,
                    config_path=loc,
                    instrument=self.instrument.upper(),
                    extrapolate_SED=self.extrapolate_SED,
                    SED_file=self.SED_file,
                    SBE_save=self.source_stamps_file)
                dispersed_objtype_seed.observation(orders=orders)
                dispersed_objtype_seed.disperse(orders=orders)
                # Only include the background in one of the object type seed images
                if not background_done:
                    if self.instrument == 'nircam':
                        background_image = dispersed_objtype_seed.disperse_background_1D(
                            [back_wave, back_sig])
                        dispersed_objtype_seed.finalize(Back=background_image,
                                                        BackLevel=None)
                    else:
                        # BackLevel is used as such: background / max(background) * BackLevel
                        # So we need to either set BackLevel equal to the requested level
                        # NOT THE RATIO OF THAT TO MEDIUM, or we need to open the background
                        # file and multiply it by the ratio of the requested level to medium.
                        # The former isn't quite correct because it'll be scaling the maximum
                        # value in the image to "low" or "high", rather than the median
                        full_background_file = os.path.join(
                            loc, background_file)
                        background_image = fits.getdata(full_background_file)

                        # Before scaling the background image by the scaling_factor
                        # we need to normalize by the sigma-clipped mean value. This is
                        # because the background files were produced and scaled to the
                        # ETC "medium" level at some arbirtrary pointing, but the
                        # "medium" level is pointing-dependent. Current background files
                        # are scaled such that the "medium" value from the ETC is the
                        # sigma-clipped mean value.
                        clip, lo, hi = sigmaclip(background_image,
                                                 low=3,
                                                 high=3)
                        background_mean = np.mean(clip)
                        background_image = background_image / background_mean * scaling_factor
                        dispersed_objtype_seed.finalize(Back=background_image,
                                                        BackLevel=None)

                    background_done = True
                else:
                    dispersed_objtype_seed.finalize()
                disp_seed += dispersed_objtype_seed.final

        # Disperser output is always full frame. Remove the signal from
        # the refrence pixels now since we know exactly where they are
        disp_seed[0:4, :] = 0.
        disp_seed[2044:, :] = 0.
        disp_seed[:, 0:4] = 0.
        disp_seed[:, 2044:] = 0.

        # Crop to the requested subarray if necessary
        if cat.params['Readout']['array_name'] not in self.fullframe_apertures:
            print("Subarray bounds: {}".format(cat.subarray_bounds))
            print("Dispersed seed image size: {}".format(disp_seed.shape))
            disp_seed = self.crop_to_subarray(disp_seed, cat.subarray_bounds)

            # Segmentation map will be centered in a frame that is larger
            # than full frame by a factor of sqrt(2), so crop appropriately
            print("Need to make this work for subarrays...")
            segy, segx = cat.seed_segmap.shape
            dx = int((segx - 2048) / 2)
            dy = int((segy - 2048) / 2)
            segbounds = [
                cat.subarray_bounds[0] + dx, cat.subarray_bounds[1] + dy,
                cat.subarray_bounds[2] + dx, cat.subarray_bounds[3] + dy
            ]
            cat.seed_segmap = self.crop_to_subarray(cat.seed_segmap, segbounds)

        # If it is a NIRCam observation, multiply the dispsersed seed
        # image by the flat field. For NIRISS, the flat was multiplied
        # in to the direct seed image, as they need to have their
        # occulting spots in the direct seed image.
        if self.instrument == 'nircam':
            disp_seed *= self.flatfield

        # Save the dispersed seed image if requested
        # Save in units of e/s, under the idea that this should be a
        # "perfect" noiseless view of the scene that does not depend on
        # detector effects, such as gain.
        if self.save_dispersed_seed:
            self.save_dispersed_seed_image(disp_seed)

        # Convert seed image to ADU/sec to be consistent
        # with other simulator outputs
        if self.instrument == 'niriss':
            gain = MEAN_GAIN_VALUES['niriss']
        elif self.instrument == 'nircam':
            gain = MEAN_GAIN_VALUES['nircam']['lw{}'.format(
                self.module.lower())]

        disp_seed /= gain

        # Update seed image header to reflect the
        # division by the gain
        cat.seedinfo['units'] = 'ADU/sec'

        # Prepare dark current exposure if
        # needed.
        if self.override_dark is None:
            d = dark_prep.DarkPrep(offline=self.offline)
            d.paramfile = self.wfss_yaml
            d.prepare()
            obslindark = d.prepDark
        else:
            self.read_dark_product()
            obslindark = self.darkPrep

        # Using the first of the imaging seed image yaml
        # files as a base, adjust to create the yaml file
        # for the creation of the final dispersed
        # integration

        # I think we won't need this anymore assuming that
        # one of the input yaml files is for wfss mode
        #y = yaml_update.YamlUpdate()
        #y.file = self.paramfiles[0]
        #if self.instrument == 'nircam':
        #    y.filter = self.crossing_filter
        #    y.pupil = 'GRISM' + self.direction
        #elif self.instrument == 'niriss':
        #    y.filter = 'GR150' + self.direction
        #    y.pupil = self.crossing_filter
        #y.outname = ("wfss_dispersed_{}_{}.yaml"
        #             .format(dmode, self.crossing_filter))
        #y.run()

        # Combine into final observation
        obs = obs_generator.Observation(offline=self.offline)
        obs.linDark = obslindark
        obs.seed = disp_seed
        obs.segmap = cat.seed_segmap
        obs.seedheader = cat.seedinfo
        #obs.paramfile = y.outname
        obs.paramfile = self.wfss_yaml
        obs.create()
Beispiel #6
0
    def create(self):
        # Make sure inputs are correct
        self.check_inputs()

        # Loop over the yaml files and create
        # a direct seed image for each
        imseeds = []
        # Create imaging seed images
        for pfile in self.paramfiles:
            cat = catalog_seed_image.Catalog_seed()
            cat.paramfile = pfile
            cat.make_seed()
            imseeds.append(cat.seed_file)

        # Create dispersed seed image from
        # the direct images
        dmode = 'mod{}_{}'.format(self.module,self.direction)
        loc = os.path.join(self.datadir,"nircam/GRISM_NIRCAM/")
        background_file = ("{}_{}_back.fits"
                           .format(self.crossing_filter,dmode))
        disp_seed = Grism_seed(imseeds, self.crossing_filter,
                               dmode, config_path=loc,
                               extrapolate_SED=self.extrapolate_SED)
        disp_seed.observation()
        disp_seed.finalize(Back = background_file)

        # Get gain map
        gainfile = cat.params['Reffiles']['gain']
        gain, gainheader = self.read_gain_file(gainfile)

        # Disperser output is always full frame. Crop to the
        # requested subarray if necessary
        if cat.params['Readout']['array_name'] not in self.fullframe_apertures:
            print("Subarray bounds: {}".format(cat.subarray_bounds))
            print("Dispersed seed image size: {}".format(disp_seed.final.shape))
            disp_seed.final = self.crop_to_subarray(disp_seed.final, cat.subarray_bounds)
            gain = self.crop_to_subarray(gain, cat.subarray_bounds)
            # Segmentation map will be centered in a frame that is larger
            # than full frame by a factor of sqrt(2), so crop appropriately
            segy, segx = cat.seed_segmap.shape
            dx = int((segx - 2048) / 2)
            dy = int((segy - 2048) / 2)
            segbounds = [cat.subarray_bounds[0] + dx, cat.subarray_bounds[1] + dy,
                         cat.subarray_bounds[2] + dx, cat.subarray_bounds[3] + dy]
            cat.seed_segmap = self.crop_to_subarray(cat.seed_segmap, segbounds)

        # Convert seed image to ADU/sec to be consistent
        # with other simulator outputs
        disp_seed.final /= gain

        # Update seed image header to reflect the
        # division by the gain
        cat.seedinfo['units'] = 'ADU/sec'

        # Save the dispersed seed image
        if self.save_dispersed_seed:
            hh00 = fits.PrimaryHDU()
            hh11 = fits.ImageHDU(disp_seed.final)
            hhll = fits.HDUList([hh00,hh11])
            hhll[0].header['units'] = 'ADU/sec'
            if self.disp_seed_filename is None:
                pdir, pf = os.path.split(self.paramfiles[0])
                dname = 'dispersed_seed_image_for_' + pf + '.fits'
                self.disp_seed_filename = os.path.join(pdir, dname)
            hhll.writeto(self.disp_seed_filename, overwrite=True)
            print(("Dispersed seed image saved to {}"
                   .format(self.disp_seed_filename)))

        # Prepare dark current exposure if
        # needed.
        if self.override_dark is None:
            d = dark_prep.DarkPrep()
            d.paramfile = self.paramfiles[0]
            d.prepare()
            obslindark = d.prepDark
        else:
            self.read_dark_product(self.override_dark)
            obslindark = self.prepDark

        # Using the first of the imaging seed image yaml
        # files as a base, adjust to create the yaml file
        # for the creation of the final dispersed
        # integration
        y = yaml_update.YamlUpdate()
        y.file = self.paramfiles[0]
        y.filter = self.crossing_filter
        y.pupil = 'GRISM' + self.direction
        y.outname = ("wfss_dispersed_{}_{}.yaml"
                     .format(dmode,self.crossing_filter))
        y.run()

        # Combine into final observation
        obs = obs_generator.Observation()
        obs.linDark = obslindark
        obs.seed = disp_seed.final
        obs.segmap = cat.seed_segmap
        obs.seedheader = cat.seedinfo
        obs.paramfile = y.outname
        obs.create()