Ejemplo n.º 1
0
    def __init__(self, home=''):
        """Initialize the Libradtran settings for Spectractor.

        Parameters
        ----------
        home: str, optional
            The path to the directory where libradtran directory is. If not specified $HOME is taken (default: '').
        """
        self.my_logger = set_logger(self.__class__.__name__)
        if home == '':
            self.home = os.environ['HOME']
        else:
            self.home = home
        self.settings = {}

        # Definitions and configuration
        # -------------------------------------

        # LibRadTran installation directory
        self.simulation_directory = 'simulations'
        ensure_dir(self.simulation_directory)
        self.libradtran_path = parameters.LIBRADTRAN_DIR

        # Filename : RT_LS_pp_us_sa_rt_z15_wv030_oz30.txt
        #          : Prog_Obs_Rte_Atm_proc_Mod_zXX_wv_XX_oz_XX
        self.Prog = 'RT'  # definition the simulation program is libRadTran
        self.proc = 'as'  # Absoprtion + Rayleigh + aerosols special
        self.equation_solver = 'pp'  # pp for parallel plane or ps for pseudo-spherical
        self.Atm = [
            'us'
        ]  # short name of atmospheric sky here US standard and  Subarctic winter
        self.Proc = 'sa'  # light interaction processes : sc for pure scattering,ab for pure absorption
        # sa for scattering and absorption, ae with aerosols default, as with aerosol special
        self.Mod = 'rt'  # Models for absorption bands : rt for REPTRAN, lt for LOWTRAN, k2 for Kato2
Ejemplo n.º 2
0
def SpectrumSimulatorSimGrid(filename,
                             outputdir,
                             pwv_grid=[0, 10, 5],
                             ozone_grid=[100, 700, 7],
                             aerosol_grid=[0, 0.1, 5]):
    """ SimulatorSimGrid
    Main function to evaluate several spectra
    A grid of spectra will be produced for a given target, airmass and pressure

    """
    my_logger = set_logger(__name__)
    my_logger.info('\n\tStart SIMULATORGRID')
    # Initialisation
    spectrum, telescope, disperser, target = SimulatorInit(filename)
    # Set output path
    ensure_dir(outputdir)
    # extract the basename : simimar as os.path.basename(file)
    base_filename = filename.split('/')[-1]
    output_filename = os.path.join(
        outputdir, base_filename.replace('spectrum', 'spectrasim'))
    output_atmfilename = os.path.join(
        outputdir, base_filename.replace('spectrum', 'atmsim'))

    # SIMULATE ATMOSPHERE GRID
    # ------------------------
    airmass = spectrum.header['AIRMASS']
    pressure = spectrum.header['OUTPRESS']
    temperature = spectrum.header['OUTTEMP']
    atm = AtmosphereGrid(filename,
                         airmass=airmass,
                         pressure=pressure,
                         temperature=temperature)
    atm.set_grid(pwv_grid, ozone_grid, aerosol_grid)

    # test if file already exists
    if os.path.exists(output_atmfilename
                      ) and os.path.getsize(output_atmfilename) > 20000:
        filesize = os.path.getsize(output_atmfilename)
        infostring = " atmospheric simulation file %s of size %d already exists, thus load_image it ..." % (
            output_atmfilename, filesize)
        my_logger.info(infostring)
        atm.load_file(output_atmfilename)
    else:
        my_logger.info(
            f"\n\tFile {output_atmfilename} does not exist yet. Compute it...")
        atm.compute()
        atm.save_file(filename=output_atmfilename)
        # libradtran.clean_simulation_directory()
    if parameters.DEBUG:
        infostring = '\n\t ========= Atmospheric simulation :  ==============='
        my_logger.info(infostring)
        atm.plot_transmission()  # plot all atm transp profiles
        atm.plot_transmission_image(
        )  # plot 2D image summary of atm simulations

    # SPECTRA-GRID
    # -------------
    # in any case we re-calculate the spectra in case of change of spectrum function
    spectra = SpectrumSimGrid(spectrum, atm, telescope, disperser, target)
    spectra.compute()
    spectra.save_spectra(output_filename)
    if parameters.DEBUG:
        spectra.plot_spectra()
        spectra.plot_spectra_img()
