def renormalize_visit_spectrum(norm_spec, spec_err, label_guess, NN_coeffs_norm, NN_coeffs_flux, v_helio): ''' Because visit spectra are initially normalized using a different routine than is implemented in the main spectral modle, then need to be normalized again. This first obtains the continuum for a synthetic single-star model with parameters given by label_guess, multiplies the spectrum by this continuum, and then normalizes that "unnormalized" spectrum using the default normalization routine. It isn't critical that label_guess be vary accurate, since it only supplies a smooth continuum that is divided out again anyway, but it can help a bit. Normally, label_guess is obtained by fitting a single-star model to the combined spectrum. ''' star_labels = label_guess[:5] labels = np.concatenate([star_labels, [v_helio]]) flux_spec_synth = spectral_model.get_surface_flux_spectrum_single_star( labels=labels, NN_coeffs_norm=NN_coeffs_norm, NN_coeffs_flux=NN_coeffs_flux) cont_synth = utils.get_apogee_continuum(wavelength=wavelength, spec=flux_spec_synth, spec_err=None, cont_pixels=cont_pixels) flux_spec_data = cont_synth * norm_spec cont_data = utils.get_apogee_continuum(wavelength=wavelength, spec=flux_spec_data, spec_err=spec_err, cont_pixels=cont_pixels) renormalized_spec = flux_spec_data / cont_data return renormalized_spec
def get_normalized_spectrum_N(labels, NN_coeffs_norm, NN_coeffs_flux, NN_coeffs_Teff2_logg2, NN_coeffs_R, spec_err=None): '''Determine Teff and logg of the non-primary stars, predict the normalized spectra of the primary,secondary,thrid,etc. stars, add them together, and normalize. labels = [Teff, logg, [Fe/H], [Mg/Fe], vmacro1, dv1] + [q2, vmacro2, dv2] + .. [qN, vmacroN, dvN] ''' Teff1, logg1, feh, alphafe, vmacro1, dv1 = labels[:6] labels1 = [Teff1, logg1, feh, alphafe, vmacro1, dv1] f_lambda1 = get_unnormalized_spectrum_single_star( labels=labels1, NN_coeffs_norm=NN_coeffs_norm, NN_coeffs_flux=NN_coeffs_flux, NN_coeffs_R=NN_coeffs_R) f_lambda_N = f_lambda1.copy() ## now determin the number of additional components, each with q, vmacro, dv N = int((len(labels) - 6) / 3) for n in range(N): q, vmacro2, dv2 = labels[6 + n * 3:9 + n * 3] Teff2, logg2 = get_Teff2_logg2_NN( labels=[Teff1, logg1, feh, q], NN_coeffs_Teff2_logg2=NN_coeffs_Teff2_logg2) labels2 = [Teff2, logg2, feh, alphafe, vmacro2, dv2] f_lambda2 = get_unnormalized_spectrum_single_star( labels=labels2, NN_coeffs_norm=NN_coeffs_norm, NN_coeffs_flux=NN_coeffs_flux, NN_coeffs_R=NN_coeffs_R) f_lambda_N += f_lambda2 cont = utils.get_apogee_continuum(wavelength=wavelength, spec=f_lambda_N, spec_err=spec_err, cont_pixels=cont_pixels) f_lambda_N_norm = f_lambda_N / cont return f_lambda_N_norm
def get_surface_flux_spectrum_single_star(labels, NN_coeffs_norm, NN_coeffs_flux): ''' (a) predict the normalized spectrum, presumably using a data-driven model, (b) predict the unnormalized spectrum, using e.g. Kurucz models (c) get the continuum from the unnormalized spectrum (d) multiply the normalized spectrum by said continuum. (e) redshift as necessary The result is an unnormalized spectrum which has the high-frequency features (e.g. lines) from the data-driven model and low-frequency features (continuum) from the synthetic model. This has units of surface flux; i.e., it is not scaled by the radius of the star labels: [Teff, logg, feh, alpha, vmacro, dv] (dv is the velocity offset) ''' c_aa_s = 2.998e18 # speed of light in angstroms/sec dv = labels[-1] # first, get f_nu in surface flux units. # labels for the synthetic spectra are [Teff, logg, feh, alpha] # and for the data driven spectra, [Teff, logg, feh, alpha, vmacro] fnu = get_spectrum_from_neural_net(labels[:-2], NN_coeffs=NN_coeffs_flux, normalized=False) cont = utils.get_apogee_continuum(wavelength=wavelength, spec=fnu, spec_err=None, cont_pixels=cont_pixels) fnu_norm = get_spectrum_from_neural_net(labels[:-1], NN_coeffs=NN_coeffs_norm, normalized=True) fnu_good = fnu_norm * cont # in erg cm^-2 s^-1 Hz^-1 Sr^-1 # change from fnu to f_lambda. The factor of 4*pi gets rid of the Sr^-1. f_lambda = fnu_good * ( c_aa_s / wavelength**2) * 4 * np.pi # in erg cm^-2 s^-1 AA^-1 f_shifted = utils.doppler_shift(wavelength=wavelength, flux=f_lambda, dv=dv) return f_shifted
def get_normalized_spectrum_single_star(labels, NN_coeffs_norm, NN_coeffs_flux, spec_err=None): ''' Predict a spectrum in unnormalized space. Then normalize it using the observed flux uncertainties, so that it can be compared to the observed spectrum self-consistently. labels = [Teff, logg, [Fe/H], [Mg/Fe], v_macro, dv] ''' f_lambda = get_surface_flux_spectrum_single_star( labels=labels, NN_coeffs_norm=NN_coeffs_norm, NN_coeffs_flux=NN_coeffs_flux) cont = utils.get_apogee_continuum(wavelength=wavelength, spec=f_lambda, spec_err=spec_err, cont_pixels=cont_pixels) f_lambda_norm = f_lambda / cont return f_lambda_norm
def get_normalized_spectrum_binary(labels, NN_coeffs_norm, NN_coeffs_flux, NN_coeffs_Teff2_logg2, NN_coeffs_R, spec_err=None): ''' Determine Teff and logg of the secondary, predict the normalized spectra of the primary and the secondary, add them together, and normalize. labels = [Teff1, logg1, [Fe/H], [Mg/Fe], q, v_macro1, v_macro2, dv1, dv2] spec_err is an array of uncertainties that is used in normalization only. If you just want to predict the theoretical spectrum, it isn't needed. But if you're fitting an observed spectrum, this should be the uncertainties of the observed spectrum, so that the observed and model spectra are normalized self-consistently. ''' Teff1, logg1, feh, alphafe, q, vmacro1, vmacro2, dv1, dv2 = labels Teff2, logg2 = get_Teff2_logg2_NN( labels=[Teff1, logg1, feh, q], NN_coeffs_Teff2_logg2=NN_coeffs_Teff2_logg2) labels1 = [Teff1, logg1, feh, alphafe, vmacro1, dv1] labels2 = [Teff2, logg2, feh, alphafe, vmacro2, dv2] f_lambda1 = get_unnormalized_spectrum_single_star( labels=labels1, NN_coeffs_norm=NN_coeffs_norm, NN_coeffs_flux=NN_coeffs_flux, NN_coeffs_R=NN_coeffs_R) f_lambda2 = get_unnormalized_spectrum_single_star( labels=labels2, NN_coeffs_norm=NN_coeffs_norm, NN_coeffs_flux=NN_coeffs_flux, NN_coeffs_R=NN_coeffs_R) f_lambda_binary = f_lambda1 + f_lambda2 cont = utils.get_apogee_continuum(wavelength=wavelength, spec=f_lambda_binary, spec_err=spec_err, cont_pixels=cont_pixels) f_lambda_binary_norm = f_lambda_binary / cont return f_lambda_binary_norm
def read_batch_of_spectra(batch_count, batch_size=10000): ''' Download a bunch of *combined* spectra in one go. Set the uncertainties to a large value in bad pixels, normalize, and save the batch locally. ''' # read in the catalog catalog catalog, fibers = read_apogee_catalog() catalog = catalog[batch_count * batch_size:(batch_count + 1) * batch_size] fibers = fibers[batch_count * batch_size:(batch_count + 1) * batch_size] _COMBINED_INDEX = 1 nspec = len(catalog) spec = np.zeros((nspec, 7214)) specerr = np.zeros((nspec, 7214)) # Set up bad pixel mask badcombpixmask = bitmask.badpixmask() + 2**bitmask.apogee_pixmask_int( "SIG_SKYLINE") # loop through the individual targets for ii in range(nspec): field = catalog['FIELD'][ii].decode() ap_id = catalog['APOGEE_ID'][ii].decode() loc_id = catalog['LOCATION_ID'][ii] print('processing target %d with id %s' % (ii, ap_id)) try: if loc_id == 1: temp1 = apread.apStar(field, ap_id, ext=1, header=False, aspcapWavegrid=True) temp2 = apread.apStar(field, ap_id, ext=2, header=False, aspcapWavegrid=True) temp3 = apread.apStar(field, ap_id, ext=3, header=False, aspcapWavegrid=True) else: temp1 = apread.apStar(loc_id, ap_id, ext=1, header=False, aspcapWavegrid=True) temp2 = apread.apStar(loc_id, ap_id, ext=2, header=False, aspcapWavegrid=True) temp3 = apread.apStar(loc_id, ap_id, ext=3, header=False, aspcapWavegrid=True) if temp1.shape[0] > 6000: spec[ii] = temp1 specerr[ii] = temp2 mask = temp3 else: spec[ii] = temp1[_COMBINED_INDEX] specerr[ii] = temp2[_COMBINED_INDEX] mask = temp3[_COMBINED_INDEX] # Inflate uncertainties for bad pixels specerr[ii, (mask & (badcombpixmask)) != 0] += \ 100. * np.mean(spec[ii, np.isfinite(spec[ii])]) # Inflate pixels with high SNR to 0.5 highsnr = spec[ii] / specerr[ii] > 200. specerr[ii, highsnr] = 0.005 * np.fabs(spec[ii, highsnr]) # Continuum-normalize cont = utils.get_apogee_continuum(wavelength=wavelength, spec=spec[ii], spec_err=specerr[ii], cont_pixels=cont_pixels) spec[ii] /= cont specerr[ii] /= cont specerr[ii, highsnr] = 0.005 except OSError: print('target could not be found!') continue # save spectra np.savez('spectra/apogee_all_spectra_' + str(batch_count) + '.npz', wavelength=wavelength, spectra=spec, spec_err=specerr, apogee_id=np.array(catalog["APOGEE_ID"]), apogee_fiber_id=fibers)
def get_combined_spectrum_single_object(apogee_id, catalog=None, save_local=False): ''' apogee_id should be a byte-like object; i.e b'2M13012770+5754582' This downloads a single combined spectrum and the associated error array, and it normalizes both. ''' # read in the allStar catalog if you haven't already if catalog is None: catalog, fibers = read_apogee_catalog() # Set up bad pixel mask badcombpixmask = bitmask.badpixmask() + 2**bitmask.apogee_pixmask_int( "SIG_SKYLINE") _COMBINED_INDEX = 1 msk = np.where(catalog['APOGEE_ID'] == apogee_id)[0] if not len(msk): raise ValueError( 'the desired Apogee ID was not found in the allStar catalog.') field = catalog['FIELD'][msk[0]].decode() ap_id = apogee_id.decode() loc_id = catalog['LOCATION_ID'][msk[0]] if loc_id == 1: temp1 = apread.apStar(field, ap_id, ext=1, header=False, aspcapWavegrid=True) temp2 = apread.apStar(field, ap_id, ext=2, header=False, aspcapWavegrid=True) temp3 = apread.apStar(field, ap_id, ext=3, header=False, aspcapWavegrid=True) else: temp1 = apread.apStar(loc_id, ap_id, ext=1, header=False, aspcapWavegrid=True) temp2 = apread.apStar(loc_id, ap_id, ext=2, header=False, aspcapWavegrid=True) temp3 = apread.apStar(loc_id, ap_id, ext=3, header=False, aspcapWavegrid=True) if temp1.shape[0] > 6000: spec = temp1 specerr = temp2 mask = temp3 else: spec = temp1[_COMBINED_INDEX] specerr = temp2[_COMBINED_INDEX] mask = temp3[_COMBINED_INDEX] # Inflate uncertainties for bad pixels specerr[(mask & (badcombpixmask)) != 0] += 100 * np.mean( spec[np.isfinite(spec)]) # Inflate pixels with high SNR to 0.5 highsnr = spec / specerr > 200. specerr[highsnr] = 0.005 * np.fabs(spec[highsnr]) # Continuum-normalize cont = utils.get_apogee_continuum(wavelength=wavelength, spec=spec, spec_err=specerr, cont_pixels=cont_pixels) spec /= cont specerr /= cont specerr[highsnr] = 0.005 if save_local: np.savez('spectra/combined/spectrum_ap_id_' + str(apogee_id.decode()) + '_.npz', spectrum=spec, spec_err=specerr) return spec, specerr