Ejemplo n.º 1
0
def SimulatorInit(filename, fast_load=False):
    """ SimulatorInit
    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 SIMULATOR initialisation')
    # Load data spectrum
    spectrum = Spectrum(filename, fast_load=fast_load)

    # TELESCOPE TRANSMISSION
    # ------------------------
    telescope = TelescopeTransmission(spectrum.filter_label)

    # DISPERSER TRANSMISSION
    # ------------------------
    if not isinstance(spectrum.disperser, str):
        disperser = spectrum.disperser
    else:
        disperser = Hologram(spectrum.disperser)

    # STAR SPECTRUM
    # ------------------------
    if not isinstance(spectrum.target, str):
        target = spectrum.target
    else:
        target = Target(spectrum.target)

    return spectrum, telescope, disperser, target
Ejemplo n.º 2
0
 def __init__(self,
              x,
              y,
              yerr,
              file_name="",
              nwalkers=18,
              nsteps=1000,
              burnin=100,
              nbins=10,
              verbose=0,
              plot=False,
              live_fit=False,
              truth=None):
     FitWorkspace.__init__(self,
                           file_name,
                           nwalkers,
                           nsteps,
                           burnin,
                           nbins,
                           verbose,
                           plot,
                           live_fit,
                           truth=truth)
     self.my_logger = set_logger(self.__class__.__name__)
     self.x = x
     self.data = y
     self.err = yerr
     self.a = 1
     self.b = 1
     self.p = np.array([self.a, self.b])
     self.input_labels = ["a", "b"]
     self.axis_names = ["$a$", "$b$"]
     self.bounds = np.array([(-100, 100), (-100, 100)])
     self.nwalkers = max(2 * self.ndim, nwalkers)
Ejemplo n.º 3
0
    def __init__(self,
                 spectrum,
                 atmgrid,
                 telescope,
                 disperser,
                 target,
                 filename=""):
        """
        Args:
            filename (:obj:`str`): path to the image
        """
        self.my_logger = set_logger(self.__class__.__name__)

        self.spectrum = spectrum
        self.header = spectrum.header
        self.disperser = disperser
        self.target = target
        self.telescope = telescope

        self.atmgrid = atmgrid
        self.lambdas = self.atmgrid.atmgrid[0, self.atmgrid.index_atm_data:]
        self.lambdas_binwidths = np.gradient(self.lambdas)
        self.spectragrid = None

        self.filename = ""
        if filename != "":
            self.filename = filename
            self.spectrum.load_spectrum(filename)
Ejemplo n.º 4
0
    def __init__(self, label, verbose=False):
        """Initialize ArcLamp class.

        Parameters
        ----------
        label: str
            String label to name the lamp.
        verbose: bool, optional
            Set True to increase verbosity (default: False)

        Examples
        --------

        Mercury-Argon lamp:

        >>> t = ArcLamp("HG-AR", verbose=False)
        >>> print([line.wavelength for line in t.lines.lines][:5])
        [253.652, 296.728, 302.15, 313.155, 334.148]
        >>> print(t.emission_spectrum)
        True

        """
        Target.__init__(self, label, verbose=verbose)
        self.my_logger = set_logger(self.__class__.__name__)
        self.emission_spectrum = True
        self.lines = Lines(HGAR_LINES, emission_spectrum=True, orders=[1, 2])
Ejemplo n.º 5
0
    def __init__(self, level, frame=None):
        """Create a BackgroundModel instance.

        The background model size is set with the parameters.CCD_IMSIZE global keyword.

        Parameters
        ----------
        level: float
            The mean level of the background in image units.
        frame: array_like, None
            (x, y, smooth) right and upper limits in pixels of a vignetting frame,
            and the smoothing gaussian width (default: None).

        Examples
        --------
        >>> from spectractor import parameters
        >>> parameters.CCD_IMSIZE = 200
        >>> bgd = BackgroundModel(10)
        >>> model = bgd.model()
        >>> np.all(model==10)
        True
        >>> model.shape
        (200, 200)
        >>> bgd = BackgroundModel(10, frame=(160, 180, 3))
        >>> bgd.plot_model()
        """
        self.my_logger = set_logger(self.__class__.__name__)
        self.level = level
        if self.level <= 0:
            self.my_logger.warning(
                '\n\tBackground level must be strictly positive.')
        else:
            self.my_logger.info(f'\n\tBackground set to {level:.3f} ADU/s.')
        self.frame = frame
Ejemplo n.º 6
0
    def __init__(self, airmass, pressure, temperature):
        """Class to evaluate an atmospheric transmission using Libradtran.

        Parameters
        ----------
        airmass: float
            Airmass of the source object.
        pressure: float
            Pressure of the atmosphere in hPa.
        temperature: float
            Temperature of the atmosphere in Celsius degrees.

        Examples
        --------
        >>> a = Atmosphere(airmass=1.2, pressure=800, temperature=5)
        >>> print(a.airmass)
        1.2
        >>> print(a.pressure)
        800
        >>> print(a.temperature)
        5
        >>> print(a.transmission(500))
        1.0
        """
        self.my_logger = set_logger(self.__class__.__name__)
        self.airmass = airmass
        self.pressure = pressure
        self.temperature = temperature
        self.pwv = None
        self.ozone = None
        self.aerosols = None
        self.transmission = lambda x: np.ones_like(x).astype(float)
        self.title = ""
        self.label = ""
Ejemplo n.º 7
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.º 8
0
    def __init__(self, label, verbose=False):
        """Initialize Target class.

        Parameters
        ----------
        label: str
            String label to name the target
        verbose: bool, optional
            Set True to increase verbosity (default: False)

        """
        self.my_logger = set_logger(self.__class__.__name__)
        self.label = label
        self.type = None
        self.wavelengths = []
        self.spectra = []
        self.verbose = verbose
        self.emission_spectrum = False
        self.hydrogen_only = False
        self.sed = None
        self.lines = None
        self.radec_position = None
        self.radec_position_after_pm = None
        self.redshift = 0
        self.image = None
        self.image_x0 = None
        self.image_y0 = None
Ejemplo n.º 9
0
def SpectrumSimulatorCore(spectrum,
                          telescope,
                          disperser,
                          airmass=1.0,
                          pressure=800,
                          temperature=10,
                          pwv=5,
                          ozone=300,
                          aerosols=0.05,
                          A1=1.0,
                          A2=0.,
                          reso=0,
                          D=parameters.DISTANCE2CCD,
                          shift=0.):
    """ SimulatorCore
    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 SPECTRUMSIMULATOR core program')
    # SIMULATE ATMOSPHERE
    # -------------------
    atmosphere = Atmosphere(airmass, pressure, temperature)

    # SPECTRUM SIMULATION
    # --------------------
    spectrum_simulation = SpectrumSimulation(spectrum, atmosphere, telescope,
                                             disperser)
    spectrum_simulation.simulate(A1, A2, ozone, pwv, aerosols, reso, D, shift)
    if parameters.DEBUG:
        spectrum_simulation.plot_spectrum(force_lines=True)
    return spectrum_simulation
