def estimate_init(obs_x, obs_y, param_info): """ Estimate and return updated starting point in param_info based on the input spectrum. Parameters ---------- obs_x and obs_y: np.array input spectrum param_info: tuple of dics The dictonaries contain info for each type of component. """ # simple linear interpolation function for spectrum sp = interpolate.interp1d(obs_x, obs_y) # guess starting point of bb for i, (fix, temp) in enumerate( zip(param_info[0]["amps_fixed"], param_info[0]["temps"])): if (fix is False) & ( temp >= 2500 ): # stellar comoponent is defined by BB that has T>=2500 K bb = BlackBody1D(1, temp) if min(obs_x) < 5: lam = min(obs_x) + 0.1 # the wavelength used to compare else: # if min(obs_x) > 5, use 5.5 um lam = 5.5 amp_guess = sp(lam) / bb(lam) param_info[0]["amps"][i] = amp_guess elif fix is False: fmax_lam = 2898.0 / temp bb = BlackBody1D(1, temp) if (fmax_lam >= min(obs_x)) & (fmax_lam <= max(obs_x)): lam = fmax_lam amp_guess = sp(lam) / bb(lam) * 0.2 elif fmax_lam > max(obs_x): lam = max(obs_x) amp_guess = obs_y[np.argmax(obs_x)] / bb(lam) * 0.2 else: lam = min(obs_x) amp_guess = obs_y[np.argmin(obs_x)] / bb(lam) * 0.2 param_info[0]["amps"][i] = amp_guess else: pass # guess starting point of dust features and lines # set to half of the median (non-negative) intensity of the entire input spectrum # dust (1), h2 (2), and ion (3) for j in range(1, 4): if param_info[j] is not None: for i, fix in enumerate(param_info[j]["amps_fixed"]): if fix is False: amp_guess = 0.5 * np.median(obs_y) param_info[j]["amps"][i] = amp_guess return param_info
def __init__(self, bb_info, dust_features, h2_features, ion_features): """ Setup a variant based on inputs. Generates an astropy.modeling compound model. Notes ----- Would be great to rename the parameters such that they uniquely identify the component (dust, gas, specific line, etc.). This is possible - say a discussion on the stsci slack channel - James Davies? """ model_comps = [] self.bb_info = bb_info if bb_info is not None: amps = bb_info['amps'] temps = bb_info['temps'] amps_limits = bb_info['amps_limits'] n_bb = len(amps) cont_model = BlackBody1D(temperature=temps[0], amplitude=amps[0], fixed={'temperature': True}) cont_model.amplitude.bounds = amps_limits[0] for k in range(1, n_bb): new_model = BlackBody1D(temperature=temps[k], amplitude=amps[k], fixed={'temperature': True}) new_model.amplitude.bounds = amps_limits[k] cont_model = cont_model + new_model self.cont_model = cont_model model_comps.append(cont_model) # dust features should be a Drude profile self.dust_features = dust_features if dust_features is not None: amps = dust_features['amps'] x_0 = dust_features['x_0'] fwhms = dust_features['fwhms'] amps_limits = dust_features['amps_limits'] x_0_limits = dust_features['x_0_limits'] fwhms_limits = dust_features['fwhms_limits'] n_df = len(amps) df_model = Drude1D(amplitude=amps[0], x_0=x_0[0], fwhm=fwhms[0], bounds={ 'amplitude': amps_limits[0], 'x_0': x_0_limits[0], 'fwhm': fwhms_limits[0] }) for k in range(1, n_df): df_model = df_model + Drude1D(amplitude=amps[k], x_0=x_0[k], fwhm=fwhms[k], bounds={ 'amplitude': amps_limits[k], 'x_0': x_0_limits[k], 'fwhm': fwhms_limits[k] }) self.df_model = df_model model_comps.append(df_model) self.h2_features = h2_features if h2_features is not None: amps = h2_features['amps'] x_0 = h2_features['x_0'] fwhms = h2_features['fwhms'] amps_limits = h2_features['amps_limits'] x_0_limits = h2_features['x_0_limits'] fwhms_limits = h2_features['fwhms_limits'] names = h2_features['names'] n_h2 = len(amps) h2_model = Gaussian1D(name=names[0], amplitude=amps[0], mean=x_0[0], stddev=fwhms[0] / 2.355, bounds={ 'amplitude': amps_limits[0], 'x_0': x_0_limits[0], 'stddev': (fwhms_limits[0][0] / 2.355, fwhms_limits[0][1] / 2.355) }) for k in range(1, n_h2): h2_model = h2_model + Gaussian1D( name=names[k], amplitude=amps[k], mean=x_0[k], stddev=fwhms[k] / 2.355, bounds={ 'amplitude': amps_limits[k], 'x_0': x_0_limits[k], 'stddev': (fwhms_limits[k][0] / 2.355, fwhms_limits[k][1] / 2.355) }) self.h2_model = h2_model model_comps.append(h2_model) self.ion_features = ion_features if ion_features is not None: amps = ion_features['amps'] x_0 = ion_features['x_0'] fwhms = ion_features['fwhms'] amps_limits = ion_features['amps_limits'] x_0_limits = ion_features['x_0_limits'] fwhms_limits = ion_features['fwhms_limits'] names = ion_features['names'] n_ion = len(amps) ion_model = Gaussian1D(name=names[0], amplitude=amps[0], mean=x_0[0], stddev=fwhms[0] / 2.355, bounds={ 'amplitude': amps_limits[0], 'x_0': x_0_limits[0], 'stddev': (fwhms_limits[0][0] / 2.355, fwhms_limits[0][1] / 2.355) }) for k in range(1, n_ion): ion_model = ion_model + Gaussian1D( name=names[k], amplitude=amps[k], mean=x_0[k], stddev=fwhms[k] / 2.355, bounds={ 'amplitude': amps_limits[k], 'x_0': x_0_limits[k], 'stddev': (fwhms_limits[k][0] / 2.355, fwhms_limits[k][1] / 2.355) }) self.ion_model = ion_model model_comps.append(ion_model) self.model = model_comps[0] for cmodel in model_comps[1:]: self.model += cmodel # need to make the type of attenuation model a passed variable self.model *= S07_attenuation()
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], 'stddev': 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]})
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) 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]}, )