예제 #1
0
    def __init__(self, path, air=True, wl_range=(150, 300000 - 50)):
        super().__init__(
            name="CIFIST",
            points=[
                np.concatenate(
                    (np.arange(1200, 2351, 50), np.arange(2400, 7001, 100)),
                    axis=0),
                np.arange(2.5, 5.6, 0.5),
            ],
            param_names=["T", "logg"],
            wave_units="AA",
            flux_units="",
            air=air,
            wl_range=wl_range,
            path=path,
        )

        self.par_dicts = [None, None]
        self.rname = "lte{0:0>5.1f}-{1:.1f}-0.0a+0.0.BT-Settl.spec.fits.gz"
        self.full_rname = os.path.join(self.path, self.rname)

        wl_dict = create_log_lam_grid(dv=0.08,
                                      start=self.wl_range[0],
                                      end=self.wl_range[1])
        self.wl = wl_dict["wl"]
예제 #2
0
 def test_many_fluxes(self, mock_data):
     dv = calculate_dv(mock_data[0])
     new_wave = create_log_lam_grid(dv, mock_data[0].min(),
                                    mock_data[0].max())["wl"]
     flux_stack = np.tile(mock_data[1], (4, 1))
     fluxes = resample(mock_data[0], flux_stack, new_wave)
     assert fluxes.shape == (4, len(new_wave))
예제 #3
0
    def __init__(self, path, air=True, wl_range=(2999, 13000)):
        super().__init__(
            name="BTSettl",
            points={
                "T": np.arange(3000, 7001, 100),
                "logg": np.arange(2.5, 5.6, 0.5),
                "Z": np.arange(-0.5, 0.6, 0.5),
                "alpha": np.array([0.0]),
            },
            wave_units="AA",
            flux_units="",
            air=air,
            wl_range=wl_range,
            path=path,
        )

        # Normalize to 1 solar luminosity?
        self.rname = "CIFIST2011/M{Z:}/lte{T:0>3.0f}-{logg:.1f}{Z:}.BT-Settl.spec.7.bz2"
        self.full_rname = os.path.join(self.path, self.rname)
        # self.Z_dict = {-2:'-2.0', -1.5:'-1.5', -1:'-1.0', -0.5:'-0.5', 0.0: '-0.0', 0.5: '+0.5', 1: '+1.0'}
        self.Z_dict = {-0.5: "-0.5a+0.2", 0.0: "-0.0a+0.0", 0.5: "+0.5a0.0"}

        wl_dict = create_log_lam_grid(0.08 / C.c_kms,
                                      start=self.wl_range[0],
                                      end=self.wl_range[1])
        self.wl = wl_dict["wl"]
예제 #4
0
    def __init__(
        self,
        emulator: Union[str, Emulator],
        data: Union[str, Spectrum],
        grid_params: Sequence[float],
        max_deque_len: int = 100,
        name: str = "SpectrumModel",
        **params,
    ):
        if isinstance(emulator, str):
            emulator = Emulator.load(emulator)
        if isinstance(data, str):
            data = Spectrum.load(data)

        if len(data) > 1:
            raise ValueError(
                "Multiple orders detected in data, please use EchelleModel")

        self.emulator: Emulator = emulator
        self.data_name = data.name
        self.data = data[0]

        dv = calculate_dv(self.data.wave)
        self.min_dv_wave = create_log_lam_grid(dv, self.emulator.wl.min(),
                                               self.emulator.wl.max())["wl"]
        self.bulk_fluxes = resample(self.emulator.wl,
                                    self.emulator.bulk_fluxes,
                                    self.min_dv_wave)

        self.residuals = deque(maxlen=max_deque_len)

        # manually handle cheb coeffs to offset index by 1
        chebs = params.pop("cheb", [])
        cheb_idxs = [str(i) for i in range(1, len(chebs) + 1)]
        params["cheb"] = dict(zip(cheb_idxs, chebs))
        # load rest of params into FlatterDict
        self.params = FlatterDict(params)
        self.frozen = []
        self.name = name

        # Unpack the grid parameters
        self.n_grid_params = len(grid_params)
        self.grid_params = grid_params

        # None means "yet to be calculated", do not use NaN
        self._lnprob = None
        self._glob_cov = None
        self._loc_cov = None

        self.log = logging.getLogger(self.__class__.__name__)

        self.flux_scalar_func = flux_scalar = LinearNDInterpolator(
            self.emulator.grid_points, self.emulator.flux_scalar)