Ejemplo n.º 10
0
 def __init__(self):
     self.my_logger = set_logger(self.__class__.__name__)
     self.p = np.array([])
     self.param_names = ["amplitude", "x_c", "y_c", "saturation"]
     self.axis_names = ["$A$", r"$x_c$", r"$y_c$", "saturation"]
     self.bounds = [[]]
     self.p_default = np.array([1, 0, 0, 1])
     self.max_half_width = np.inf
Ejemplo n.º 11
0
def SpectrumSimulator(filename,
                      outputdir="",
                      pwv=5,
                      ozone=300,
                      aerosols=0.05,
                      A1=1.,
                      A2=0.,
                      reso=None,
                      D=parameters.DISTANCE2CCD,
                      shift=0.):
    """ Simulator
    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 SPECTRACTORSIM')
    # Initialisation
    spectrum, telescope, disperser, target = SimulatorInit(filename)

    # SIMULATE SPECTRUM
    # -------------------
    airmass = spectrum.header['AIRMASS']
    pressure = spectrum.header['OUTPRESS']
    temperature = spectrum.header['OUTTEMP']

    spectrum_simulation = SpectrumSimulatorCore(spectrum,
                                                telescope,
                                                disperser,
                                                airmass,
                                                pressure,
                                                temperature,
                                                pwv,
                                                ozone,
                                                aerosols,
                                                A1=A1,
                                                A2=A2,
                                                reso=reso,
                                                D=D,
                                                shift=shift)

    # Save the spectrum
    spectrum_simulation.header['OZONE_T'] = ozone
    spectrum_simulation.header['PWV_T'] = pwv
    spectrum_simulation.header['VAOD_T'] = aerosols
    spectrum_simulation.header['A1_T'] = A1
    spectrum_simulation.header['A2_T'] = A2
    spectrum_simulation.header['RESO_T'] = reso
    spectrum_simulation.header['D2CCD_T'] = D
    spectrum_simulation.header['X0_T'] = shift
    output_filename = filename.replace('spectrum', 'sim')
    if outputdir != "":
        base_filename = filename.split('/')[-1]
        output_filename = os.path.join(
            outputdir, base_filename.replace('spectrum', 'sim'))
    spectrum_simulation.save_spectrum(output_filename, overwrite=True)

    return spectrum_simulation
Ejemplo n.º 12
0
def run_spectrum_minimisation(fit_workspace, method="newton"):
    """Interface function to fit spectrum simulation parameters to data.

    Parameters
    ----------
    fit_workspace: SpectrumFitWorkspace
        An instance of the SpectrogramFitWorkspace class.
    method: str, optional
        Fitting method (default: 'newton').

    Examples
    --------

    >>> filename = 'tests/data/sim_20170530_134_spectrum.fits'
    >>> atmgrid_filename = filename.replace('sim', 'reduc').replace('spectrum', 'atmsim')
    >>> load_config("config/ctio.ini")
    >>> w = SpectrumFitWorkspace(filename, atmgrid_file_name=atmgrid_filename, verbose=1, plot=True, live_fit=False)
    >>> parameters.VERBOSE = True
    >>> run_spectrum_minimisation(w, method="newton")

    """
    my_logger = set_logger(__name__)
    guess = np.asarray(fit_workspace.p)
    if method != "newton":
        run_minimisation(fit_workspace, method=method)
    else:
        # fit_workspace.simulation.fast_sim = True
        # costs = np.array([fit_workspace.chisq(guess)])
        # if parameters.DISPLAY and (parameters.DEBUG or fit_workspace.live_fit):
        #     fit_workspace.plot_fit()
        # params_table = np.array([guess])
        my_logger.info(f"\n\tStart guess: {guess}\n\twith {fit_workspace.input_labels}")
        epsilon = 1e-4 * guess
        epsilon[epsilon == 0] = 1e-4
        epsilon[-1] = 0.001 * np.max(fit_workspace.data)

        # fit_workspace.simulation.fast_sim = True
        # fit_workspace.simulation.fix_psf_cube = False
        # run_minimisation_sigma_clipping(fit_workspace, method="newton", epsilon=epsilon, fix=fit_workspace.fixed,
        #                                 xtol=1e-4, ftol=1 / fit_workspace.data.size, sigma_clip=10, niter_clip=3,
        #                                 verbose=False)

        fit_workspace.simulation.fast_sim = False
        run_minimisation_sigma_clipping(fit_workspace, method="newton", epsilon=epsilon, fix=fit_workspace.fixed,
                                        xtol=1e-6, ftol=1 / fit_workspace.data.size, sigma_clip=10, niter_clip=3,
                                        verbose=False)
        if fit_workspace.filename != "":
            parameters.SAVE = True
            ipar = np.array(np.where(np.array(fit_workspace.fixed).astype(int) == 0)[0])
            fit_workspace.plot_correlation_matrix(ipar)
            header = f"{fit_workspace.spectrum.date_obs}\nchi2: {fit_workspace.costs[-1] / fit_workspace.data.size}"
            fit_workspace.save_parameters_summary(ipar, header=header)
            # save_gradient_descent(fit_workspace, costs, params_table)
            fit_workspace.plot_fit()
            parameters.SAVE = False
Ejemplo n.º 13
0
    def __init__(self,
                 spectrum,
                 atmosphere,
                 telescope,
                 disperser,
                 fast_sim=True):
        """Class to simulate cross spectrum.

        Parameters
        ----------
        spectrum: Spectrum
            Spectrum instance to load main properties before simulation.
        atmosphere: Atmosphere
            Atmosphere or AtmosphereGrid instance to make the atmospheric simulation.
        telescope: TelescopeTransmission
            Telescope transmission.
        disperser: Grating
            Disperser instance.
        fast_sim: bool, optional
            If True, do a fast simulation without integrating within the wavelength bins (default: True).

        Examples
        --------
        >>> spectrum, telescope, disperser, target = SimulatorInit("./tests/data/reduc_20170530_134_spectrum.fits")
        >>> atmosphere = Atmosphere(airmass=1.2, pressure=800, temperature=10)
        >>> sim = SpectrumSimulation(spectrum, atmosphere, telescope, disperser, fast_sim=True)

        """
        Spectrum.__init__(self)
        for k, v in list(spectrum.__dict__.items()):
            self.__dict__[k] = copy.copy(v)
        self.my_logger = set_logger(self.__class__.__name__)
        self.disperser = disperser
        self.telescope = telescope
        self.atmosphere = atmosphere
        self.fast_sim = fast_sim
        # save original pixel distances to zero order
        # self.disperser.grating_lambda_to_pixel(self.lambdas, x0=self.x0, order=1)
        # now reset data
        self.lambdas = None
        self.lambdas_order2 = None
        self.err = None
        self.model = lambda x: np.zeros_like(x)
        self.model_err = lambda x: np.zeros_like(x)
        lbdas_sed = self.target.wavelengths[0]
        sub = np.where((lbdas_sed > parameters.LAMBDA_MIN)
                       & (lbdas_sed < parameters.LAMBDA_MAX))
        self.lambdas_step = min(parameters.LAMBDA_STEP, np.min(lbdas_sed[sub]))
Ejemplo n.º 14
0
def SpectrogramSimulatorCore(spectrum,
                             telescope,
                             disperser,
                             airmass=1.0,
                             pressure=800,
                             temperature=10,
                             pwv=5,
                             ozone=300,
                             aerosols=0.05,
                             A1=1.0,
                             A2=0.,
                             D=parameters.DISTANCE2CCD,
                             shift_x=0.,
                             shift_y=0.,
                             shift_t=0.,
                             angle=0.,
                             B=1.,
                             psf_poly_params=None,
                             with_background=True,
                             fast_sim=False,
                             full_image=False,
                             with_adr=True):
    """ SimulatorCore
    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 SPECTROGRAMSIMULATOR core program')
    # SIMULATE ATMOSPHERE
    # -------------------

    atmosphere = Atmosphere(airmass, pressure, temperature)
    spectrum.rotation_angle = angle

    # SPECTRUM SIMULATION
    # --------------------
    spectrogram_simulation = SpectrogramModel(spectrum,
                                              atmosphere,
                                              telescope,
                                              disperser,
                                              with_background=with_background,
                                              fast_sim=fast_sim,
                                              full_image=full_image,
                                              with_adr=with_adr)
    spectrogram_simulation.simulate(A1, A2, ozone, pwv, aerosols, D, shift_x,
                                    shift_y, angle, B, psf_poly_params)
    return spectrogram_simulation
