Пример #1
0
 def evaluate(x, tau, x_0, fwhm):
     if tau == 0.0:
         return np.full((len(x)), 0.0)
     else:
         profile = Drude1D(amplitude=1.0, fwhm=fwhm, x_0=x_0)
         tau_x = tau * profile(x)
         return (1.0 - np.exp(-1.0 * tau_x)) / tau_x
Пример #2
0
    def kvt(in_x):
        """
        Output the kvt extinction curve
        """
        # fmt: off
        kvt_wav = np.array([
            8.0, 8.2, 8.4, 8.6, 8.8, 9.0, 9.2, 9.4, 9.6, 9.7, 9.75, 9.8, 10.0,
            10.2, 10.4, 10.6, 10.8, 11.0, 11.2, 11.4, 11.6, 11.8, 12.0, 12.2,
            12.4, 12.6, 12.7
        ])

        kvt_int = np.array([
            .06, .09, .16, .275, .415, .575, .755, .895, .98, .99, 1.0, .99,
            .94, .83, .745, .655, .58, .525, .43, .35, .27, .20, .13, .09, .06,
            .045, .04314
        ])
        # fmt: on

        # Extend kvt profile to shorter wavelengths
        if min(in_x) < min(kvt_wav):
            kvt_wav_short = in_x[in_x < min(kvt_wav)]
            kvt_int_short_tmp = min(kvt_int) * np.exp(
                2.03 * (kvt_wav_short - min(kvt_wav)))
            # Since kvt_int_shoft_tmp does not reach min(kvt_int),
            # we scale it to stitch it.
            kvt_int_short = kvt_int_short_tmp * (kvt_int[0] /
                                                 max(kvt_int_short_tmp))

            spline_x = np.concatenate([kvt_wav_short, kvt_wav])
            spline_y = np.concatenate([kvt_int_short, kvt_int])
        else:
            spline_x = kvt_wav
            spline_y = kvt_int

        intfunc = interpolate.interp1d(spline_x, spline_y)
        in_x_spline = in_x[in_x < max(kvt_wav)]
        new_spline_y = intfunc(in_x_spline)

        nf = Drude1D(amplitude=0.4, x_0=18.0, fwhm=0.247 * 18.0)
        in_x_drude = in_x[in_x >= max(kvt_wav)]

        ext = np.concatenate([new_spline_y, nf(in_x_drude)])

        # Extend to ~2 um
        # assuming beta is 0.1
        beta = 0.1
        y = (1.0 - beta) * ext + beta * (9.7 / in_x)**1.7

        return y
