def main(bands: Optional[List[str]] = None, verbose: bool = False) -> None: """Preform the barycentric shifting of atmosphere masks and saves result. This saves time in the precision determination code. Parameters ---------- bands: list of str Wavelength bands to perform barycenter shifts on. Default is all bands. """ if (bands is None) or ("ALL" in bands): bands_ = config.bands["all"] else: bands_ = bands for band in bands_: unshifted_atmmodel = join( config.pathdir, config.paths["atmmodel"], "{0}_{1}.dat".format(config.atmmodel["base"], band), ) if verbose: print("Reading atmospheric model...", unshifted_atmmodel) atm = Atmosphere.from_file(unshifted_atmmodel) if verbose: print("Calculating impact of Barycentric movement on mask...") org_mask = atm.mask masked_before = np.sum(org_mask) atm.barycenter_broaden(consecutive_test=True) masked_after = np.sum(atm.mask) if verbose: print("Masked fraction before = {0:0.03f}".format( (len(org_mask) - masked_before) / len(org_mask))) print("Masked fraction after = {0:0.03f}".format( (len(atm.mask) - masked_after) / len(atm.mask))) shifted_atmmodel = unshifted_atmmodel.replace(".dat", "_bary.dat") if verbose: print("Saving doppler-shifted atmosphere model to {}".format( shifted_atmmodel)) header = ["# atm_wav(nm)", "atm_flux", "atm_std_flux", "atm_mask"] atm.to_file(fname=shifted_atmmodel, header=header, fmt="%11.8f") if verbose: print("Finished barycentric shifting of atmosphere masks")
def sliced_atmmodel_default_mask(request, atm_model): """To do own masking. Sliced in different places.""" lower, upper = request.param # slice limits atm = Atmosphere.from_file(atm_model) return atm[int(lower) : int(upper)]
def atmosphere_fixture(request, atm_model): percent_cutoff = request.param atm = Atmosphere.from_file(atm_model) atm.mask_transmission(percent_cutoff) return atm
def main( model: str = atmmodel, bands: Optional[List[str]] = None, new_name: Optional[str] = None, data_dir: Optional[str] = None, rv_extend: float = 100, cutoff_depth: float = 2.0, ): """Split the large atmospheric model transmission spectra into the separate bands. Keeps wavelength of atmosphere model as nanometers. Parameters ---------- model: str Telluric model file to load. It has columns wavelength, flux, std_flux, mask. bands: list[str] List bands to split model into separate files. new_name: str New file name base. data_dir: Optional[str] Directory for results. Can also be given in config.yaml "paths:atmmodel:"... rv_extend: float (positive) (default 100) Rv amount to extend wavelength range of telluric band. To later apply barycenter shifting. cutoff_depth: float Telluric line depth cutoff. Default = 2%. """ if (bands is None) or ("ALL" in bands): bands_ = eniric.bands["all"] else: bands_ = bands if new_name is None: new_name = model.split(".")[0] if data_dir is None: data_dir_ = eniric.paths["atmmodel"] else: data_dir_ = str(data_dir) model_name = join(data_dir_, model) # If trying to obtain the provided model extract from and it doesn't yet exist # extract from tar.gz file. (Extracted it is 230 MB which is to large for Git) if "Average_TAPAS_2014.dat" == atmmodel: if not os.path.exists(model_name): print("Unpacking Average_TAPAS_2014.dat.tar.gz...") import tarfile with tarfile.open(str(model_name) + ".tar.gz", "r") as tar: tar.extractall(data_dir_) print("Unpacked") print("Loading from_file {0}".format(model_name)) atm = Atmosphere.from_file(model_name) # Return value from saving each band write_status = np.empty_like(bands_, dtype=int) for i, band in enumerate(bands_): print("Starting {0}".format(band)) filename_band = "{0}_{1}.dat".format(new_name, band) band_min, band_max = band_limits(band) # * 1000 to convert into km/s band_min = doppler_shift_wav(band_min, -rv_extend) band_max = doppler_shift_wav(band_max, rv_extend) split_atm = atm.wave_select(band_min, band_max) # Apply telluric line mask atm.mask_transmission(depth=cutoff_depth) # Save the result to file filename = join(data_dir_, filename_band) header = ["# atm_wav(nm)", "atm_flux", "atm_std_flux", "atm_mask"] print("Saving to_file {}".format(filename)) write_status[i] = split_atm.to_file(filename, header=header, fmt="%11.8f") print("Done Splitting") return np.sum(write_status) # If any extracts fail they will turn up here.
def calculate_prec( spectral_types, bands, vsini, resolution, sampling, plot_atm=False, plot_ste=False, plot_flux=True, paper_plots=True, rv_offset=0.0, use_unshifted=False, snr=100, ref_band="J", new=True, grad=True, ): """Calculate precisions for given combinations. grad: bool Use more precise gradient function. """ # TODO: iterate over band last so that the J band normalization value can be # obtained first and applied to each band. print("using new config.yaml file here!!!!!!!!!!!!!!") results = {} # creating empty dictionary for the results wav_plot_m0 = [] # creating empty lists for the plots flux_plot_m0 = [] wav_plot_m3 = [] flux_plot_m3 = [] wav_plot_m6 = [] flux_plot_m6 = [] wav_plot_m9 = [] flux_plot_m9 = [] for band in bands: if use_unshifted: atmmodel = os.path.join( eniric.paths["atmmodel"], "{0}_{1}.dat".format(eniric.atmmodel["base"], band), ) print("Reading atmospheric model...") atm = Atmosphere.from_file(atmmodel) wav_atm, flux_atm, std_flux_atm, mask_atm = ( atm.wl, atm.transmission, atm.std, atm.mask, ) print( ( "There were {0:d} unmasked pixels out of {1:d}., or {2:.1%}." "" ).format( np.sum(mask_atm), len(mask_atm), np.sum(mask_atm) / len(mask_atm) ) ) print( "The model ranges from {0:4.2f} to {1:4.2f} micron.".format( wav_atm[0], wav_atm[-1] ) ) print("Done.") print("Calculating impact of Barycentric movement on mask...") # mask_atm = atm.old_barycenter_shift(wav_atm, mask_atm, rv_offset=rv_offset) mask_atm = barycenter_shift(wav_atm, mask_atm, rv_offset=rv_offset) else: shifted_atmmodel = os.path.join( eniric.paths["atmmodel"], "{0}_{1}_bary.dat".format(eniric.atmmodel["base"], band), ) print("Reading pre-doppler-shifted atmospheric model...") atm = Atmosphere.from_file(shifted_atmmodel) wav_atm, flux_atm, std_flux_atm, mask_atm = ( atm.wl, atm.transmission, atm.std, atm.mask, ) print("Done.") print( ("There were {0:d} unmasked pixels out of {1:d}, or {2:.1%}." "").format( np.sum(mask_atm), len(mask_atm), np.sum(mask_atm) / len(mask_atm) ) ) if plot_atm: # moved plotting code to separate code, eniric.obsolete.plotting_functions.py plt_functions.plot_atmosphere_model(wav_atm, flux_atm, mask_atm) # theoretical ratios calculation # wav_m0, flux_m0, wav_m3, flux_m3, wav_m6, flux_m6, wav_m9, flux_m9 = read_nIRspectra() iterations = itertools.product(spectral_types, vsini, resolution, sampling) # for star in spectral_types: # for vel in vsini: # for res in resolution: # for smpl in sampling: for (star, vel, res, smpl) in iterations: file_to_read = ( "Spectrum_{0}-PHOENIX-ACES_{1}band_vsini{2}_R{3}" "_res{4:2.01f}.dat" ).format(star, band, vel, res, float(smpl)) # print("Working on "+file_to_read+".") try: wav_stellar, flux_stellar = io.pdread_2col( os.path.join(eniric.paths["resampled"], file_to_read) ) except FileNotFoundError: # Turn list of strings into strings without symbols ["J", "K"] -> J K spectral_str = re.sub(r"[\[\]\"\',]", "", str(spectral_types)) band_str = re.sub(r"[\[\]\"\',]", "", str(bands)) vsini_str = re.sub(r"[\[\]\"\',]", "", str(vsini)) res_str = re.sub(r"[\[\]\"\',]", "", str(resolution)) sampling_str = re.sub(r"[\[\]\"\',]", "", str(sampling)) print( ( "\nFor just this file I suggest you run\n\tpython nIR_run.py -s {0} -b {1} -v {2} -R {3} " "--sample_rate {4}\nOr for all the combinations you ran here\n\tpython nIR_run.py -s {5}" " -b {6} -v {7} -R {8} --sample_rate {9}" "" ).format( star, band, vel, res, smpl, spectral_str, band_str, vsini_str, res_str, sampling_str, ) ) raise if len(wav_stellar) == 0 or len(flux_stellar) == 0: raise Exception("The file {0} is empty".format(file_to_read)) # Removing boundary effects wav_stellar = wav_stellar[2:-2] flux_stellar = flux_stellar[2:-2] # sample was left aside because only one value existed # TODO: Add metallicity and logg into id string id_string = "{0:s}-{1:s}-{2:.1f}-{3:s}".format(star, band, float(vel), res) # Getting the wav, flux and mask values from the atm model # that are the closest to the stellar wav values, see # https://stackoverflow.com/questions/2566412/find-nearest-value-in-numpy-array index_atm = np.searchsorted(wav_atm, wav_stellar) # replace indexes outside the array, at the very end, by the value at the very end # index_atm = [index if(index < len(wav_atm)) else len(wav_atm)-1 for index in index_atm] indx_mask = index_atm >= len(wav_atm) # find broken indexs index_atm[indx_mask] = len(wav_atm) - 1 # replace with index of end. wav_atm_selected = wav_atm[index_atm] flux_atm_selected = flux_atm[index_atm] mask_atm_selected = mask_atm[index_atm] # Check mask masks out deep atmosphere absorption if np.any(flux_atm_selected[mask_atm_selected] < 0.98): print( "####WARNGING####\nThis absorption mask does not mask out deep atmosphere transmission!" ) print( "Min flux_atm_selected[mask_atm_selected] = {} < 0.98\n####".format( np.min(flux_atm_selected[mask_atm_selected]) ) ) # Normalize to SNR 100 in middle of J band 1.25 micron! # flux_stellar = normalize_flux(flux_stellar, id_string) # flux_stellar = snrnorm.normalize_flux(flux_stellar, id_string, new=True) # snr=100, ref_band="J" flux_stellar = eniric.obsolete.snr_norm.normalize_flux( flux_stellar, id_string, new=new, snr=snr, ref_band=ref_band, sampling=smpl, ) if id_string in [ "M0-J-1.0-100k", "M3-J-1.0-100k", "M6-J-1.0-100k", "M9-J-1.0-100k", ]: index_ref = np.searchsorted( wav_stellar, 1.25 ) # searching for the index closer to 1.25 micron snr_estimate = np.sqrt( np.sum(flux_stellar[index_ref - 1 : index_ref + 2]) ) print( "\tSanity Check: The S/N for the {0:s} reference model was of {1:4.2f}.".format( id_string, snr_estimate ) ) elif "J" in id_string: index_ref = np.searchsorted( wav_stellar, 1.25 ) # searching for the index closer to 1.25 micron snr_estimate = np.sqrt( np.sum(flux_stellar[index_ref - 1 : index_ref + 2]) ) print( "\tSanity Check: The S/N for the {0:s} non-reference model was of {1:4.2f}.".format( id_string, snr_estimate ) ) # Precision given by the first method: print("Performing analysis for: ", id_string) prec_1 = Qcalculator.rv_precision(wav_stellar, flux_stellar, grad=grad) # Precision as given by the second_method wav_stellar_chunks, flux_stellar_chunks = eniric.legacy.mask_clumping( wav_stellar, flux_stellar, mask_atm_selected ) prec_2_old = eniric.legacy.RVprec_calc_masked( wav_stellar_chunks, flux_stellar_chunks, grad=grad ) prec_2 = eniric.legacy.RVprec_calc_masked( wav_stellar, flux_stellar, mask_atm_selected, grad=grad ) assert np.all(prec_2_old == prec_2) """ # histogram checking lengths = [len(chunk) for chunk in flux_stellar_chunks_unformatted] n, bins, patches = plt.hist(lengths, 500, range=[0.5, 500.5], histtype='stepfilled') plt.title(id_string) plt.show() """ # Precision as given by the third_method prec_3 = Qcalculator.rv_precision( wav_stellar, flux_stellar, mask=flux_atm_selected ** 2, grad=grad ) # Adding Precision results to the dictionary results[id_string] = [prec_1, prec_2, prec_3] # Prepare/Do for the plotting. if plot_ste or plot_ste == id_string: plt_functions.plot_stellar_spectum( wav_stellar, flux_stellar, wav_atm_selected, mask_atm_selected ) plot_ids = [ "M3-Z-1.0-100k", "M3-Y-1.0-100k", "M3-J-1.0-100k", "M3-H-1.0-100k", "M3-K-1.0-100k", ] if plot_flux and id_string in plot_ids: wav_plot_m0.append(wav_stellar) flux_plot_m0.append(flux_stellar) if plot_flux and id_string in plot_ids: wav_plot_m3.append(wav_stellar) flux_plot_m3.append(flux_stellar) if plot_flux and id_string in plot_ids: wav_plot_m6.append(wav_stellar) flux_plot_m6.append(flux_stellar) if plot_flux and id_string in plot_ids: wav_plot_m9.append(wav_stellar) flux_plot_m9.append(flux_stellar) if plot_flux: plt_functions.plot_nIR_flux() if paper_plots: plt_functions.plot_paper_plots() else: return results
def test_atmosphere_from_file(atm_model): atmos = Atmosphere.from_file(atmmodel=atm_model) assert len(atmos.wl) == len(atmos.transmission) assert len(atmos.transmission[atmos.mask]) != len( atmos.transmission) # mask is not all ones