Ejemplo n.º 15
0
    def __init__(self, centroid_coords, psf, amplitude):
        """Create a StarModel instance.

        The model is based on an Astropy Fittable2DModel. The centroid and amplitude
        parameters of the given model are updated by the dedicated arguments.

        Parameters
        ----------
        centroid_coords: array_like
            Tuple of (x,y) coordinates of the desired star centroid in pixels.
        psf: PSF
            PSF model
        amplitude: float
            The desired amplitude of the star in image units.

        Examples
        --------
        >>> from spectractor.extractor.psf import Moffat
        >>> p = (100, 50, 50, 5, 2, 200)
        >>> psf = Moffat(p)
        >>> s = StarModel((20, 10), psf, 200)
        >>> s.plot_model()
        >>> s.x0
        20
        >>> s.y0
        10
        >>> s.amplitude
        200
        """
        self.my_logger = set_logger(self.__class__.__name__)
        self.x0 = centroid_coords[0]
        self.y0 = centroid_coords[1]
        self.amplitude = amplitude
        # self.target = target
        self.psf = copy.deepcopy(psf)
        self.psf.p[1] = self.x0
        self.psf.p[2] = self.y0
        self.psf.p[0] = amplitude
        # to be realistic, usually fitted fwhm is too big, divide gamma by 2
        self.fwhm = self.psf.p[3]
Ejemplo n.º 16
0
    def __init__(self, label, verbose=False):
        """Initialize Star class.

        Parameters
        ----------
        label: str
            String label to name the target
        verbose: bool, optional
            Set True to increase verbosity (default: False)

        Examples
        --------

        Emission line object:

        >>> s = Star('3C273')
        >>> print(s.label)
        3C273
        >>> print(s.radec_position.dec)
        2d03m08.598s
        >>> print(s.emission_spectrum)
        True

        Standard star:

        >>> s = Star('HD111980')
        >>> print(s.label)
        HD111980
        >>> print(s.radec_position.dec)
        -18d31m20.009s
        >>> print(s.emission_spectrum)
        False

        """
        Target.__init__(self, label, verbose=verbose)
        self.my_logger = set_logger(self.__class__.__name__)
        self.simbad = None
        self.load()