Ejemplo n.º 3
0
    def simulate(self, airmass, pwv, ozone, aerosol, pressure):
        """Simulate the atmosphere transmission with Libratran.

        Parameters
        ----------
        airmass: float
            The airmass of the atmosphere.
        pwv: float
            Precipitable Water Vapor amount in mm.
        ozone: float
            Ozone quantity in Dobson units.
        aerosol: float
            VAOD of the aerosols.
        pressure: float
            Pressure of the atmosphere in hPa.

        Returns
        -------
        output_file_name: str
            The output file name relative to the current directory.

        Examples
        --------
        >>> parameters.DEBUG = True
        >>> lib = Libradtran()
        >>> output = lib.simulate(1.2, 2, 400, 0.07, 800)
        >>> print(output)
        simulations/pp/us/as/rt/in/RT_CTIO_pp_us_as_rt_z12_pwv20_oz40_aer7.OUT
        """

        self.my_logger.debug(
            f'\n\t--------------------------------------------'
            f'\n\tevaluate'
            f'\n\t 1) airmass = {airmass}'
            f'\n\t 2) pwv = {pwv}'
            f'\n\t 3) ozone = {ozone}'
            f'\n\t 4) aer = {aerosol}'
            f'\n\t 5) pressure =  {pressure}'
            f'\n\t--------------------------------------------')

        # build the part 1 of file_name
        base_filename_part1 = self.Prog + '_' + parameters.OBS_NAME + '_' + self.equation_solver + '_'

        aerosol_string = '500 ' + str(aerosol)
        # aerosol_str=str(wl0_num)+ ' '+str(tau0_num)
        aerosol_index = int(aerosol * 100.)

        # Set up type of run
        if self.proc == 'sc':
            runtype = 'no_absorption'
        elif self.proc == 'ab':
            runtype = 'no_scattering'
        elif self.proc == 'ae':
            runtype = 'aerosol_default'
        elif self.proc == 'as':
            runtype = 'aerosol_special'
        else:
            runtype = 'clearsky'

        #   Selection of RTE equation solver
        if self.equation_solver == 'pp':  # parallel plan
            equation_solver_equations = 'disort'
        elif self.equation_solver == 'ps':  # pseudo spherical
            equation_solver_equations = 'sdisort'
        else:
            self.my_logger.error(
                f'Unknown RTE equation solver {self.equation_solver}.')
            sys.exit()

        #   Selection of absorption model
        absorption_model = 'reptran'
        if self.Mod == 'rt':
            absorption_model = 'reptran'
        if self.Mod == 'lt':
            absorption_model = 'lowtran'
        if self.Mod == 'kt':
            absorption_model = 'kato'
        if self.Mod == 'k2':
            absorption_model = 'kato2'
        if self.Mod == 'fu':
            absorption_model = 'fu'
        if self.Mod == 'cr':
            absorption_model = 'crs'

        # for simulation select only two atmosphere
        # atmospheres = np.array(['afglus','afglms','afglmw','afglt','afglss','afglsw'])
        atmosphere_map = dict()  # map atmospheric names to short names
        atmosphere_map['afglus'] = 'us'
        atmosphere_map['afglms'] = 'ms'
        atmosphere_map['afglmw'] = 'mw'
        atmosphere_map['afglt'] = 'tp'
        atmosphere_map['afglss'] = 'ss'
        atmosphere_map['afglsw'] = 'sw'

        atmospheres = []
        for skyindex in self.Atm:
            if re.search('us', skyindex):
                atmospheres.append('afglus')
            if re.search('sw', skyindex):
                atmospheres.append('afglsw')

        output_directory, output_filename = None, None
        # 1) LOOP ON ATMOSPHERE
        for atmosphere in atmospheres:
            # if atmosphere != 'afglus':  # just take us standard sky
            #    break
            atmkey = atmosphere_map[atmosphere]

            # manage settings and output directories
            topdir = f'{self.simulation_directory}/{self.equation_solver}/{atmkey}/{self.proc}/{self.Mod}'
            ensure_dir(topdir)
            input_directory = topdir + '/' + 'in'
            ensure_dir(input_directory)
            output_directory = topdir + '/' + 'out'
            ensure_dir(output_directory)

            # loop on molecular model resolution
            # molecular_resolution = np.array(['coarse','medium','fine'])
            # select only COARSE Model
            molecular_resolution = 'coarse'

            # water vapor
            pwv_str = 'H2O ' + str(pwv) + ' MM'
            pwv_index = int(10 * pwv)

            # airmass
            airmass_index = int(airmass * 10)

            # Ozone
            oz_str = 'O3 ' + str(ozone) + ' DU'
            ozone_index = int(ozone / 10.)

            base_filename = f'{base_filename_part1}{atmkey}_{self.proc}_{self.Mod}_z{airmass_index}' \
                            f'_pwv{pwv_index}_oz{ozone_index}_aer{aerosol_index}'

            self.settings["data_files_path"] = self.libradtran_path + 'data'

            self.settings[
                "atmosphere_file"] = self.libradtran_path + 'data/atmmod/' + atmosphere + '.dat'
            self.settings["albedo"] = '0.2'

            self.settings["rte_solver"] = equation_solver_equations

            if self.Mod == 'rt':
                self.settings[
                    "mol_abs_param"] = absorption_model + ' ' + molecular_resolution
            else:
                self.settings["mol_abs_param"] = absorption_model

            # Convert airmass into zenith angle
            sza = np.arccos(1. / airmass) * 180. / np.pi

            # Should be no_absorption
            if runtype == 'aerosol_default':
                self.settings["aerosol_default"] = ''
            elif runtype == 'aerosol_special':
                self.settings["aerosol_default"] = ''
                self.settings["aerosol_set_tau_at_wvl"] = aerosol_string

            if runtype == 'no_scattering':
                self.settings["no_scattering"] = ''
            if runtype == 'no_absorption':
                self.settings["no_absorption"] = ''

            # set up the ozone value
            self.settings["mol_modify"] = pwv_str
            self.settings["mol_modify2"] = oz_str

            # rescale pressure   if reasonable pressure values are provided
            if 600. < pressure < 1015.:
                self.settings["pressure"] = pressure
            else:
                self.my_logger.error(f'\n\tcrazy pressure p={pressure} hPa')

            self.settings["output_user"] = '******'
            self.settings["altitude"] = str(
                parameters.OBS_ALTITUDE)  # Altitude LSST observatory
            self.settings[
                "source"] = 'solar ' + self.libradtran_path + 'data/solar_flux/kurudz_1.0nm.dat'
            self.settings["sza"] = str(sza)
            self.settings["phi0"] = '0'
            self.settings["wavelength"] = '250.0 1200.0'
            self.settings[
                "output_quantity"] = 'reflectivity'  # 'transmittance' #
            self.settings["quiet"] = ''

            input_filename = os.path.join(input_directory,
                                          base_filename + '.INP')
            output_filename = os.path.join(input_directory,
                                           base_filename + '.OUT')

            self.write_input(input_filename)
            self.run(input_filename,
                     output_filename,
                     path=self.libradtran_path)

        return output_filename
