def contExtincParams(self, wave, Rv, reddening_law): self.rcCont = pn.RedCorr(R_V=Rv, law=reddening_law) lineXx = self.rcGas.X(wave) return lineXx
def gasExtincParams(self, wave, Rv, reddening_law, Hbeta_wave=4861.331): self.rcGas = pn.RedCorr(R_V=Rv, law=reddening_law) HbetaXx = self.rcGas.X(Hbeta_wave) lineXx = self.rcGas.X(wave) lineFlambda = lineXx / HbetaXx - 1.0 return lineFlambda
def gasExtincParams(self, wave, R_v = None, red_curve = None, normWave = 4861.331): if R_v is None: R_v = self.R_v if red_curve is None: red_curve = self.red_curve self.rcGas = pn.RedCorr(R_V=R_v, law=red_curve) HbetaXx = self.rcGas.X(normWave) lineXx = self.rcGas.X(wave) lineFlambda = lineXx / HbetaXx - 1.0 return lineFlambda
def __init__(self, name): """ Creating an object to manage the Cloudy output and compare to the observations """ self.model = pc.CloudyModel(name) self.set_3D(use=False) self.m3d = None self.mask = None self.profile_defined = False self.dim_3D = 101 self.mask_ap_center = (0., 3.5) self.mask_ap_size = (12., 1) # define a RedCorr object self.RC = pn.RedCorr(E_BV=0.26, R_V=3.6, law='Fitz 99')
def read_obs(self): self.obs_txt = np.genfromtxt(emis_file, dtype=["a11", "float", "float"], delimiter=[11, 8, 6], names=['label', 'i_obs', 'e_obs'], usecols=(0, 1, 2)) # redenning correction Hb = self.obs_txt['i_obs'][self.obs_txt['label'] == 'H 1 4861A'] Ha = self.obs_txt['i_obs'][self.obs_txt['label'] == 'H 1 6563A'] self.RC = pn.RedCorr(law='Fitz 99') self.RC.setCorr(Ha / Hb / 2.85, 6563, 4861) for line in self.obs_txt: lambda_ = np.float(line['label'][-5:-1]) line['i_obs'] *= self.RC.getCorrHb(lambda_)
def get_k_values(wave, law='CCM89', silent=True, verbose=False): ''' Function to get k(lambda): A(lambda) = k(lambda) * E(B-V) Parameters ---------- wave : float or array like Wavelength in units of Angstroms law : string String for dust attenuation "law". Default: "CCM89". Full list available from RC.getLaws() Options are: 'G03 LMC', 'K76', 'F99-like', 'F88 F99 LMC', 'No correction', 'SM79 Gal', 'MCC99 FM90 LMC', 'CCM89 Bal07', 'CCM89 oD94', 'S79 H83 CCM89', 'F99', 'CCM89' - See pn.RedCorr.printLaws() for more details silent : boolean Turns off stdout messages. Default: True verbose : boolean Turns on additional stdout messages. Default: False Returns ------- k value at wave Notes ----- Created by Chun Ly, 22 November 2016 ''' if silent == False: print '### Begin balmer_decrement.get_k_values() | '+systime() RC = pn.RedCorr(E_BV = 1.0) RC.law = law if silent == False: print '### End balmer_decrement.get_k_values() | '+systime() return np.log10(RC.getCorr(wave)) / 0.4
def make_obs_tab(): lines = np.genfromtxt('lines.dat', delimiter='&', dtype=None, names='ID, lam1, lam2, f, t') RC = pn.RedCorr(cHbeta=0.4, R_V=3.1, law='CCM89') with open('new_lines.dat', 'w') as f: for l in lines: towrite = '{} & {:.2f} & {:.2f} & {:5.3f} & {:5.3f} & {}\n'.format( l['ID'], l['lam1'], l['lam2'] * 1.00029, l['f'], l['f'] * RC.getCorrHb(l['lam1']), l['t']) towrite = towrite.replace('b\'', '') towrite = towrite.replace('\\\\', '\\') towrite = towrite.replace('\\t\\t', '') towrite = towrite.replace('\\t(', '(') towrite = towrite.replace('\\t\\', '\\') towrite = towrite.replace('\\t', '**') towrite = towrite.replace('**ex', '\\tex') towrite = towrite.replace('**', '') towrite = towrite.replace('\\\\\\\\', '\\') towrite = towrite.replace('\'', ' ') f.write(towrite)
def cHbeta_from_log(self, line_df, line_labels='all', temp=10000.0, den=100.0, ref_wave='H1_4861A', comp_mode='auto', plot_address=False): # Use all hydrogen lines if none are defined if line_labels == 'all': idcs_H1 = line_df.ion == 'H1' line_labels = line_df.loc[idcs_H1].index.values assert line_labels.size > 0, f'- ERROR: No H1 ion transition lines were found in log. Check dataframe data.' # Loop through the input lines assert ref_wave in line_df.index, f'- ERROR: {ref_wave} not found in input lines log dataframe for c(Hbeta) calculation' # Label the lines which are found in the lines log idcs_lines = line_df.index.isin(line_labels) & (line_df.intg_flux > 0) & (line_df.gauss_flux > 0) line_labels = line_df.loc[idcs_lines].index.values ion_ref, waves_ref, latexLabels_ref = label_decomposition(ref_wave, scalar_output=True) ion_array, waves_array, latexLabels_array = label_decomposition(line_labels) # Observed ratios if comp_mode == 'auto': Href_flux, Href_err = line_df.loc[ref_wave, 'intg_flux'], line_df.loc[ref_wave, 'intg_err'] obsFlux, obsErr = np.empty(line_labels.size), np.empty(line_labels.size) slice_df = line_df.loc[idcs_lines] idcs_intg = slice_df.blended_label == 'None' obsFlux[idcs_intg] = slice_df.loc[idcs_intg, 'intg_flux'].values obsErr[idcs_intg] = slice_df.loc[idcs_intg, 'intg_err'].values obsFlux[~idcs_intg] = slice_df.loc[~idcs_intg, 'gauss_flux'].values obsErr[~idcs_intg] = slice_df.loc[~idcs_intg, 'gauss_err'].values obsRatio_uarray = unumpy.uarray(obsFlux, obsErr) / ufloat(Href_flux, Href_err) # TODO unumpy this with your own model elif comp_mode == 'gauss': Href_flux, Href_err = line_df.loc[ref_wave, 'gauss_flux'], line_df.loc[ref_wave, 'gauss_err'] obsFlux, obsErr = line_df.loc[idcs_lines, 'gauss_flux'], line_df.loc[idcs_lines, 'gauss_err'] obsRatio_uarray = unumpy.uarray(obsFlux, obsErr) / ufloat(Href_flux, Href_err) else: Href_flux, Href_err = line_df.loc[ref_wave, 'intg_flux'], line_df.loc[ref_wave, 'intg_err'] obsFlux, obsErr = line_df.loc[idcs_lines, 'intg_flux'], line_df.loc[idcs_lines, 'intg_err'] obsRatio_uarray = unumpy.uarray(obsFlux, obsErr) / ufloat(Href_flux, Href_err) assert not np.any(np.isnan(obsFlux)) in obsFlux, '- ERROR: nan entry in input fluxes for c(Hbeta) calculation' assert not np.any(np.isnan(obsErr)) in obsErr, '- ERROR: nan entry in input uncertainties for c(Hbeta) calculation' # Theoretical ratios H1 = pn.RecAtom('H', 1) refEmis = H1.getEmissivity(tem=temp, den=den, wave=waves_ref) emisIterable = (H1.getEmissivity(tem=temp, den=den, wave=wave) for wave in waves_array) linesEmis = np.fromiter(emisIterable, float) theoRatios = linesEmis / refEmis # Reddening law rc = pn.RedCorr(R_V=self.R_v, law=self.red_curve) Xx_ref, Xx = rc.X(waves_ref), rc.X(waves_array) f_lines = Xx / Xx_ref - 1 f_ref = Xx_ref / Xx_ref - 1 # cHbeta linear fit values x_values = f_lines - f_ref y_values = np.log10(theoRatios) - unumpy.log10(obsRatio_uarray) # Perform fit lineModel = LinearModel() y_nom, y_std = unumpy.nominal_values(y_values), unumpy.std_devs(y_values) pars = lineModel.make_params(intercept=y_nom.min(), slope=0) output = lineModel.fit(y_nom, pars, x=x_values, weights=1 / np.sqrt(y_std)) cHbeta, cHbeta_err = output.params['slope'].value, output.params['slope'].stderr intercept, intercept_err = output.params['intercept'].value, output.params['intercept'].stderr if plot_address: STANDARD_PLOT = {'figure.figsize': (14, 7), 'axes.titlesize': 12, 'axes.labelsize': 14, 'legend.fontsize': 10, 'xtick.labelsize': 10, 'ytick.labelsize': 10} axes_dict = {'xlabel': r'$f_{\lambda} - f_{H\beta}$', 'ylabel': r'$ \left(\frac{I_{\lambda}}{I_{\H\beta}}\right)_{Theo} - \left(\frac{F_{\lambda}}{F_{\H\beta}}\right)_{Obs}$', 'title': f'Logaritmic extinction coefficient calculation'} rcParams.update(STANDARD_PLOT) fig, ax = plt.subplots(figsize=(8, 4)) fig.subplots_adjust(bottom=-0.7) # Data ratios err_points = ax.errorbar(x_values, y_nom, y_std, fmt='o') # Linear fitting linear_fit = cHbeta * x_values + intercept linear_label = r'$c(H\beta)={:.2f}\pm{:.2f}$'.format(cHbeta, cHbeta_err) ax.plot(x_values, linear_fit, linestyle='--', label=linear_label) ax.update(axes_dict) # Legend ax.legend(loc='best') ax.set_ylim(-0.5, 0.5) # Generate plot plt.tight_layout() if isinstance(plot_address, (str, pathlib.WindowsPath, pathlib.PosixPath)): # crs = mplcursors.cursor(ax, hover=True) # crs.connect("add", lambda sel: sel.annotation.set_text(sel.annotation)) plt.savefig(plot_address, dpi=200, bbox_inches='tight') else: mplcursors.cursor(ax).connect("add", lambda sel: sel.annotation.set_text(latexLabels_array[sel.target.index])) plt.show() return cHbeta, cHbeta_err
idcsLineMask = np.where((flux_voxel >= lowLimit) & (flux_voxel <= highLimit)) wave_masked, flux_masked = wave[idcsLineMask], flux_voxel[idcsLineMask] # Measure halpha flux linesDb = pd.read_excel(linesFile, sheet_name=0, header=0, index_col=0) listLabels = ['H1_4861A', 'H1_6563A'] flux_dict = {} for lineLabel in listLabels: wave_regions = linesDb.loc[lineLabel, 'w1':'w6'].values idcsLinePeak, idcsContinua = lm.define_masks(wave_regions) lm.line_properties(idcsLinePeak, idcsContinua, bootstrap_size=500) lm.gauss_mcfit(idcsLinePeak, idcsContinua, bootstrap_size=500) flux_dict[lineLabel] = lm.intg_flux # Reddening correction rc = pn.RedCorr() rc.law = 'G03 LMC' halpha_norm = flux_dict['H1_6563A']/flux_dict['H1_4861A'] rc.setCorr(obs_over_theo=halpha_norm, wave1=6563., wave2=4861.) corr_spec = rc.getCorr(wave) corr_Halpha = rc.getCorr(6563) voxel_int = flux_voxel * corr_spec * flux_norm voxel_int_masked = voxel_int[idcsLineMask] halpha_flux = flux_dict['H1_6563A'] * corr_Halpha * flux_norm # Compute nebular continuum Te, ne = 10000.0, 100.0 HeII_HII, HeIII_HII = 0.1, 0.001 neb_int = nebCalc.flux_spectrum(wave, Te, halpha_flux, HeII_HII, HeIII_HII) # Fit the nebular component
def __init__(self, model_name, IR_factor=1., instrument='XShooter', SED='BB', use_slit=True): self.dir_ = dir_ self.model_name = model_name self.instrument = instrument self.SED = SED self.use_slit = use_slit self.options = ('print last', 'Save lines, intensity, column, emergent, ".lines"') emis_tab = [ 'H 1 4861.33A', 'H 1 6562.81A', 'H 1 4340.94A', 'He 1 5875.64A', 'He 1 4471.49A', 'N 2 6583.45A', 'O 1 6300.30A', 'BLND 4363.00A', 'O 3 5006.84A', 'Ne 3 3868.76A', 'BLND 5199.00A', 'O 2 3728.81A', 'O 2 3726.03A', 'BLND 7323.00A', 'Mg 1 4562.60A', 'S 3 6312.06A', 'Ar 3 5191.82A', 'S 2 6730.82A', 'S 2 6716.44A', 'S 2 4076.35A', 'S 2 4068.60A', 'Fe 3 4658.01A', 'Fe 3 5270.40A', 'Fe 3 4701.62A', 'BLND 5755.00A', 'BLND 7332.00A', 'Ar 3 7135.79A', 'Ar 3 7751.11A', 'Ne 3 3967.47A', 'BLND 1909.00A', 'BLND 2326.00A', 'Cl 3 5517.71A', 'Cl 3 5537.87A', 'Ar 2 6.98337m', 'Ar 3 8.98898m', 'Ar 3 21.8253m', 'S 4 10.5076m', 'Ne 2 12.8101m', 'Ne 3 15.5509m', 'S 3 18.7078m', 'S 3 33.4704m', 'Ca B 12.3684m', 'Ca B 12.3837m', 'BLND 3726.00A', 'BLND 3729.00A', 'Ca B 5875.64A', 'S 3 9068.62A', 'Ca B 4471.49A', 'Ca B 6.94480m', 'Ca B 7.09071m' ] self.emis_tab_call = [convert_label(label) for label in emis_tab] # Observed Tc 1 Line Intensities (relative to Hb = 100) for the lines above, in order. Corrected for reddening. # Obs_Tc1_old = [100.0, 316.92571, 46.6812, 15.24, 5.3973, 114.80788, 0.10972677, # 0.4629,109.115, 0.59746,0.01575, 149.216, 234.66, 6.10 ,0.07353, # 0.57287,0.03006,3.9613816, 2.5649392,0.281553, 0.55302, 0.271188, # 0.134266, 0.083, 1.2, 5.1,8.3, 1.96, 0.175, 27., 45.,0.28, 0.32, # 32.8, 6.5, 0.432, 0.47, 37.5, 1.46, 14., 6.21, 0.92, 0.08, 234, # 149, 15.24, np.nan, 5.40, 32.8, 32.8] Obs_Tc1 = [ 100.0, 302.83, 46.66, 15.26, 5.393, 111.33, 0.087, 0.462, 109.143, 0.596, 0.016, 148.885, 234.14, 1.434 + 4.677, 0.037, 0.574, 0.030, 3.531, 2.306, 0.281, 0.552, 0.271, 0.134, 0.083, 1.198, 2.56 + 2.54, 8.292, 1.959, 0.175, 27., 45., 0.28, 0.32, 32.8, 6.5, 0.432, 0.47, 37.5, 1.46, 14., 6.21, 0.92, 0.08, 234, 149, 15.26, np.nan, 5.393, 32.8, 32.8 ] Obs_Tc1_wave = [ 4861.33, 6562.77, 4340.47, 5875.66, 4471.50, 6583.50, 6300.30, 4363.21, 5006.84, 3868.75, 5198.26, 3728.82, 3726.03, 7324.83, 4571.10, 6312.10, 5191.82, 6730.82, 6716.44, 4076.35, 4068.60, 4658.10, 5270.40, 4701.62, 5755., 7332, 7135, 7752, 3967, 1909, 2326, 5518, 5538, 69833.7, 89889.8, 218253, 105076, 128101, 155509, 187078, 334704, 123684, 123837, 3726, 3729, 5876, 9069, 4471, 69448.0, 70907.1 ] self.line_ordered = [ 'H__1_486133A', 'H__1_656281A', 'H__1_434094A', # 'HE_1_587564A', 'CA_B_587564A', 'CA_B_447149A', 'BLND_519900A', 'BLND_575500A', 'N__2_658345A', 'O__1_630030A', 'O__2_372881A', 'O__2_372603A', 'BLND_732300A', 'BLND_733200A', 'O__3_500684A', 'BLND_436300A', 'NE_3_386876A', 'NE_3_396747A', 'CL_3_551771A', 'CL_3_553787A', 'S__2_673082A', 'S__2_671644A', 'S__2_407635A', 'S__2_406860A', 'S__3_631206A', 'S__3_906862A', 'AR_3_519182A', 'AR_3_713579A', 'AR_3_775111A', 'MG_1_456260A', 'FE_3_465801A', 'FE_3_527040A', 'FE_3_470162A', 'BLND_232600A', 'BLND_190900A' ] IR_lines = [ 'CA_B_123684M', # 'CA_B_123837M', 'NE_2_128101M', 'NE_3_155509M', 'S__3_187078M', 'S__3_334704M', 'S__4_105076M', 'CA_B_694480M', 'AR_2_698337M', 'CA_B_709071M', 'AR_3_898898M', 'AR_3_218253M' ] self.line_ordered.extend(IR_lines) self.Obs_Tc1 = {} self.Obs_Tc1_wave = {} self.emis_tab = {} for line in self.emis_tab_call: self.Obs_Tc1[line] = Obs_Tc1[self.emis_tab_call.index(line)] self.Obs_Tc1_wave[line] = Obs_Tc1_wave[self.emis_tab_call.index( line)] self.emis_tab[line] = emis_tab[self.emis_tab_call.index(line)] if self.Obs_Tc1_wave[line] > 10000: self.Obs_Tc1[line] *= IR_factor if self.instrument in ('LCO', 'AAT'): for k in self.Obs_Tc1: if self.Obs_Tc1_wave[k] < 10000. and self.Obs_Tc1_wave[ k] > 2500.: self.Obs_Tc1[k] = np.nan self.Obs_Tc1['H__1_486133A'] = 100. self.Obs_Tc1['HE_1_447149A'] = 1.1 self.Obs_Tc1['CA_B_447149A'] = 1.1 self.Obs_Tc1['N__2_658345A'] = 95.4 self.Obs_Tc1['HE_1_587564A'] = 9.01 self.Obs_Tc1['CA_B_587564A'] = 9.01 self.Obs_Tc1['O__3_500684A'] = 124. self.Obs_Tc1['O__2_372603A'] = 130. self.Obs_Tc1['O__2_372881A'] = 86. self.Obs_Tc1['BLND_436300A'] = 0.55 self.Obs_Tc1['AR_3_519182A'] = 0.031 self.Obs_Tc1['AR_3_713579A'] = 5.65 self.Obs_Tc1['AR_3_775111A'] = 1.66 self.Obs_Tc1['S__2_671644A'] = 2.2 self.Obs_Tc1['S__2_673082A'] = 3.5 self.Obs_Tc1['BLND_575500A'] = 1.09 self.Obs_Tc1['CL_3_551771A'] = 0.285 self.Obs_Tc1['CL_3_553787A'] = 0.303 self.Obs_Tc1['BLND_732300A'] = 5.43 self.Obs_Tc1['BLND_733200A'] = 4.57 self.Obs_Tc1['S__3_631206A'] = 0.46 self.Obs_Tc1['S__2_406860A'] = 0.62 self.Obs_Tc1['S__2_407635A'] = 0.18 self.Obs_Tc1['BLND_519900A'] = 0.04 self.Obs_Tc1['O__1_630030A'] = 0.12 self.Obs_Tc1['S__3_906862A'] = 12.51 #=============================================================== # Blackbody parameters - L in erg/s ; T in K #=============================================================== self.luminosity_unit = 'Q(H)' self.luminosity = 47.2 # np.log10(3000.0*3.826e33) self.Temp = 32000.0 self.gStel = 4 self.Zstel = 0 #=============================================================== # Gas density (nH) in cm-3 #=============================================================== self.densi = np.log10(1800) self.ff = 1.0 #=============================================================== # Inner radius - in log(cm) #=============================================================== self.inner_radius = np.log10(1.0e15) self.outer_radius = None #np.log10(2.1e17) #=============================================================== # Distance in kpc #=============================================================== self.distance = 2.3 #=============================================================== #Abundances #=============================================================== # Model c068mb self.abund = { 'He': np.log10(0.14E0), 'C': np.log10(5.13E-4), 'N': np.log10(8.90E-5), 'O': np.log10(6.00E-4), 'Ne': np.log10(1.60E-5), 'Mg': np.log10(3.47E-5), 'Si': np.log10(9.01E-7), 'S': np.log10(4.10E-6), 'Cl': np.log10(9.40E-8), 'Ar': np.log10(1.80E-6), 'Fe': np.log10(5.00E-7) } self.dust_type = 'graphite_ism_10.opc' self.dust = 1.5 # A function in form of lambda to transform size in cm into arcsec, for a distance defined above. self.arcsec = lambda cm: conv_arc(dist=self.distance, dist_proj=cm) self.cm = lambda arcsec: conv_arc(dist=self.distance, ang_size=arcsec) # An object to correct from redenning in case of necessity self.RC = pn.RedCorr(cHbeta=0.4, law='CCM89') # Reading the line profiles self.profiles = np.genfromtxt('obs_profile.cvs', delimiter=',', names=True) self.ypos = 1.17
# Measure line fluxes idcs_lines = ~lm.linesDF.index.str.contains('_b') obsLines = lm.linesDF.loc[idcs_lines].index.values # Measure line fluxes pdf.create_pdfDoc(pdfTableFile, pdf_type='table') pdf.pdf_insert_table(tableHeaders) # Normalizing flux if 'H1_6563A' in lm.linesDF.index: flux_Halpha = lm.linesDF.loc['H1_6563A', 'gauss_flux'] flux_Hbeta = lm.linesDF.loc['H1_4861A', 'intg_flux'] halpha_norm = flux_Halpha / flux_Hbeta rc = pn.RedCorr(R_V=3.4, law='G03 LMC') rc.setCorr(obs_over_theo=halpha_norm / 2.86, wave1=6563., wave2=4861.) cHbeta = rc.cHbeta else: flux_Hbeta = lm.linesDF.loc['H1_4861A', 'intg_flux'] rc = pn.RedCorr(R_V=3.4, law='G03 LMC', cHbeta=obsData[obj]['cHbeta']) cHbeta = float(rc.cHbeta) for lineLabel in obsLines: label_entry = lm.linesDF.loc[lineLabel, 'latexLabel'] wavelength = lm.linesDF.loc[lineLabel, 'wavelength'] eqw, eqwErr = lm.linesDF.loc[lineLabel, 'eqw'], lm.linesDF.loc[lineLabel, 'eqw_err'] flux_intg = lm.linesDF.loc[lineLabel, 'intg_flux'] / flux_Hbeta * scaleTable flux_intgErr = lm.linesDF.loc[lineLabel, 'intg_err'] / flux_Hbeta * scaleTable
def dustCorr_pyNeb(method, extra_cols): '''Correct emission lines for dust extinction using pyNeb's implementation of the Cardelli89 extinction curve Assumptions: - Ha/Hb = 2.86 at 10,000 K at 100 cm^-3 ''' ############################################################################ # IMPORT DATA ############################################################################ flux_table = Table.read(method + '.txt', format='ascii.commented_header') # Which lines need to be de-reddened? colNames = flux_table.colnames # Add 'index' to extra_cols extra_cols.append('index') if method == 'I06': extra_cols.append('flag3727') extra_cols.append('flagOppSp') extra_cols.append('flagNH') extra_cols.append('flagNeH') extra_cols.append('flagS2') extra_cols.append('S2ratio') extra_cols.append('flagO3') extra_cols.append('O3ratio') # Remove all column names that are not 'index' or in extra_cols emLines = [line for line in colNames if line not in extra_cols] ############################################################################ # INITIALIZE EXTINCTION CORRECTION ############################################################################ RC = pn.RedCorr(law='CCM89') ############################################################################ # CORRECT FOR DUST EXTINCTION ############################################################################ # observed H_alpha / H_beta ratio HaHb_obs = flux_table['H_ALPHA_FLUX'] / flux_table['H_BETA_FLUX'] # Calculate correction coefficient based on theoretical H_alpha/H_beta ratio RC.setCorr(HaHb_obs / 2.86, 6563., 4861.) for line in emLines[::2]: # Extract wavelength from line waveLength = line.split('_')[1] if waveLength == 'BETA': waveLength = 4861. elif waveLength == 'ALPHA': waveLength = 6563. else: waveLength = float(waveLength) # De-redden emission line flux_table[line] = flux_table[line] * RC.getCorrHb(waveLength) ############################################################################ # SAVE DATA ############################################################################ flux_table.write(method + '.txt', format='ascii.commented_header', overwrite=True)
nebCompFile = outputFolder/f'{obj}{ext}_NebFlux.txt' nebPlotFile = outputFolder/f'{obj}{ext}_nebComp.png' # Set wavelength and flux print(f'\n-- Treating {counter} :{obj}{ext}.fits') wave, flux_array, header = sr.import_fits_data(fits_file, instrument='OSIRIS') wave_rest = wave / (1 + z) idx_wave = (wave_rest >= wmin_array[i]) & (wave_rest <= wmax_array[i]) flux = flux_array[idx_band][0] if ext in ('_B', '_R') else flux_array # Load line measurer object lm = sr.LineMesurer(wave_rest[idx_wave], flux[idx_wave], linesDF_address=lineLog_file, normFlux=flux_norm) nebCalc = NebularContinua() # Reddening correction rc = pn.RedCorr(R_V=3.4, law='G03 LMC', cHbeta=cHbeta) red_corr = rc.getCorr(lm.wave) int = lm.flux * red_corr # Compute nebular continuum Hbeta_flux = lm.linesDF.loc['H1_4861A'].intg_flux Halpha_redCorr = rc.getCorr(6563) Halpha_int = Hbeta_flux * 2.86 * Halpha_redCorr neb_int = nebCalc.flux_spectrum(lm.wave, Te_low, Halpha_int, HeII_HII, HeIII_HeII) # Save object spectrum without nebular component flux_noNeb = ((int - neb_int) / red_corr) * flux_norm flux_neb = (neb_int/red_corr) * flux_norm np.savetxt(nebFluxNoNebCompFile, np.transpose(np.array([lm.wave, flux_noNeb])), fmt="%7.1f %10.4e") np.savetxt(nebCompFile, np.transpose(np.array([lm.wave, flux_neb])), fmt="%7.1f %10.4e")
# Convert wavelength to x def x(wave): return 10000. / wave # Define an extinction law (to be used below) def my_X(wave, par=0): x = 10000. / wave Rv = 3.1 X_lin = x / 2. # linear part of the extinction law X_bump = 0.5 * x**2. - 6 * x + 20. # bump part of the extinction law return Rv * np.where(x < 5., X_lin, X_bump) # Define a reddening correction object RC = pn.RedCorr() # List the available laws RC.printLaws() # Plot the available laws RC.plot(laws='all') plt.show() # Choose the one we intend to use RC.law = 'CCM 89' # or define a new one RC.UserFunction = my_X RC.law = 'user' # Plot the selected law as a function of x
def measure_flux(LineMaps, peak_tbl, alpha, Rv, Ebv, lines=None, aperture_size=1.5, background='local', extinction='MW'): '''measure flux for all lines in lines for each position in peak_tbl, the flux inside an aperture of `aperture_size` is measured for each line `lines` (if `LineMaps` has an extinsion). The background is estimated from an annulus with a sigma clipped median (both median and mean are reported). The [OIII] fluxes are Milky Way extinction corrected and the internal extinction is estimated if the Halpha and Hbeta lines are present. Parameters ---------- LineMaps : Galaxy Galaxy object with detected sources peak_tbl : astropy table Table with columns `x` and `y` (position of the sources) alpha : float power index of the moffat lines : list list of lines that are measured aperture_size : float size of the aperture in multiples of the fwhm background : no longer used extinction : no longer used ''' #del self.peaks_tbl['SkyCoord'] # convertion factor from arcsec to pixel (used for the PSF) input_unit = 1e-20 * u.erg / u.cm**2 / u.s ''' check the input parameters ''' # self must be of type Galaxy if not isinstance(LineMaps, ReadLineMaps): logger.warning('input should be of type ReadLineMaps') if background not in ['global', 'local', None]: raise TypeError(f'unknown Background estimation: {background}') # if no line is specified, we measure the flux in all line maps if not lines: lines = LineMaps.lines else: # make sure lines is a list lines = [lines] if not isinstance(lines, list) else lines for line in lines: if not hasattr(LineMaps, line): raise AttributeError( f'{LineMaps.name} has no attribute {line}') logger.info( f'measuring fluxes in {LineMaps.name} for {len(peak_tbl)} sources\naperture = {aperture_size} fwhm' ) ''' loop over all lines to measure the fluxes for all sources ''' out = {} for line in lines: logger.info(f'measuring fluxes in {line} line map') # select data and error (copy in case we need to modify it) data = getattr(LineMaps, f'{line}').copy() error = getattr(LineMaps, f'{line}_err').copy() try: v_disp = getattr(LineMaps, f'{line}_SIGMA') except: logger.warning('no maps with velocity dispersion for ' + line) v_disp = np.zeros(data.shape) # the fwhm varies slightly with wavelength wavelength = int(re.findall(r'\d{4}', line)[0]) PSF_correction = correct_PSF(wavelength) # calculate a global background map mask = np.isnan(data) ''' bkg = Background2D(data,(10,10), #filter_size=(15,15), sigma_clip= None,#SigmaClip(sigma=3.,maxiters=None), bkg_estimator=MedianBackground(), mask=mask).background bkg[mask] = np.nan from astropy.convolution import convolve, Gaussian2DKernel, Box2DKernel kernel = Box2DKernel(10) #Gaussian2DKernel(10) bkg_convolve = convolve(data,kernel,nan_treatment='interpolate',preserve_nan=True) # this is too slow and the masks ignore bright HA emitter etc. source_mask = np.zeros(self.shape,dtype=bool) for fwhm in np.unique(peak_tbl['fwhm']): source_part = peak_tbl[peak_tbl['fwhm']==fwhm] positions = np.transpose((source_part['x'], source_part['y'])) r = 4 * (fwhm-PSF_correction) / 2 aperture = CircularAperture(positions, r=r) for m in aperture.to_mask(method='center'): source_mask |= m.to_image(self.shape).astype(bool) ''' ''' loop over the individual pointings (they have different fwhm) ''' for fwhm in np.unique(peak_tbl['fwhm']): source_part = peak_tbl[peak_tbl['fwhm'] == fwhm] positions = np.transpose((source_part['x'], source_part['y'])) gamma = (fwhm - PSF_correction) / (2 * np.sqrt(2**(1 / alpha) - 1)) if aperture_size > 3: logger.warning('aperture > 3 FWHM') r = aperture_size * (fwhm - PSF_correction) / 2 aperture = CircularAperture(positions, r=r) # measure the flux for each source phot = aperture_photometry(data, aperture, error=error) # the local background subtraction estimates the background for # each source individually (annulus with 5 times the area of aperture) r_in = 4 * (fwhm - PSF_correction) / 2 r_out = np.sqrt(5 * r**2 + r_in**2) annulus_aperture = CircularAnnulus(positions, r_in=r_in, r_out=r_out) annulus_masks = annulus_aperture.to_mask(method='center') # background from annulus with sigma clipping bkg_median = [] bgk_mean = [] for mask in annulus_masks: # select the pixels inside the annulus and calulate sigma clipped median annulus_data = mask.multiply(data) annulus_data_1d = annulus_data[mask.data > 0] _, median_sigclip, _ = sigma_clipped_stats( annulus_data_1d[~np.isnan(annulus_data_1d)], sigma=3, maxiters=10, cenfunc='median') mean_sigclip, _, _ = sigma_clipped_stats( annulus_data_1d[~np.isnan(annulus_data_1d)], sigma=3, maxiters=10, cenfunc='mean') bkg_median.append(median_sigclip) bgk_mean.append(mean_sigclip) # save bkg_median in case we need it again and multiply background with size of the aperture phot['bkg_median'] = np.array(bkg_median) * aperture.area phot['bkg_mean'] = np.array(bgk_mean) * aperture.area ''' # background from annulus with masked sources ones = np.ones(self.shape) # calculate flux in annulus where other sources are masked bkg_phot = aperture_photometry(data,annulus_aperture,mask=source_mask) # calculate area of the annulus (parts can be masked) bkg_area = aperture_photometry(ones,annulus_aperture,mask=source_mask) # save bkg_median in case we need it again phot['bkg_median'] = bkg_phot['aperture_sum'] / bkg_area['aperture_sum'] # multiply background with size of the aperture phot['bkg_local'] = phot['bkg_median'] * aperture.area ''' phot[f'{line}_flux'] = phot['aperture_sum'] - phot['bkg_median'] phot[f'{line}_flux_raw'] = phot['aperture_sum'] # we don't subtract the background from OIII because there is none #if line == 'OIII5006_DAP': # phot[f'{line}_flux'] = phot['aperture_sum'] # correct for flux that is lost outside of the aperture phot[f'{line}_flux'] /= light_in_moffat(r, alpha, gamma) phot[f'{line}_flux_raw'] /= light_in_moffat(r, alpha, gamma) phot[f'{line}_flux_err'] = phot[ 'aperture_sum_err'] / light_in_moffat(r, alpha, gamma) phot['bkg_median'] /= light_in_moffat(r, alpha, gamma) phot['bkg_mean'] /= light_in_moffat(r, alpha, gamma) #print(f'{fwhm}: {light_in_moffat(r,alpha,gamma):.2f}') # calculate the average of the velocity dispersion aperture = CircularAperture(positions, r=4) SIGMA = aperture_photometry(v_disp, aperture) phot['SIGMA'] = SIGMA['aperture_sum'] / aperture.area # calculate stellar mass (for mass specific PN number, not used) #aperture = CircularAperture(positions, r=2) #stellar_mass = aperture_photometry(LineMaps.stellar_mass,aperture) #phot['stellar_mass'] = stellar_mass['aperture_sum'] / aperture.area # save fwhm in an additional column phot['fwhm'] = fwhm # concatenate new sources with output table if 'flux' in locals(): phot['id'] += np.amax(flux['id'], initial=0) flux = vstack([flux, phot]) else: flux = phot # for consistent table output for col in flux.colnames: flux[col].info.format = '%.8g' flux['fwhm'].info.format = '%.3g' out[line] = flux # we need an empty table for the next line del flux # so far we have an individual table for each emission line for line, v in out.items(): # find the wavelength for the extinction correction wavelength = re.findall(r'\d{4}', line) if len(wavelength) != 1: logger.error( 'line name must contain wavelength as 4 digit number in angstrom' ) wavelength = int(wavelength[0]) # first we create the output table with if 'flux' not in locals(): flux = v[['id', 'xcenter', 'ycenter', 'fwhm']] flux.rename_columns(['xcenter', 'ycenter'], ['x', 'y']) flux['x'] = flux[ 'x'].value # we don't want them to be in pixel units flux['y'] = flux['y'].value # flux[f'{line}_flux'] = v[f'{line}_flux'] flux[f'{line}_flux_err'] = v[f'{line}_flux_err'] flux[f'{line}_flux_raw'] = v[f'{line}_flux_raw'] flux[f'{line}_bkg_median'] = v[f'bkg_median'] flux[f'{line}_bkg_mean'] = v[f'bkg_mean'] # linemaps are already MW extinction corrected (OIII sum is not) # the new [OIII] fluxes use the DAP [OIII] errors and hence are already extinction corrected if line == 'OIII5006': rc = pyneb.RedCorr(R_V=3.1, E_BV=Ebv, law='CCM89') flux['OIII5006_flux'] *= rc.getCorr(5006) flux['OIII5006_flux_raw'] *= rc.getCorr(5006) flux['OIII5006_bkg_median'] *= rc.getCorr(5006) flux['OIII5006_bkg_mean'] *= rc.getCorr(5006) logger.info( f'lambda{wavelength}: Av={-2.5*np.log10(1/rc.getCorr(5006)):.2f}' ) # those columns are only needed for tests if False: flux[f'{line}_aperture_sum'] = v['aperture_sum'] flux[f'{line}_bkg_local'] = v['bkg_local'] flux[f'{line}_bkg_median'] = v['bkg_median'] #flux[f'{k}_bkg_convole'] = v['bkg_convolve'] flux[f'{line}_SIGMA'] = v['SIGMA'] # the internal extinction correction based on the balmer decrement # we do not calculate an error of E(B-V) and hance also do not account for this in the corrected errors if 'HB4861' in lines and 'HA6562' in lines: logger.info('correction for internal extinction with balmer decrement') rc = pyneb.RedCorr(R_V=3.1, law='CCM89') rc.setCorr(obs_over_theo=flux['HA6562_flux'] / flux['HB4861_flux'] / 2.86, wave1=6562.81, wave2=4861.33) rc.E_BV[(rc.E_BV < 0) | (flux['HB4861_flux'] < 3 * flux['HB4861_flux_err']) | (flux['HA6562_flux'] < 3 * flux['HA6562_flux_err'])] = 0 flux['EBV_balmer'] = rc.E_BV for line in lines: wavelength = int(re.findall(r'\d{4}', line)[0]) flux[f'{line}_flux_corr'] = flux[f'{line}_flux'] * rc.getCorr( wavelength) flux[f'{line}_flux_corr_err'] = flux[ f'{line}_flux_err'] * rc.getCorr(wavelength) flux[f'{line}_bkg_median_corr'] = flux[ f'{line}_bkg_median'] * rc.getCorr(wavelength) flux[f'{line}_bkg_mean_corr'] = flux[ f'{line}_bkg_mean'] * rc.getCorr(wavelength) logger.info('all flux measurements completed') return flux
cHbeta = results_dict['Initial_values']['cHbeta_BR_Hbeta_Hgamma_Hdelta'] HeII_HII, HeIII_HeII = results_dict['Initial_values'][ 'HeII_HII'], results_dict['Initial_values']['HeIII_HII'] # Load spectrum print(f'\n-- Treating: {obj}{ext}.fits') wave, flux_array, header = sr.import_fits_data(fits_file, instrument='OSIRIS') flux = flux_array[idx_band][0] if ext in ('_B', '_R') else flux_array lm = sr.LineMesurer(wave, flux, redshift=z_array[i], crop_waves=(wmin_array[i], wmax_array[i])) # Extinction correction rc = pn.RedCorr(R_V=RV, law=red_law, cHbeta=cHbeta[0]) red_corr = rc.getCorr(lm.wave) red_corr_Hbeta = rc.getCorr(4861.0) int_spec = lm.flux * red_corr emis_AlphaBetaRatio = H1.getEmissivity(tem=Te_low, den=ne, wave=6563) / H1.getEmissivity( tem=Te_low, den=ne, wave=4861) # Hbeta red correction f_spec_Hbeta, f_Hbeta = compute_spectrum_flambda(lm.wave, red_law, RV, ref_line='H1_4861A') int_spec_Hbeta, int_spec_mine_err = deredd_fluxes(lm.flux, np.zeros(lm.flux.size), cHbeta[0], cHbeta[1],
def compute_cHbeta(line_df, reddening_curve, R_v, temp=10000.0, den=100.0, ref_wave='H1_4861A', compMode='auto'): assert ref_wave in line_df.index, f'- ERROR: Reference line {ref_wave} is not in input dataframe index' # Create hydrogen recombination atom for emissivities calculation H1 = pn.RecAtom('H', 1) # Use all the lines from the input data frame line_labels = line_df.index.values ion_ref, waves_ref, latexLabels_ref = label_decomposition( ref_wave, scalar_output=True) ion_array, waves_array, latexLabels_array = label_decomposition( line_labels) # Mode 1: Distinguish between single (intg_flux) and blended (gauss_flux) lines if compMode == 'auto': Href_flux, Href_err = line_df.loc[ref_wave, 'intg_flux'], line_df.loc[ref_wave, 'intg_err'] obsFlux, obsErr = np.empty(line_labels.size), np.empty( line_labels.size) idcs_intg = (line_df.blended == 'None') obsFlux[idcs_intg], obsErr[idcs_intg] = line_df.loc[ idcs_intg, ['intg_flux', 'intg_err']].values obsFlux[~idcs_intg], obsErr[~idcs_intg] = line_df.loc[ ~idcs_intg, ['gauss_flux', 'gauss_err']].values # Mode 2: Use always the gaussian flux elif compMode == 'gauss': Href_flux, Href_err = line_df.loc[ref_wave, 'gauss_flux'], line_df.loc[ ref_wave, 'gauss_err'] obsFlux, obsErr = line_df['gauss_flux'].values, line_df[ 'gauss_err'].values # Ratio propagating the uncertainty between the lines obsFlux_norm = obsFlux / Href_flux obsErr_norm = obsFlux_norm * np.sqrt( np.square(obsErr / obsFlux) + np.square(Href_err / Href_flux)) assert not np.any( np.isnan(obsFlux) ) in obsFlux, '- ERROR: nan entry in input fluxes for c(Hbeta) calculation' assert not np.any( np.isnan(obsErr) ) in obsErr, '- ERROR: nan entry in input uncertainties for c(Hbeta) calculation' # Theoretical ratios refEmis = H1.getEmissivity(tem=temp, den=den, wave=waves_ref) emisIterable = (H1.getEmissivity(tem=temp, den=den, wave=wave) for wave in waves_array) linesEmis = np.fromiter(emisIterable, float) theoRatios = linesEmis / refEmis # Reddening law rc = pn.RedCorr(R_V=R_v, law=reddening_curve) Xx_ref, Xx = rc.X(waves_ref), rc.X(waves_array) f_lines = Xx / Xx_ref - 1 f_ref = Xx_ref / Xx_ref - 1 # cHbeta slope fit axes x_fred = f_lines - f_ref y_flux = np.log10(theoRatios) - np.log10(obsFlux_norm) y_err = (obsErr_norm / obsFlux_norm) * (1.0 / np.log(10)) # Perform fit lineModel = LinearModel() pars = lineModel.make_params(intercept=y_flux.min(), slope=0) output = lineModel.fit(y_flux, pars, x=x_fred, weights=1 / np.sqrt(y_err)) cHbeta, cHbeta_err = output.params['slope'].value, output.params[ 'slope'].stderr intercept, intercept_err = output.params['intercept'].value, output.params[ 'intercept'].stderr # Store the results output_dict = dict(cHbeta=cHbeta, cHbeta_err=cHbeta_err, intercept=intercept, intercept_err=intercept_err, obsRecomb=obsFlux_norm, obsRecombErr=obsErr_norm, y=y_flux, y_err=y_err, x=x_fred, line_labels=latexLabels_array, ref_line=latexLabels_ref) return output_dict
wave_rest, flux, header = sr.import_fits_data(fitsFolder/fitsFile, instrument='SDSS') idx_wave = (wave_rest >= obsData['sample_data']['wmin_array']) & (wave_rest <= obsData['sample_data']['wmax_array']) # Load line measurer object lm = sr.LineMesurer(wave_rest[idx_wave], flux[idx_wave], lineLogFolder / lineLogFile, normFlux=flux_norm) # Measure line fluxes idcs_lines = ~lm.linesDF.index.str.contains('_b') obsLines = lm.linesDF.loc[idcs_lines].index.values # Equivalent width Hbeta eqw_Hbeta, eqwErr_Hbeta = lm.linesDF.loc['H1_4861A', 'eqw'], lm.linesDF.loc['H1_4861A', 'eqw_err'] eqw_entry = r'${:0.2f}$ $\pm$ ${:0.2f}$'.format(eqw_Hbeta, eqwErr_Hbeta) # Normalizing flux flux_Halpha = lm.linesDF.loc['H1_6563A', 'gauss_flux'] flux_Hbeta = lm.linesDF.loc['H1_4861A', 'intg_flux'] halpha_norm = flux_Halpha / flux_Hbeta halphaRatio_entry = r'${:0.2f}$'.format(halpha_norm) colorGrade = colorChooser(halpha_norm, 2.86) RatioComparison = r'\textcolor{{{}}}{{{}}}'.format(colorGrade, halphaRatio_entry) # Reddening coefficient rc = pn.RedCorr(R_V=3.4, law='G03 LMC') rc.setCorr(obs_over_theo=halpha_norm/2.86, wave1=6563., wave2=4861.) cHbeta_entry = r'${:0.2f}$'.format(rc.cHbeta) pdf.addTableRow([objName, eqw_entry, cHbeta_entry, RatioComparison], last_row=True if file_address == addressList[-1] else False) # Generate the table pdf.generate_pdf(clean_tex=True)
lm = sr.LineMesurer(wave, flux, redshift=z_array[i], crop_waves=(wmin_array[i], wmax_array[i])) # Starlight wrapper sw = SSPsynthesizer() # Read output data stellar_Wave, obj_input_flux, stellar_flux, fit_output = sw.load_starlight_output(starlight2012_folder/outputFile) z_gp = obsData['sample_data']['z_array'][i] Mcor, Mint = fit_output['Mcor_tot'], fit_output['Mini_tot'] mass_galaxy = computeSSP_galaxy_mass(Mcor, 1, z_gp) massProcess_galaxy = computeSSP_galaxy_mass(Mint, 1, z_gp) idcs_below_20Myr = fit_output['DF'].age_j < 2*10**7 mass_galaxy_20Myr_percent = np.sum(fit_output['DF'].loc[idcs_below_20Myr, 'Mcor_j'].values) # Store starlight configuration values for linux runy rc = pn.RedCorr(R_V=RV, E_BV=fit_output['Av_min'] / RV, law=red_law) cHbeta_star = rc.cHbetaFromEbv(fit_output['Av_min']/RV) starlight_cfg = {'gridFileName': outputFile, 'outputFile': outputFile, 'saveFolder': starlight2012_folder.as_posix(), 'Galaxy_mass_Current': mass_galaxy, 'Galaxy_mass_Prosessed': massProcess_galaxy, 'Galaxy_mass_Percentbelow20Myr': mass_galaxy_20Myr_percent, 'Chi2': fit_output['Chi2'], 'A_V_stellarr': fit_output['Av_min'], 'cHbeta_stellar': cHbeta_star, 'PixelMeanDevPer': fit_output['SumXdev'], 'SN': fit_output['SignalToNoise_magnitudeWave']} sr.parseConfDict(results_file, starlight_cfg, f'Starlight_run_{cycle}', clear_section=True) # Plot the results
idcs_comp = complete_df['wavelength'] == wave lineLabel = complete_df.loc[idcs_comp].index.values[0] flux_array, err_array = complete_df.loc[ idcs_comp, 'gauss_flux'].values, complete_df.loc[idcs_comp, 'gauss_err'].values label_root = lineLabel[0:lineLabel.rfind('_')] flux, err = flux_array.sum(), np.sqrt(np.sum(np.square(err_array))) complete_df.loc[label_root, 'gauss_flux':'gauss_err'] = flux, err complete_df.loc[label_root, 'wavelength'] = wave # Apply the extinction correction cHbeta = obsCfg['MIKE_extinction_summed_profiles'][ f'cHbeta_{arm}_{i_night}'][0] rc = pn.RedCorr(R_V=R_v, law=red_curve, cHbeta=cHbeta) wave_array, flux_array, err_array = complete_df.wavelength.values, complete_df.gauss_flux.values, complete_df.gauss_err.values corr = rc.getCorr(wave_array) intensity, intensityErr = flux_array * corr, err_array * corr complete_df['gauss_int'], complete_df[ 'gauss_int_err'] = intensity, intensityErr # Compute the nSII density T_low = 15000.0 ne_low = 250 S3 = pn.Atom('S', 3) S2 = pn.Atom('S', 2) O2 = pn.Atom('O', 2) O3 = pn.Atom('O', 3) mc_steps = 1000
# Compute Hb emissivity at T=10000K pn.getRecEmissivity(10000, 1e2, 4, 2, atom='H1') # simultaneously compute temperature and density from pairs of line ratios # First of all, a Diagnostics object must be created and initialized with the relevant diagnostics. diags = pn.Diagnostics() # this creates the object diags.getAllDiags() # see what Diagnostics exist tem, den = diags.getCrossTemDen('[NII] 5755/6548', '[SII] 6731/6716', 50, 1.0, guess_tem=10000, tol_tem=1., tol_den=1., max_iter=5) #TO BE CONTINUED FROM HERE print(tem, den) ####################################################################### # HANDLING OBSERVATIONS # explore the line label list to write an observation record pn.LINE_LABEL_LIST # if only Ar IV is needed pn.LINE_LABEL_LIST['Ar4'] # list the available extinction laws pn.RedCorr().printLaws()
try: # sum up the flux inside of this mask row['HA6562_FLUX'] = np.sum(mask.multiply(cutout_Halpha.data)) row['HB4861_FLUX'] = np.sum(mask.multiply(cutout_Hbeta.data)) # for the uncertainty we take the square root of the sum of the squared uncertainties row['HA6562_FLUX_ERR'] = np.sqrt( np.sum(mask.multiply(cutout_Halpha.data)**2)) row['HB4861_FLUX_ERR'] = np.sqrt( np.sum(mask.multiply(cutout_Hbeta.data)**2)) except: continue print('correct for extinction') # Milky Way extinction rc_MW = pyneb.RedCorr(E_BV=EBV_MW[gal_name], R_V=3.1, law='CCM89 oD94') tbl['HA6562_FLUX'] *= rc_MW.getCorr(6562) tbl['HB4861_FLUX'] *= rc_MW.getCorr(4861) tbl['HA6562_FLUX_ERR'] *= rc_MW.getCorr(6562) tbl['HB4861_FLUX_ERR'] *= rc_MW.getCorr(4861) # Internal extinction is estimated from the Balmer decrement rc_balmer = pyneb.RedCorr(R_V=3.1, law='CCM89 oD94') rc_balmer.setCorr(obs_over_theo=tbl['HA6562_FLUX'] / tbl['HB4861_FLUX'] / 2.86, wave1=6562.81, wave2=4861.33) # set E(B-V) to zero if S/N is less than 3 in Halpha or Hbeta rc_balmer.E_BV[(rc_balmer.E_BV < 0) | (tbl['HB4861_FLUX'] < 3 * tbl['HB4861_FLUX_ERR']) | (tbl['HA6562_FLUX'] < 3 * tbl['HA6562_FLUX_ERR'])] = 0