Ejemplo n.º 17
0
    def __init__(self, label, verbose=False):
        """Initialize Monochromator class.

        Parameters
        ----------
        label: str
            String label to name the monochromator.
        verbose: bool, optional
            Set True to increase verbosity (default: False)

        Examples
        --------

        >>> t = Monochromator("XX", verbose=False)
        >>> print(t.label)
        XX
        >>> print(t.emission_spectrum)
        True

        """
        Target.__init__(self, label, verbose=verbose)
        self.my_logger = set_logger(self.__class__.__name__)
        self.emission_spectrum = True
        self.lines = Lines([], emission_spectrum=True, orders=[1, 2])
Ejemplo n.º 18
0
    def __init__(self, filter_label=""):
        """Transmission of the telescope as product of the following transmissions:

        - mirrors
        - throughput
        - quantum efficiency
        - Filter

        Parameters
        ----------
        filter_label: str, optional
            The filter string name.

        Examples
        --------
        >>> t = TelescopeTransmission()
        >>> t.plot_transmission()
        """

        self.my_logger = set_logger(self.__class__.__name__)
        self.filter_label = filter_label
        self.transmission = None
        self.transmission_err = None
        self.load_transmission()
Ejemplo n.º 19
0
 def __init__(self, filename, target_label=None):
     self.my_logger = set_logger(self.__class__.__name__)
     Image.__init__(self, filename, target_label=target_label)
     self.true_lambdas = None
     self.true_spectrum = None
Ejemplo n.º 20
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
Ejemplo n.º 21
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.º 22
0
    def __init__(self,
                 image_filename="",
                 filename="",
                 airmass=1.,
                 pressure=800.,
                 temperature=10.,
                 pwv_grid=[0, 10, 10],
                 ozone_grid=[100, 700, 7],
                 aerosol_grid=[0, 0.1, 10]):
        """Class to load and interpolate grids of atmospheric transmission computed with Libradtran.

        Parameters
        ----------
        image_filename: str, optional
            The original image fits file name from which the grid was computed or has to be computed (default: "").
        filename: str, optional
            The file name of the atmospheric grid if it exists (default: "").
        airmass: float, optional
            Airmass of the source object (default: 1).
        pressure: float, optional
            Pressure of the atmosphere in hPa (default: 800).
        temperature: float, optional
            Temperature of the atmosphere in Celsius degrees (default: 10).
        pwv_grid: list
            List of 3 numbers for the PWV quantity: min, max, number of simulations (default: [0, 10, 10]).
        ozone_grid: list
            List of 3 numbers for the ozone quantity: min, max, number of simulations (default: [100, 700, 7]).
        aerosol_grid: list
            List of 3 numbers for the aerosol quantity: min, max, number of simulations (default: [0, 0.1, 10]).

        Examples
        --------
        >>> a = AtmosphereGrid(filename='./tests/data/reduc_20170530_134_atmsim.fits')
        >>> a.image_filename.split('/')[-1]
        'reduc_20170530_134_spectrum.fits'
        """
        Atmosphere.__init__(self, airmass, pressure, temperature)
        self.my_logger = set_logger(self.__class__.__name__)
        self.image_filename = image_filename
        self.filename = filename
        # ------------------------------------------------------------------------
        # Definition of data format for the atmospheric grid
        # -----------------------------------------------------------------------------

        #  row 0 : count number
        #  row 1 : aerosol value
        #  row 2 : pwv value
        #  row 3 : ozone value
        #  row 4 : data start
        self.index_atm_count = 0
        self.index_atm_aer = 1
        self.index_atm_pwv = 2
        self.index_atm_oz = 3
        self.index_atm_data = 4

        # specify parameters for the atmospheric grid
        self.atmgrid = None
        self.NB_ATM_HEADER = self.index_atm_data + 1
        self.NB_ATM_DATA = len(parameters.LAMBDAS) - 1
        self.NB_ATM_POINTS = 0
        self.AER_Points = np.array([])
        self.OZ_Points = np.array([])
        self.PWV_Points = np.array([])
        self.set_grid(pwv_grid=pwv_grid,
                      ozone_grid=ozone_grid,
                      aerosol_grid=aerosol_grid)

        # the interpolated grid
        self.lambdas = parameters.LAMBDAS
        self.model = None

        self.header = fits.Header()
        if filename != "":
            self.load_file(filename)