Пример #3
0
    def __init__(
        self,
        obs_x,
        obs_y,
        estimate_start=False,
        param_info=None,
        filename=None,
        tformat=None,
    ):
        """
        Setup a variant based on inputs.  Generates an astropy.modeling
        compound model.
        """
        # check that param_info or filename is set
        if filename is None and param_info is None:
            raise ValueError("Either param_info or filename need to be set \
                             when initializing a PAHFITBase object")

        # read in the parameter info from a file
        if filename is not None:
            param_info = self.read(filename, tformat=tformat)

        if estimate_start:
            # guess values and update starting point (if not set fixed) based on the input spectrum
            param_info = self.estimate_init(obs_x, obs_y, param_info)

        if not param_info:
            raise ValueError("No parameter information set.")

        self.param_info = param_info

        bb_info = param_info[0]
        dust_features = param_info[1]
        h2_features = param_info[2]
        ion_features = param_info[3]
        att_info = param_info[4]

        # setup the model
        self.model = None
        self.bb_info = bb_info
        if bb_info is not None:
            bbs = []
            for k in range(len(bb_info["names"])):
                BBClass = ModifiedBlackBody1D if bb_info["modified"][
                    k] else BlackBody1D
                bbs.append(
                    BBClass(
                        name=bb_info["names"][k],
                        temperature=bb_info["temps"][k],
                        amplitude=bb_info["amps"][k],
                        bounds={
                            "temperature": bb_info["temps_limits"][k],
                            "amplitude": bb_info["amps_limits"][k],
                        },
                        fixed={
                            "temperature": bb_info["temps_fixed"][k],
                            "amplitude": bb_info["amps_fixed"][k],
                        },
                    ))
            self.model = sum(bbs[1:], bbs[0])

        self.dust_features = dust_features
        if dust_features is not None:
            df = []
            for k in range(len(dust_features["names"])):
                df.append(
                    Drude1D(
                        name=dust_features["names"][k],
                        amplitude=dust_features["amps"][k],
                        x_0=dust_features["x_0"][k],
                        fwhm=dust_features["fwhms"][k],
                        bounds={
                            "amplitude": dust_features["amps_limits"][k],
                            "x_0": dust_features["x_0_limits"][k],
                            "fwhm": dust_features["fwhms_limits"][k],
                        },
                        fixed={
                            "amplitude": dust_features["amps_fixed"][k],
                            "x_0": dust_features["x_0_fixed"][k],
                            "fwhm": dust_features["fwhms_fixed"][k],
                        },
                    ))

            df = sum(df[1:], df[0])
            if self.model:
                self.model += df
            else:
                self.model = df

        self.h2_features = h2_features
        if h2_features is not None:
            h2 = []
            for k in range(len(h2_features["names"])):
                h2.append(
                    Gaussian1D(
                        name=h2_features["names"][k],
                        amplitude=h2_features["amps"][k],
                        mean=h2_features["x_0"][k],
                        stddev=h2_features["fwhms"][k] / 2.355,
                        bounds={
                            "amplitude":
                            h2_features["amps_limits"][k],
                            "mean":
                            h2_features["x_0_limits"][k],
                            "stddev": (
                                h2_features["fwhms"][k] * 0.9 / 2.355,
                                h2_features["fwhms"][k] * 1.1 / 2.355,
                            ),
                        },
                        fixed={
                            "amplitude": h2_features["amps_fixed"][k],
                            "mean": h2_features["x_0_fixed"][k],
                            "stddev": h2_features["fwhms_fixed"][k],
                        },
                    ))
            h2 = sum(h2[1:], h2[0])
            if self.model:
                self.model += h2
            else:
                self.model = h2

        self.ion_features = ion_features
        if ion_features is not None:
            ions = []
            for k in range(len(ion_features["names"])):
                ions.append(
                    Gaussian1D(
                        name=ion_features["names"][k],
                        amplitude=ion_features["amps"][k],
                        mean=ion_features["x_0"][k],
                        stddev=ion_features["fwhms"][k] / 2.355,
                        bounds={
                            "amplitude":
                            ion_features["amps_limits"][k],
                            "mean":
                            ion_features["x_0_limits"][k],
                            "stddev": (
                                ion_features["fwhms"][k] * 0.9 / 2.355,
                                ion_features["fwhms"][k] * 1.1 / 2.355,
                            ),
                        },
                        fixed={
                            "amplitude": ion_features["amps_fixed"][k],
                            "mean": ion_features["x_0_fixed"][k],
                            "stddev": ion_features["fwhms_fixed"][k],
                        },
                    ))
            ions = sum(ions[1:], ions[0])
            if self.model:
                self.model += ions
            else:
                self.model = ions

        # add additional att components to the model if necessary
        if not self.model:
            raise ValueError("No model components found")

        self.att_info = att_info
        if att_info is not None:
            for k in range(len(att_info["names"])):
                if (
                        att_info["names"][k] == "S07_att"
                ):  # Only loop through att components that can be parameterized
                    self.model *= S07_attenuation(
                        name=att_info["names"][k],
                        tau_sil=att_info["amps"][k],
                        bounds={"tau_sil": att_info["amps_limits"][k]},
                        fixed={"tau_sil": att_info["amps_fixed"][k]},
                    )
                else:
                    self.model *= att_Drude1D(
                        name=att_info["names"][k],
                        tau=att_info["amps"][k],
                        x_0=att_info["x_0"][k],
                        fwhm=att_info["fwhms"][k],
                        bounds={
                            "tau": att_info["amps_limits"][k],
                            "fwhm": att_info["fwhms_limits"][k],
                        },
                        fixed={"x_0": att_info["x_0_fixed"][k]},
                    )