예제 #5
0
def test_grid_dv_regression(dv):
    grid = create_log_lam_grid(dv, 1e4, 4e4)
    dv_ = calculate_dv(grid["wl"])
    dv_d = calculate_dv_dict(grid)
    assert np.isclose(dv_, dv_d)
    assert dv_ <= dv
예제 #6
0
def test_invalid_points_grid(start, end):
    with pytest.raises(ValueError):
        create_log_lam_grid(1000, start, end)
예제 #7
0
def test_grid_keys():
    grid = create_log_lam_grid(1000, 3000, 3e4)
    assert "wl" in grid
    assert "CRVAL1" in grid
    assert "CDELT1" in grid
    assert "NAXIS1" in grid
예제 #8
0
 def test_benchmark(self, benchmark, benchmark_data):
     wave, flux = benchmark_data
     dv = calculate_dv(wave)
     new_wave = create_log_lam_grid(dv, wave.min(), wave.max())["wl"]
     benchmark(resample, wave, flux, new_wave)
예제 #9
0
 def test_resample(self, mock_data):
     dv = calculate_dv(mock_data[0])
     new_wave = create_log_lam_grid(dv, mock_data[0].min(),
                                    mock_data[0].max())["wl"]
     flux = resample(*mock_data, new_wave)
     assert flux.shape == new_wave.shape