Ejemplo n.º 23
0
    def __init__(self, file_name, atmgrid_file_name="", nwalkers=18, nsteps=1000, burnin=100, nbins=10,
                 verbose=0, plot=False, live_fit=False, truth=None):
        """Class to fit a spectrum extracted with Spectractor.

        The spectrum is supposed to be the product of the star SED, the instrument throughput and the atmospheric
        transmission, contaminated eventually by a second order diffraction.
        The truth parameters are loaded from the file header if provided.
        If provided, the atmospheric grid is used for the atmospheric transmission simulations and interpolated
        with splines, otherwise Libradtran is called at each step (slower).

        Parameters
        ----------
        file_name: str
            Spectrum file name.
        atmgrid_file_name: str, optional
            Atmospheric grid file name (default: "").
        nwalkers: int, optional
            Number of walkers for MCMC fitting.
        nsteps: int, optional
            Number of steps for MCMC fitting.
        burnin: int, optional
            Number of burn-in steps for MCMC fitting.
        nbins: int, optional
            Number of bins for MCMC chains analysis.
        verbose: int, optional
            Verbosity level (default: 0).
        plot: bool, optional
            If True, many plots are produced (default: False).
        live_fit: bool, optional
            If True, many plots along the fitting procedure are produced to see convergence in live (default: False).
        truth: array_like, optional
            Array of truth parameters to compare with the best fit result (default: None).

        Examples
        --------

        >>> filename = 'tests/data/reduc_20170530_134_spectrum.fits'
        >>> atmgrid_filename = filename.replace('spectrum', 'atmsim')
        >>> load_config("config/ctio.ini")
        >>> w = SpectrumFitWorkspace(filename, atmgrid_file_name=atmgrid_filename, nsteps=1000,
        ... burnin=2, nbins=10, verbose=1, plot=True, live_fit=False)
        >>> lambdas, model, model_err = w.simulate(*w.p)
        >>> w.plot_fit()

        """
        FitWorkspace.__init__(self, file_name, nwalkers, nsteps, burnin, nbins, verbose, plot, live_fit, truth=truth)
        if "spectrum" not in file_name:
            raise ValueError("file_name argument must contain spectrum keyword and be an output from Spectractor.")
        self.my_logger = set_logger(self.__class__.__name__)
        self.spectrum, self.telescope, self.disperser, self.target = SimulatorInit(file_name)
        self.airmass = self.spectrum.header['AIRMASS']
        self.pressure = self.spectrum.header['OUTPRESS']
        self.temperature = self.spectrum.header['OUTTEMP']
        if atmgrid_file_name == "":
            self.atmosphere = Atmosphere(self.airmass, self.pressure, self.temperature)
        else:
            self.use_grid = True
            self.atmosphere = AtmosphereGrid(file_name, atmgrid_file_name)
            if parameters.VERBOSE:
                self.my_logger.info(f'\n\tUse atmospheric grid models from file {atmgrid_file_name}. ')
        self.lambdas = self.spectrum.lambdas
        self.data = self.spectrum.data
        self.err = self.spectrum.err
        self.data_cov = self.spectrum.cov_matrix
        self.A1 = 1.0
        self.A2 = 0
        self.ozone = 400.
        self.pwv = 3
        self.aerosols = 0.05
        self.reso = -1
        self.D = self.spectrum.header['D2CCD']
        self.shift_x = self.spectrum.header['PIXSHIFT']
        self.B = 0
        self.p = np.array([self.A1, self.A2, self.ozone, self.pwv, self.aerosols, self.reso, self.D,
                           self.shift_x, self.B])
        self.fixed = [False] * self.p.size
        # self.fixed[0] = True
        self.fixed[1] = "A2_T" not in self.spectrum.header  # fit A2 only on sims to evaluate extraction biases
        self.fixed[5] = True
        # self.fixed[6:8] = [True, True]
        self.fixed[7] = True
        self.fixed[8] = True
        # self.fixed[-1] = True
        self.input_labels = ["A1", "A2", "ozone", "PWV", "VAOD", "reso [pix]", r"D_CCD [mm]",
                             r"alpha_pix [pix]", "B"]
        self.axis_names = ["$A_1$", "$A_2$", "ozone", "PWV", "VAOD", "reso [pix]", r"$D_{CCD}$ [mm]",
                           r"$\alpha_{\mathrm{pix}}$ [pix]", "$B$"]
        bounds_D = (self.D - 5 * parameters.DISTANCE2CCD_ERR, self.D + 5 * parameters.DISTANCE2CCD_ERR)
        self.bounds = [(0, 2), (0, 2/parameters.GRATING_ORDER_2OVER1), (100, 700), (0, 10), (0, 0.1),
                       (0.1, 10), bounds_D, (-2, 2), (-np.inf, np.inf)]
        if atmgrid_file_name != "":
            self.bounds[2] = (min(self.atmosphere.OZ_Points), max(self.atmosphere.OZ_Points))
            self.bounds[3] = (min(self.atmosphere.PWV_Points), max(self.atmosphere.PWV_Points))
            self.bounds[4] = (min(self.atmosphere.AER_Points), max(self.atmosphere.AER_Points))
        self.nwalkers = max(2 * self.ndim, nwalkers)
        self.simulation = SpectrumSimulation(self.spectrum, self.atmosphere, self.telescope, self.disperser)
        self.amplitude_truth = None
        self.lambdas_truth = None
        self.output_file_name = file_name.replace('_spectrum', '_spectrum_A2=0')
        self.get_truth()
