def extract_spectrum(input: str, output: str, start: float, end: float): """Extracts the given wavelength range from input and store it as output. Args: input: Input filename output: Output filename start: Wavelength start end: Wavelength end """ # load spectrum with FitsSpectrum(input) as fs_in: # open spectrum to write with FitsSpectrum(output, 'w') as fs_out: # copy extracted spectrum fs_out.spectrum = fs_in.spectrum.extract(start, end) # loop all extensions: for ext in fs_in.hdu_names(): # skip no name if ext.strip() == '': continue # try to get hdu try: # get hdu hdu = fs_in[ext] # need to create new hdu with primary=False, since that gets lost on extract fs_out[ext] = SpectrumFitsHDU(spec=hdu.extract(start, end), primary=False) except ValueError: pass
def _load_spectrum(self, filename: str) -> (Spectrum, np.ndarray): """Loads the given spectrum and its uncertainties and creates a mask. Args: filename: Name of file to load. Returns: Tuple of Spectrum and mask of valid pixels """ # open file self.log.info("Loading file {0:s}.".format(filename)) with FitsSpectrum(filename) as fs: # get spectrum self._spec = fs.spectrum # mask of good pixels self._valid = fs.good_pixels.astype(np.bool) # mask all NaNs self._valid &= ~np.isnan(self._spec.flux) & ~np.isnan( self._spec.wave) # add other masks for mask in self._masks: self._valid &= mask(fs.spectrum, filename=fs.filename)
def plot(spectra: list, output: str = None, results: bool = False, range: list = None, **kwargs): # if spectra not a list, make it a list if not isinstance(spectra, list): spectra = [spectra] # check output pdf = None if output: # want a PDF? if output.endswith('.pdf'): pdf = PdfPages(output) else: # if no pdf, we only allow for one spectrum to plot if len(spectra) > 1: raise ValueError( 'Plotting into a file other than a PDF works for a single spectrum only!' ) # loop spectra for filename in sorted(spectra): # load spectrum with FitsSpectrum(filename) as fs: # get spectrum spec = fs[ 'NORMALIZED'] if 'NORMALIZED' in fs and results else fs.spectrum # and model model = fs.best_fit if results else None # get residuals residuals = fs.residuals if results else None # good pixels valid = fs.good_pixels # plot plot_spectrum(spec, model, residuals, valid, wave_range=range, title=fs.filename) # show if output: pdf.savefig(papertype='a4', orientation='landscape') plt.close() else: plt.show() # close file if output: pdf.close()
def write_results_to_file(self, fits_file: FitsSpectrum): """Write results of this component into a given SpectrumFile Args: fits_file: Opened FitsSpectrum to write results into """ # get results object params = fits_file.results(self.prefix) # loop parameter names for name, param in self.parameters.items(): # write results into results object vary = 'vary' not in param or param['vary'] is True params[name] = [param['value'], param['stderr'] if vary else None]
def _write_results_to_file(self, filename: str, result: MinimizerResult, best_fit: Spectrum, stats: dict): """Writes results of fit back to file. Args: filename: Name of file to write results into. result: Result from optimization. best_fit: Best fit model. stats: Fit statistics. """ # Write fits results back to file self.log.info("Writing results to file.") with FitsSpectrum(filename, 'rw') as fs: # stats res = fs.results('SPEXXY') for x in stats: res[x] = stats[x] # loop all components for cmp in self._cmps: # write results cmp.write_results_to_file(fs) # tellurics if self._tellurics is not None: # molecular abundances self._tellurics.write_results_to_file(fs) # weights weights = fs.results("WEIGHTS") for cmp in self._cmps: weights[cmp.prefix] = cmp.weight # write spectra best fit, good pixels mask, residuals and # multiplicative polynomial if best_fit is not None: fs.best_fit = best_fit fs.residuals = self._spec.flux - best_fit.flux fs.good_pixels = self._valid fs.mult_poly = self._mult_poly.values # loop all components again to add spectra for cmp in self._cmps: # get spectrum tmp = cmp() tmp.mode(self._spec.wave_mode) tmp = tmp.resample(spec=self._spec) cmpspec = SpectrumFitsHDU(spec=tmp, primary=False) # set it fs['CMP_' + cmp.name] = cmpspec # tellurics spectrum if self._tellurics is not None: tmp = self._tellurics() tmp.mode(self._spec.wave_mode) tmp = tmp.resample(spec=self._spec) tell = SpectrumFitsHDU(spec=tmp, primary=False) # set it fs['TELLURICS'] = tell # covariance if hasattr(result, 'covar'): fs.covar = result.covar
def run(args): """ Takes a list of spectra and calculates the mean of the fitted tellurics. :param args: argparse namespace with .spectra: List of files containing spectra. .output: Output file for mean tellurics (default: tellurics.fits) .snlimit: Only calculate mean tellurics for spectra with a S/N higher than the given number. .weight: If set, tellurics are weightes by the S/N of their spectra. """ # get all spectra filenames = [] for s in args.spectra: if '*' in s: filenames.extend(glob.glob(s)) else: filenames.append(s) # read headers logging.info('Reading all FITS headers...') headers = bulk_read_header(filenames, ['HIERARCH SPECTRUM SNRATIO']) # get all snr values logging.info('Extracting all S/N values...') snrs = {} for i, row in headers.iterrows(): try: snrs[row['FILE']] = float(row['HIERARCH SPECTRUM SNRATIO']) except ValueError: continue # no snlimit given? if args.snlimit is not None: snlimit = args.snlimit else: tmp = sorted(snrs.values()) logging.info('Calculating S/N limit from %.2f%% highest S/N values...', args.snfrac) snlimit = tmp[-int(len(tmp) * args.snfrac / 100)] logging.info('Using S/N limit of %.2f...', snlimit) # filter by snlimit logging.info('Filtering spectra by S/N limit...') spectra = [filename for filename, snr in snrs.items() if snr > snlimit] # tellurics spectrum tellurics = None weights = None count = None wave_start, wave_step, wave_count = args.resample if wave_count is not None: wave_count = int(wave_count) # loop files logging.info('Processing %d spectra...', len(spectra)) for i, spec in enumerate(spectra, 1): # open file with FitsSpectrum(spec, 'r') as fs: # get signal to noise snr = fs.header["HIERARCH SPECTRUM SNRATIO"] # print logging.info('(%d/%d) %s %-5.2f', i, len(spectra), spec, snr) # weight weight = snr if args.weight else 1. # some more info if wave_start is None: # WAVE extension or CRVAL/CDELT? if 'CRVAL1' in fs.header and 'CDELT1' in fs.header: wave_start = fs.header["CRVAL1"] wave_step = fs.header["CDELT1"] if fs.header['CUNIT1'] == 'm': wave_start *= 1e10 wave_step *= 1e10 wave_count = fs.header['NAXIS1'] elif 'WAVE' in fs.header and fs.header['WAVE'] in fs: logging.error( 'Combining tellurics on PIXTABLE spectra not allowed without resampling.' ) continue else: logging.error('Could not determine wavelength grid.') continue # get tellurics tell = fs.tellurics if not tell: continue # resample tell = tell.resample(wave_start=wave_start, wave_step=wave_step, wave_count=wave_count) # tellurics exist? on first iteration we create the array. if tellurics is None: tellurics = np.zeros((wave_count)) weights = np.zeros((wave_count)) count = np.zeros((wave_count)) # add to sum w = np.where(~np.isnan(tell.flux)) tellurics[w] += weight * tell.flux[w] weights[w] += weight count[w] += 1 # divide by sum w = np.where(~np.isnan(tellurics) & ~np.isnan(weights)) tellurics[w] /= weights[w] # create spectrum for tellurics and save it tell_spec = SpectrumFits(flux=tellurics, wave_start=wave_start, wave_step=wave_step, primary=True) tell_spec.save(args.output) # output logging.info("Finished successfully.")