예제 #10
0
    def __init__(
        self,
        grid_interface,
        filename,
        instrument=None,
        wl_range=None,
        ranges=None,
        key_name=None,
    ):

        self.log = logging.getLogger(self.__class__.__name__)

        self.grid_interface = grid_interface
        self.filename = os.path.expandvars(filename)
        self.instrument = instrument

        # The flux formatting key will always have alpha in the name, regardless
        # of whether or not the library uses it as a parameter.
        if key_name is None:
            key_name = (
                self.grid_interface.rname.replace("/", "__")
                .replace(".fits", "")
                .replace(".FITS", "")
            )

        self.key_name = key_name

        if ranges is None:
            self.points = self.grid_interface.points
        else:
            # Take only those points of the GridInterface that fall within the ranges specified
            self.points = []
            # We know which subset we want, so use these.
            for i, (low, high) in enumerate(ranges):
                valid_points = self.grid_interface.points[i]
                ind = (valid_points >= low) & (valid_points <= high)
                self.points.append(valid_points[ind])
                # Note that at this point, this is just the grid points that fall within the rectangular
                # bounds set by ranges. If the raw library is actually irregular (e.g. CIFIST),
                # then self.points will contain points that don't actually exist in the raw library.

        # the raw wl from the spectral library
        self.wl_native = self.grid_interface.wl  # raw grid
        self.dv_native = calculate_dv(self.wl_native)

        self.hdf5 = h5py.File(self.filename, "w")
        self.hdf5.attrs["grid_name"] = grid_interface.name
        self.flux_group = self.hdf5.create_group("flux")
        self.flux_group.attrs["units"] = grid_interface.flux_units
        self.flux_group.attrs["key_name"] = self.key_name

        # We'll need a few wavelength grids
        # 1. The original synthetic grid: ``self.wl_native``
        # 2. A finely spaced log-lambda grid respecting the ``dv`` of
        #   ``self.wl_native``, onto which we can interpolate the flux values
        #   in preperation of the FFT: ``self.wl_loglam``
        # [ DO FFT ]
        # 3. A log-lambda spaced grid onto which we can downsample the result
        #   of the FFT, spaced with a ``dv`` such that we respect the remaining
        #   Fourier modes: ``self.wl_final``

        # There are three ranges to consider when wanting to make a grid:
        # 1. The full range of the synthetic library
        # 2. The full range of the instrument/dataset
        # 3. The range specified by the user in config.yaml
        # For speed reasons, we will always truncate to to wl_range. If either
        # the synthetic library or the instrument library is smaller than this range,
        # raise an error.
        if wl_range is None:
            wl_min, wl_max = 0, np.inf
        else:
            wl_min, wl_max = wl_range
        buffer = 50  # [AA]
        wl_min -= buffer
        wl_max += buffer

        # If the raw synthetic grid doesn't span the full range of the user
        # specified grid, raise an error.
        # Instead, let's choose the maximum limit of the synthetic grid?
        if self.instrument is not None:
            inst_min, inst_max = self.instrument.wl_range
        else:
            inst_min, inst_max = 0, np.inf
        imposed_min = np.max([self.wl_native[0], inst_min])
        imposed_max = np.min([self.wl_native[-1], inst_max])
        if wl_min < imposed_min:
            self.log.info(
                "Given minimum wavelength ({}) is less than instrument or grid minimum. Truncating to {}".format(
                    wl_min, imposed_min
                )
            )
            wl_min = imposed_min
        if wl_max > imposed_max:
            self.log.info(
                "Given maximum wavelength ({}) is greater than instrument or grid maximum. Truncating to {}".format(
                    wl_max, imposed_max
                )
            )
            wl_max = imposed_max

        if wl_max < wl_min:
            raise ValueError("Minimum wavelength must be less than maximum wavelength")

        # Calculate wl_loglam
        # use the dv that preserves the native quality of the raw PHOENIX grid
        wl_dict = create_log_lam_grid(self.dv_native, wl_min, wl_max)
        wl_loglam = wl_dict["wl"] # - wl_FFT
        dv_loglam = calculate_dv_dict(wl_dict) # - dv_FFT

        self.log.info(
            "FFT grid stretches from {} to {}".format(wl_loglam[0], wl_loglam[-1])
        )
        self.log.info("wl_loglam dv is {} km/s".format(dv_loglam))

        if self.instrument is None:
            mask = (self.wl_native > wl_min) & (self.wl_native < wl_max)
            self.wl_final = self.wl_native[mask]
            self.dv_final = self.dv_native

            def inst_broaden(w, f):
                return (w, f)

        else:

            # The final wavelength grid, onto which we will interpolate the
            # Fourier filtered wavelengths, is part of the instrument object
            dv_temp = self.instrument.FWHM / self.instrument.oversampling
            wl_dict = create_log_lam_grid(dv_temp, wl_min, wl_max)
            self.wl_final = wl_dict["wl"] 
            self.dv_final = calculate_dv_dict(wl_dict)

            if (self.instrument is None) or ('SPEX_PRISM' not in self.instrument.name):
                def inst_broaden(w, f):
                    return (w, instrumental_broaden(w, f, self.instrument.FWHM))
            else:
                def inst_broaden(w, f):
                    return (self.wl_final, instrumental_broaden(wave=w, flux=f, fwhm=self.instrument.FWHM, res_gradient=
                            self.instrument.res_gradient, res_wl_step=8, wave_final=self.wl_final))

        def resample_loglam(w, f):
            return (wl_loglam, resample(w, f, wl_loglam)) # - fl

        def resample_final(w, f):
            return (self.wl_final, resample(w, f, self.wl_final))

        if 'SPEX_PRISM' not in self.instrument.name:
            self.transform = lambda flux: resample_final(
                *inst_broaden(*resample_loglam(self.wl_native, flux))
            )
        else:
            self.transform = lambda flux: inst_broaden(
                *resample_loglam(self.wl_native, flux)
            )

        # Create the wl dataset separately using float64 due to rounding errors w/ interpolation.
        wl_dset = self.hdf5.create_dataset("wl", data=self.wl_final, compression=9)
        wl_dset.attrs["air"] = self.grid_interface.air
        wl_dset.attrs["dv"] = self.dv_final
        wl_dset.attrs["units"] = self.grid_interface.wave_units
예제 #11
0
def mock_data(mock_hdf5_interface):
    wave = mock_hdf5_interface.wl
    new_wave = create_log_lam_grid(1e3, wave.min(), wave.max())["wl"]
    flux = resample(wave, next(mock_hdf5_interface.fluxes), new_wave)
    yield new_wave, flux