Ejemplo n.º 24
0
def SpectrogramSimulator(filename,
                         outputdir="",
                         pwv=5,
                         ozone=300,
                         aerosols=0.05,
                         A1=1.,
                         A2=0.,
                         D=parameters.DISTANCE2CCD,
                         shift_x=0.,
                         shift_y=0.,
                         shift_t=0.,
                         angle=0.,
                         B=1.,
                         psf_poly_params=None):
    """ Simulator
    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 SPECTRACTORSIM')
    # Initialisation
    spectrum, telescope, disperser, target = SimulatorInit(filename)

    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()

    # SIMULATE SPECTRUM
    # -------------------
    airmass = spectrum.header['AIRMASS']
    pressure = spectrum.header['OUTPRESS']
    temperature = spectrum.header['OUTTEMP']

    spectrogram_simulation = SpectrogramSimulatorCore(
        spectrum,
        telescope,
        disperser,
        airmass,
        pressure,
        temperature,
        pwv,
        ozone,
        aerosols,
        D=D,
        shift_x=shift_x,
        shift_y=shift_y,
        shift_t=shift_t,
        angle=angle,
        B=B,
        psf_poly_params=psf_poly_params)

    # Save the spectrum
    spectrogram_simulation.header['OZONE_T'] = ozone
    spectrogram_simulation.header['PWV_T'] = pwv
    spectrogram_simulation.header['VAOD_T'] = aerosols
    spectrogram_simulation.header['A1_T'] = A1
    spectrogram_simulation.header['A2_T'] = A2
    spectrogram_simulation.header['D2CCD_T'] = D
    spectrogram_simulation.header['X0_T'] = shift_x
    spectrogram_simulation.header['Y0_T'] = shift_y
    spectrogram_simulation.header['TSHIFT_T'] = shift_t
    spectrogram_simulation.header['ROTANGLE'] = angle
    output_filename = filename.replace('spectrum', 'sim')
    if outputdir != "":
        base_filename = filename.split('/')[-1]
        output_filename = os.path.join(
            outputdir, base_filename.replace('spectrum', 'sim'))
    # spectrogram_simulation.save_spectrum(output_filename, overwrite=True)

    return spectrogram_simulation
Ejemplo n.º 25
0
    def __init__(self,
                 file_name,
                 atmgrid_file_name="",
                 nwalkers=18,
                 nsteps=1000,
                 burnin=100,
                 nbins=10,
                 verbose=0,
                 plot=False,
                 live_fit=False,
                 truth=None):
        """Class to fit a spectrogram extracted with Spectractor.

        First the spectrogram is cropped using the parameters.PIXWIDTH_SIGNAL parameter to increase speedness.
        The truth parameters are loaded from the file header if provided.
        If provided, the atmospheric grid is used for the atmospheric transmission simulations and interpolated
        with splines, otherwise Libradtran is called at each step (slower).

        Parameters
        ----------
        file_name: str
            Spectrum file name.
        atmgrid_file_name: str, optional
            Atmospheric grid file name (default: "").
        nwalkers: int, optional
            Number of walkers for MCMC fitting.
        nsteps: int, optional
            Number of steps for MCMC fitting.
        burnin: int, optional
            Number of burn-in steps for MCMC fitting.
        nbins: int, optional
            Number of bins for MCMC chains analysis.
        verbose: int, optional
            Verbosity level (default: 0).
        plot: bool, optional
            If True, many plots are produced (default: False).
        live_fit: bool, optional
            If True, many plots along the fitting procedure are produced to see convergence in live (default: False).
        truth: array_like, optional
            Array of truth parameters to compare with the best fit result (default: None).

        Examples
        --------

        >>> filename = 'tests/data/reduc_20170530_134_spectrum.fits'
        >>> atmgrid_filename = filename.replace('spectrum', 'atmsim')
        >>> load_config("config/ctio.ini")
        >>> w = SpectrogramFitWorkspace(filename, atmgrid_file_name=atmgrid_filename, nsteps=1000,
        ... burnin=2, nbins=10, verbose=1, plot=True, live_fit=False)
        >>> lambdas, model, model_err = w.simulate(*w.p)
        >>> w.plot_fit()

        """
        FitWorkspace.__init__(self,
                              file_name,
                              nwalkers,
                              nsteps,
                              burnin,
                              nbins,
                              verbose,
                              plot,
                              live_fit,
                              truth=truth)
        if "spectrum" not in file_name:
            raise ValueError(
                "file_name argument must contain spectrum keyword and be an output from Spectractor."
            )
        self.filename = self.filename.replace("spectrum", "spectrogram")
        self.spectrum, self.telescope, self.disperser, self.target = SimulatorInit(
            file_name)
        self.airmass = self.spectrum.header['AIRMASS']
        self.pressure = self.spectrum.header['OUTPRESS']
        self.temperature = self.spectrum.header['OUTTEMP']
        self.my_logger = set_logger(self.__class__.__name__)
        if atmgrid_file_name == "":
            self.atmosphere = Atmosphere(self.airmass, self.pressure,
                                         self.temperature)
        else:
            self.use_grid = True
            self.atmosphere = AtmosphereGrid(file_name, atmgrid_file_name)
            if parameters.VERBOSE:
                self.my_logger.info(
                    f'\n\tUse atmospheric grid models from file {atmgrid_file_name}. '
                )
        self.crop_spectrogram()
        self.lambdas = self.spectrum.lambdas
        self.Ny, self.Nx = self.spectrum.spectrogram.shape
        self.data = self.spectrum.spectrogram.flatten()
        self.err = self.spectrum.spectrogram_err.flatten()
        self.A1 = 1.0
        self.A2 = 1.0
        self.ozone = 400.
        self.pwv = 3
        self.aerosols = 0.05
        self.D = self.spectrum.header['D2CCD']
        self.psf_poly_params = self.spectrum.chromatic_psf.from_table_to_poly_params(
        )
        length = len(self.spectrum.chromatic_psf.table)
        self.psf_poly_params = self.psf_poly_params[length:]
        self.psf_poly_params_labels = np.copy(
            self.spectrum.chromatic_psf.poly_params_labels[length:])
        self.psf_poly_params_names = np.copy(
            self.spectrum.chromatic_psf.poly_params_names[length:])
        self.psf_poly_params_bounds = self.spectrum.chromatic_psf.set_bounds_for_minuit(
            data=None)
        self.spectrum.chromatic_psf.psf.apply_max_width_to_bounds(
            max_half_width=self.spectrum.spectrogram_Ny)
        psf_poly_params_bounds = self.spectrum.chromatic_psf.set_bounds()
        self.shift_x = self.spectrum.header['PIXSHIFT']
        self.shift_y = 0.
        self.angle = self.spectrum.rotation_angle
        self.B = 1
        self.saturation = self.spectrum.spectrogram_saturation
        self.p = np.array([
            self.A1, self.A2, self.ozone, self.pwv, self.aerosols, self.D,
            self.shift_x, self.shift_y, self.angle, self.B
        ])
        self.fixed_psf_params = np.array([0, 1, 2, 3, 4, 9])
        self.atm_params_indices = np.array([2, 3, 4])
        self.psf_params_start_index = self.p.size
        self.p = np.concatenate([self.p, self.psf_poly_params])
        self.input_labels = [
            "A1", "A2", "ozone [db]", "PWV [mm]", "VAOD", r"D_CCD [mm]",
            r"shift_x [pix]", r"shift_y [pix]", r"angle [deg]", "B"
        ] + list(self.psf_poly_params_labels)
        self.axis_names = [
            "$A_1$", "$A_2$", "ozone [db]", "PWV [mm]", "VAOD",
            r"$D_{CCD}$ [mm]", r"$\Delta_{\mathrm{x}}$ [pix]",
            r"$\Delta_{\mathrm{y}}$ [pix]", r"$\theta$ [deg]", "$B$"
        ] + list(self.psf_poly_params_names)
        bounds_D = (self.D - 5 * parameters.DISTANCE2CCD_ERR,
                    self.D + 5 * parameters.DISTANCE2CCD_ERR)
        self.bounds = np.concatenate([
            np.array([(0, 2), (0, 2 / parameters.GRATING_ORDER_2OVER1),
                      (100, 700), (0, 10), (0, 0.1), bounds_D, (-2, 2),
                      (-10, 10), (-90, 90), (0.8, 1.2)]),
            psf_poly_params_bounds
        ])
        self.fixed = [False] * self.p.size
        for k, par in enumerate(self.input_labels):
            if "x_c" in par or "saturation" in par or "y_c" in par:
                self.fixed[k] = True
        # A2 is free only if spectrogram is a simulation or if the order 2/1 ratio is not known and flat
        self.fixed[
            1] = "A2_T" not in self.spectrum.header  # not self.spectrum.disperser.flat_ratio_order_2over1
        # self.fixed[5:7] = [True, True]  # DCCD, x0
        self.fixed[6] = True  # Delta x
        # self.fixed[7] = True  # Delta y
        # self.fixed[8] = True  # angle
        self.fixed[9] = True  # B
        if atmgrid_file_name != "":
            self.bounds[2] = (min(self.atmosphere.OZ_Points),
                              max(self.atmosphere.OZ_Points))
            self.bounds[3] = (min(self.atmosphere.PWV_Points),
                              max(self.atmosphere.PWV_Points))
            self.bounds[4] = (min(self.atmosphere.AER_Points),
                              max(self.atmosphere.AER_Points))
        self.nwalkers = max(2 * self.ndim, nwalkers)
        self.simulation = SpectrogramModel(self.spectrum,
                                           self.atmosphere,
                                           self.telescope,
                                           self.disperser,
                                           with_background=True,
                                           fast_sim=False,
                                           with_adr=True)
        self.lambdas_truth = None
        self.amplitude_truth = None
        self.get_spectrogram_truth()
Ejemplo n.º 26
0
    def __init__(self,
                 lines,
                 redshift=0,
                 atmospheric_lines=True,
                 hydrogen_only=False,
                 emission_spectrum=False,
                 orders=[1]):
        """ Main emission/absorption lines in nm. Sorted lines are sorted in self.lines.
        See http://www.pa.uky.edu/~peter/atomic/ or https://physics.nist.gov/PhysRefData/ASD/lines_form.html

        Parameters
        ----------
        lines: list
            List of Line objects to gather and sort.
        redshift: float, optional
            Red shift the spectral lines. Must be positive or null (default: 0)
        atmospheric_lines: bool, optional
            Set True if the atmospheric spectral lines must be included (default: True)
        hydrogen_only: bool, optional
            Set True to gather only the hydrogen spectral lines, atmospheric lines still included (default: False)
        emission_spectrum: bool, optional
            Set True if the spectral line has to be detected in emission (default: False)
        orders: list, optional
            List of integers corresponding to the diffraction order to account for the line search and the
            wavelength calibration (default: [1])

        Examples
        --------
        The default first five lines:

        >>> lines = Lines(ISM_LINES+HYDROGEN_LINES, redshift=0, atmospheric_lines=False, hydrogen_only=False, emission_spectrum=False)
        >>> print([lines.lines[i].wavelength for i in range(5)])
        [353.1, 388.8, 397.0, 410.2, 434.0]

        The four hydrogen lines only:

        >>> lines = Lines(ISM_LINES+HYDROGEN_LINES+ATMOSPHERIC_LINES, redshift=0, atmospheric_lines=False, hydrogen_only=True, emission_spectrum=True)
        >>> print([lines.lines[i].wavelength for i in range(4)])
        [397.0, 410.2, 434.0, 486.3]
        >>> print(lines.emission_spectrum)
        True

        Redshift the hydrogen lines, the atmospheric lines stay unchanged:

        >>> lines = Lines(ISM_LINES+HYDROGEN_LINES+ATMOSPHERIC_LINES, redshift=1, atmospheric_lines=True, hydrogen_only=True, emission_spectrum=True)
        >>> print([lines.lines[i].wavelength for i in range(7)])
        [687.472, 760.3, 763.1, 794.0, 820.4, 822.696, 868.0]

        Redshift all the spectral lines, except the atmospheric lines:

        >>> lines = Lines(ISM_LINES+HYDROGEN_LINES+ATMOSPHERIC_LINES, redshift=1, atmospheric_lines=True, hydrogen_only=False, emission_spectrum=True)
        >>> print([lines.lines[i].wavelength for i in range(5)])
        [687.472, 706.2, 760.3, 763.1, 777.6]

        Hydrogen lines at order 1 and 2:
        >>> lines = Lines(HYDROGEN_LINES, redshift=0, atmospheric_lines=True, hydrogen_only=False, emission_spectrum=True, orders=[1, 2])
        >>> print([lines.lines[i].wavelength for i in range(len(lines.lines))])
        [397.0, 410.2, 434.0, 486.3, 656.3, 794.0, 820.4, 868.0, 972.6, 1312.6]

        Negative redshift:

        >>> lines = Lines(HYDROGEN_LINES, redshift=-0.5)

        """
        self.my_logger = set_logger(self.__class__.__name__)
        if redshift < -1e-2:
            self.my_logger.error(
                f'\n\tRedshift must small in absolute value (|z|<0.01) or be positive or null. '
                f'Got redshift={redshift}.')
        self.lines = []
        self.orders = orders
        for order in orders:
            for line in lines:
                tmp_line = copy.deepcopy(line)
                tmp_line.wavelength *= order
                if order > 1:
                    if line.label[-1] == "$":
                        tmp_line.label = tmp_line.label[:-1]
                    tmp_line.label += "^(2)"
                    if line.label[-1] == "$":
                        tmp_line.label += "$"
                self.lines.append(tmp_line)
        self.redshift = redshift
        self.atmospheric_lines = atmospheric_lines
        self.hydrogen_only = hydrogen_only
        self.emission_spectrum = emission_spectrum
        self.lines = self.sort_lines()
Ejemplo n.º 27
0
    def __init__(self, psf, data, data_errors, bgd_model_func=None, file_name="",
                 nwalkers=18, nsteps=1000, burnin=100, nbins=10,
                 verbose=0, plot=False, live_fit=False, truth=None):
        """

        Parameters
        ----------
        psf
        data: array_like
            The data array (background subtracted) of dimension 1 or 2.
        data_errors
        bgd_model_func
        file_name
        nwalkers
        nsteps
        burnin
        nbins
        verbose
        plot
        live_fit
        truth

        Examples
        --------

        Build a mock spectrogram with random Poisson noise:

        >>> p = np.array([100, 50, 50, 3, 2, -0.1, 2, 200])
        >>> psf = MoffatGauss(p)
        >>> data = psf.evaluate(p)
        >>> data_errors = np.sqrt(data+1)

        Fit the data:

        >>> w = PSFFitWorkspace(psf, data, data_errors, bgd_model_func=None, verbose=True)

        """
        FitWorkspace.__init__(self, file_name, nwalkers, nsteps, burnin, nbins, verbose=verbose, plot=plot,
                              live_fit=live_fit, truth=truth)
        self.my_logger = set_logger(self.__class__.__name__)
        if data.shape != data_errors.shape:
            raise ValueError(f"Data and uncertainty arrays must have the same shapes. "
                             f"Here data.shape={data.shape} and data_errors.shape={data_errors.shape}.")
        self.psf = psf
        self.data = data
        self.err = data_errors
        self.bgd_model_func = bgd_model_func
        self.p = np.copy(self.psf.p)  # [1:])
        self.guess = np.copy(self.psf.p)
        self.saturation = self.psf.p[-1]
        self.fixed = [False] * len(self.p)
        self.fixed[-1] = True  # fix saturation parameter
        self.input_labels = list(np.copy(self.psf.param_names))  # [1:]))
        self.axis_names = list(np.copy(self.psf.axis_names))  # [1:]))
        self.bounds = self.psf.bounds  # [1:]
        self.nwalkers = max(2 * self.ndim, nwalkers)

        # prepare the fit
        if data.ndim == 2:
            self.Ny, self.Nx = self.data.shape
            self.psf.apply_max_width_to_bounds(self.Ny)
            yy, xx = np.mgrid[:self.Ny, :self.Nx]
            self.pixels = np.asarray([xx, yy])
        elif data.ndim == 1:
            self.Ny = self.data.size
            self.Nx = 1
            self.psf.apply_max_width_to_bounds(self.Ny)
            self.pixels = np.arange(self.Ny)
            self.fixed[1] = True
        else:
            raise ValueError(f"Data array must have dimension 1 or 2. Here pixels.ndim={data.ndim}.")

        # update bounds
        self.bounds = self.psf.bounds  # [1:]
        total_flux = np.sum(data)
        self.bounds[0] = (0.1 * total_flux, 2 * total_flux)

        # error matrix
        # here image uncertainties are assumed to be uncorrelated
        # (which is not exactly true in rotated images)
        self.W = 1. / (self.err * self.err)
        self.W = self.W.flatten()  # np.diag(self.W.flatten())
        self.W_dot_data = self.W * self.data.flatten()
Ejemplo n.º 28
0
    def __init__(self,
                 wavelength,
                 label,
                 atmospheric=False,
                 emission=False,
                 label_pos=[0.007, 0.02],
                 width_bounds=[0.5, 6],
                 use_for_calibration=False):
        """Class modeling the emission or absorption lines. lines attributes contains main spectral lines
        sorted in wavelength.

        Parameters
        ----------
        wavelength: float
            Wavelength of the spectral line in nm
        label: str

        atmospheric: bool
            Set True if the spectral line is atmospheric (default: False)
        emission: bool
            Set True if the spectral line has to be detected in emission. Can't be true if the line is atmospheric.
            (default: False)
        label_pos: [float, float]
            Position of the label in the plot with respect to the vertical lin (default: [0.007,0.02])
        width_bounds: [float, float]
            Minimum and maximum width (in nm) of the line for fitting procedures (default: [1,7])
        use_for_calibration: bool
            Use this line for the dispersion relation calibration, bright line recommended (default: False)

        Examples
        --------
        >>> l = Line(550, label='test', atmospheric=True, emission=True)
        >>> print(l.wavelength)
        550
        >>> print(l.label)
        test
        >>> print(l.atmospheric)
        True
        >>> print(l.emission)
        False
        """
        self.my_logger = set_logger(self.__class__.__name__)
        self.wavelength = wavelength  # in nm
        self.label = label
        self.label_pos = label_pos
        self.atmospheric = atmospheric
        self.emission = emission
        if self.atmospheric:
            self.emission = False
        self.width_bounds = width_bounds
        self.fitted = False
        self.use_for_calibration = use_for_calibration
        self.high_snr = False
        self.fit_lambdas = None
        self.fit_gauss = None
        self.fit_bgd = None
        self.fit_snr = None
        self.fit_fwhm = None
        self.fit_popt = None
        self.fit_pcov = None
        self.fit_popt_gaussian = None
        self.fit_pcov_gaussian = None
        self.fit_chisq = None
        self.fit_eqwidth_mod = None
        self.fit_eqwidth_data = None
        self.fit_bgd_npar = parameters.CALIB_BGD_NPARAMS