Ejemplo n.º 4
0
def ImageSim(image_filename,
             spectrum_filename,
             outputdir,
             pwv=5,
             ozone=300,
             aerosols=0.03,
             A1=1,
             A2=1,
             psf_poly_params=None,
             psf_type=None,
             with_rotation=True,
             with_stars=True,
             with_adr=True):
    """ The basic use of the extractor consists first to define:
    - the path to the fits image from which to extract the image,
    - the path of the output directory to save the extracted spectrum (created automatically if does not exist yet),
    - the rough position of the object in the image,
    - the name of the target (to search for the extra-atmospheric spectrum if available).
    Then different parameters and systematics can be set:
    - pwv: the pressure water vapor (in mm)
    - ozone: the ozone quantity (in XX)
    - aerosols: the vertical aerosol optical depth
    - A1: a global grey absorption parameter for the spectrum
    - A2: the relative amplitude of second order compared with first order
    - with_rotation: rotate the spectrum according to the disperser characteristics (True by default)
    - with_stars: include stars in the image field (True by default)
    - with_adr: include ADR effect (True by default)
    """
    my_logger = set_logger(__name__)
    my_logger.info(f'\n\tStart IMAGE SIMULATOR')
    # Load reduced image
    spectrum, telescope, disperser, target = SimulatorInit(spectrum_filename)
    image = ImageModel(image_filename, target_label=target.label)
    guess = np.array([spectrum.header['TARGETX'], spectrum.header['TARGETY']])
    if "CCDREBIN" in spectrum.header:
        guess *= spectrum.header["CCDREBIN"]
    if parameters.DEBUG:
        image.plot_image(scale='symlog', target_pixcoords=guess)
    # Fit the star 2D profile
    my_logger.info('\n\tSearch for the target in the image...')
    target_pixcoords = find_target(image, guess)
    # Background model
    my_logger.info('\n\tBackground model...')
    bgd_level = float(np.mean(spectrum.spectrogram_bgd))
    background = BackgroundModel(level=bgd_level,
                                 frame=None)  # (1600, 1650, 100))
    if parameters.DEBUG:
        background.plot_model()

    # Target model
    my_logger.info('\n\tStar model...')
    # Spectrogram is simulated with spectrum.x0 target position: must be this position to simualte the target.
    star = StarModel(image.target_pixcoords, image.target_star2D,
                     image.target_star2D.p[0])
    # reso = star.fwhm
    if parameters.DEBUG:
        star.plot_model()
    # Star field model
    starfield = None
    if with_stars:
        my_logger.info('\n\tStar field model...')
        starfield = StarFieldModel(image)
        if parameters.DEBUG:
            image.plot_image(scale='symlog',
                             target_pixcoords=starfield.pixcoords)
            starfield.plot_model()

    # Spectrum model
    my_logger.info('\n\tSpectrum model...')
    airmass = image.header['AIRMASS']
    pressure = image.header['OUTPRESS']
    temperature = image.header['OUTTEMP']
    telescope = TelescopeTransmission(image.filter_label)

    # Rotation: useful only to fill the Dy_disp_axis column in PSF table
    if not with_rotation:
        rotation_angle = 0
    else:
        rotation_angle = spectrum.rotation_angle

    # Load PSF
    if psf_type is not None:
        from spectractor.extractor.psf import load_PSF
        parameters.PSF_TYPE = psf_type
        psf = load_PSF(psf_type=psf_type)
        spectrum.psf = psf
        spectrum.chromatic_psf.psf = psf
    if psf_poly_params is None:
        my_logger.info('\n\tUse PSF parameters from _table.csv file.')
        psf_poly_params = spectrum.chromatic_psf.from_table_to_poly_params()
    else:
        spectrum.chromatic_psf.deg = (len(psf_poly_params) - 1) // (
            len(spectrum.chromatic_psf.psf.param_names) - 2) - 1
        spectrum.chromatic_psf.set_polynomial_degrees(
            spectrum.chromatic_psf.deg)
        if spectrum.chromatic_psf.deg == 0:  # x_c must have deg >= 1
            psf_poly_params.insert(1, 0)
        my_logger.info(
            f'\n\tUse PSF parameters {psf_poly_params} as polynoms of '
            f'degree {spectrum.chromatic_psf.degrees}')
    if psf_type is not None and psf_poly_params is not None:
        spectrum.chromatic_psf.init_table()

    # Simulate spectrogram
    spectrogram = SpectrogramSimulatorCore(spectrum,
                                           telescope,
                                           disperser,
                                           airmass,
                                           pressure,
                                           temperature,
                                           pwv=pwv,
                                           ozone=ozone,
                                           aerosols=aerosols,
                                           A1=A1,
                                           A2=A2,
                                           D=spectrum.disperser.D,
                                           shift_x=0.,
                                           shift_y=0.,
                                           shift_t=0.,
                                           B=1.,
                                           psf_poly_params=psf_poly_params,
                                           angle=rotation_angle,
                                           with_background=False,
                                           fast_sim=False,
                                           full_image=True,
                                           with_adr=with_adr)

    # now we include effects related to the wrong extraction of the spectrum:
    # wrong estimation of the order 0 position and wrong DISTANCE2CCD
    # distance = spectrum.chromatic_psf.get_algebraic_distance_along_dispersion_axis()
    # spectrum.disperser.D = parameters.DISTANCE2CCD
    # spectrum.lambdas = spectrum.disperser.grating_pixel_to_lambda(distance, spectrum.x0, order=1)

    # Image model
    my_logger.info('\n\tImage model...')
    image.compute(star, background, spectrogram, starfield=starfield)

    # Recover true spectrum
    spectrogram.set_true_spectrum(spectrogram.lambdas,
                                  ozone,
                                  pwv,
                                  aerosols,
                                  shift_t=0)
    true_lambdas = np.copy(spectrogram.true_lambdas)
    true_spectrum = np.copy(spectrogram.true_spectrum)

    # Saturation effects
    saturated_pixels = np.where(spectrogram.data > image.saturation)[0]
    if len(saturated_pixels) > 0:
        my_logger.warning(
            f"\n\t{len(saturated_pixels)} saturated pixels detected above saturation "
            f"level at {image.saturation} ADU/s in the spectrogram."
            f"\n\tSpectrogram maximum is at {np.max(spectrogram.data)} ADU/s.")
    image.data[image.data > image.saturation] = image.saturation

    # Convert data from ADU/s in ADU
    image.convert_to_ADU_units()

    # Add Poisson and read-out noise
    image.add_poisson_and_read_out_noise()

    # Round float ADU into closest integers
    # image.data = np.around(image.data)

    # Plot
    if parameters.VERBOSE and parameters.DISPLAY:  # pragma: no cover
        image.convert_to_ADU_rate_units()
        image.plot_image(scale="symlog",
                         title="Image simulation",
                         target_pixcoords=target_pixcoords,
                         units=image.units)
        image.convert_to_ADU_units()

    # Set output path
    ensure_dir(outputdir)
    output_filename = image_filename.split('/')[-1]
    output_filename = (output_filename.replace('reduc',
                                               'sim')).replace('trim', 'sim')
    output_filename = os.path.join(outputdir, output_filename)

    # Save images and parameters
    image.header['A1_T'] = A1
    image.header['A2_T'] = A2
    image.header['X0_T'] = spectrum.x0[0]
    image.header['Y0_T'] = spectrum.x0[1]
    image.header['D2CCD_T'] = spectrum.disperser.D
    image.header['OZONE_T'] = ozone
    image.header['PWV_T'] = pwv
    image.header['VAOD_T'] = aerosols
    image.header['ROT_T'] = rotation_angle
    image.header['ROTATION'] = int(with_rotation)
    image.header['STARS'] = int(with_stars)
    image.header['BKGD_LEV'] = background.level
    image.header['PSF_DEG'] = spectrum.spectrogram_deg
    image.header['PSF_TYPE'] = parameters.PSF_TYPE
    psf_poly_params_truth = np.array(psf_poly_params)
    if psf_poly_params_truth.size > spectrum.spectrogram_Nx:
        psf_poly_params_truth = psf_poly_params_truth[spectrum.spectrogram_Nx:]
    image.header['LBDAS_T'] = np.array_str(true_lambdas,
                                           max_line_width=1000000,
                                           precision=2)
    image.header['AMPLIS_T'] = np.array_str(true_spectrum,
                                            max_line_width=1000000,
                                            precision=2)
    image.header['PSF_P_T'] = np.array_str(psf_poly_params_truth,
                                           max_line_width=1000000,
                                           precision=4)
    image.save_image(output_filename, overwrite=True)
    return image