Пример #4
0
    def __init__(self, param_info=None, filename=None, tformat=None):
        """
        Setup a variant based on inputs.  Generates an astropy.modeling
        compound model.
        """
        # check that param_info or filename is set
        if filename is None and param_info is None:
            raise ValueError(
                "Either param_info or filename need to be set \
                             when initializing a PAHFITBase object"
            )

        # read in the parameter info from a file
        if filename is not None:
            param_info = self.read(filename, tformat=tformat)

        bb_info = param_info[0]
        dust_features = param_info[1]
        h2_features = param_info[2]
        ion_features = param_info[3]
        att_info = param_info[4]

        # setup the model
        self.bb_info = bb_info
        if bb_info is not None:
            # 1st component defines the overall model variable
            self.model = BlackBody1D(
                name=bb_info["names"][0],
                temperature=bb_info["temps"][0],
                amplitude=bb_info["amps"][0],
                bounds={
                    "temperature": bb_info["temps_limits"][0],
                    "amplitude": bb_info["amps_limits"][0],
                },
                fixed={
                    "temperature": bb_info["temps_fixed"][0],
                    "amplitude": bb_info["amps_fixed"][0],
                },
            )
            for k in range(1, len(bb_info["names"])):
                self.model += BlackBody1D(
                    name=bb_info["names"][k],
                    temperature=bb_info["temps"][k],
                    amplitude=bb_info["amps"][k],
                    bounds={
                        "temperature": bb_info["temps_limits"][k],
                        "amplitude": bb_info["amps_limits"][k],
                    },
                    fixed={
                        "temperature": bb_info["temps_fixed"][k],
                        "amplitude": bb_info["amps_fixed"][k],
                    },
                )

        self.dust_features = dust_features
        if dust_features is not None:
            for k in range(len(dust_features["names"])):
                self.model += Drude1D(
                    name=dust_features["names"][k],
                    amplitude=dust_features["amps"][k],
                    x_0=dust_features["x_0"][k],
                    fwhm=dust_features["fwhms"][k],
                    bounds={
                        "amplitude": dust_features["amps_limits"][k],
                        "x_0": dust_features["x_0_limits"][k],
                        "fwhm": dust_features["fwhms_limits"][k],
                    },
                    fixed={
                        "amplitude": dust_features["amps_fixed"][k],
                        "x_0": dust_features["x_0_fixed"][k],
                        "fwhm": dust_features["fwhms_fixed"][k],
                    },
                )

        self.h2_features = h2_features
        if h2_features is not None:
            for k in range(len(h2_features["names"])):
                self.model += Gaussian1D(
                    name=h2_features["names"][k],
                    amplitude=h2_features["amps"][k],
                    mean=h2_features["x_0"][k],
                    stddev=h2_features["fwhms"][k] / 2.355,
                    bounds={
                        "amplitude": h2_features["amps_limits"][k],
                        "mean": h2_features["x_0_limits"][k],
                        "stddev": (
                            h2_features["fwhms_limits"][k][0] / 2.355,
                            h2_features["fwhms_limits"][k][1] / 2.355,
                        ),
                    },
                    fixed={
                        "amplitude": h2_features["amps_fixed"][k],
                        "mean": h2_features["x_0_fixed"][k],
                        "stddev": h2_features["fwhms_fixed"][k],
                    },
                )

        self.ion_features = ion_features
        if ion_features is not None:
            for k in range(len(ion_features["names"])):
                self.model += Gaussian1D(
                    name=ion_features["names"][k],
                    amplitude=ion_features["amps"][k],
                    mean=ion_features["x_0"][k],
                    stddev=ion_features["fwhms"][k] / 2.355,
                    bounds={
                        "amplitude": ion_features["amps_limits"][k],
                        "mean": ion_features["x_0_limits"][k],
                        "stddev": (
                            ion_features["fwhms_limits"][k][0] / 2.355,
                            ion_features["fwhms_limits"][k][1] / 2.355,
                        ),
                    },
                    fixed={
                        "amplitude": ion_features["amps_fixed"][k],
                        "mean": ion_features["x_0_fixed"][k],
                        "stddev": ion_features["fwhms_fixed"][k],
                    },
                )

        # apply the attenuation to *all* the components
        self.model *= S07_attenuation(
            name=att_info["names"][0],
            tau_sil=att_info["amps"][0],
            bounds={"tau_sil": att_info["amps_limits"][0]},
            fixed={"tau_sil": att_info["amps_fixed"][0]},
        )
Пример #5
0
def eqws(comp_type, x_0, amp, fwhm_stddev, obs_fit):
    """
    Calculate the emission features equivalent width
    (integral[(I_nu-I_cont)/I_cont d_lam]) in microns.

    Parameters
    ----------
    comp_type : string
        type of emission component (Drude1D/Gaussian)
    x_0 : float
        central wavelength of the feature.
    amp : float
        central intensity of the feature.
    fwhm_stddev : float
        fwhm or stddev of the feature depending on comp_type.
    Returns
    -------
    eqw : float
        the equivalent width of the feature
    """
    # Check if the emission component is Gaussian and calculate fwhm.
    if comp_type == 'Gaussian1D':
        fwhm = 2 * fwhm_stddev * np.sqrt(2 * np.log(2))
    else:
        fwhm = fwhm_stddev

    # Get range and wavelength region for integration.
    low = x_0 - (fwhm * 6)
    lmin = low if low > 0 else 0.
    lmax = x_0 + (fwhm * 6)
    # lam = np.arange(100) / 99 * (lmax - lmin) + lmin
    lam = np.linspace(lmin, lmax, num=100)

    # Calculate the continuum and feature components in the integration range.
    cont_components = []
    for cmodel in obs_fit:
        if isinstance(cmodel, BlackBody1D):
            cont_components.append(cmodel)
    cont_model = cont_components[0]
    for cmodel in cont_components[1:]:
        cont_model += cmodel
    continuum = np.nan_to_num(cont_model(lam))

    if comp_type == 'Drude1D':
        drude = Drude1D(amplitude=amp,
                        x_0=x_0,
                        fwhm=fwhm)
        lnu = drude(lam)

    elif comp_type == 'Gaussian1D':
        gauss = Gaussian1D(amplitude=amp,
                           mean=x_0,
                           stddev=fwhm_stddev)
        lnu = gauss(lam)

    # Following the EQW calculation in IDL PAHFIT, we define a default broad limit (bl).
    # Set bl to replace the continuum in the EQW calculation
    # by its profile-averaged value for fractional FWHM greater than that limit.
    # Useful when the EQW requires extrapolation to regions where the continuum
    # can vanish, such that f_line/f_continuum diverges.
    # Default value bl = 0.05.
    bl = 0.05

    # Calculate EQW.
    if fwhm / x_0 > bl:
        ilam = integrate.simpson(lnu, lam)
        weighted_cont = integrate.simpson(lnu * continuum, lam) / ilam
        eqw = ilam / weighted_cont
    else:
        eqw = integrate.simpson(lnu / continuum, lam)

    return eqw