if __name__ == "__main__": # Initialize fitter fitter = TelluricFitter(debug=False) #Set the observatory location with a keyword fitter.SetObservatory("McDonald") #Can also give a python dictionary like below #observatory = {"latitude": 30.5, # "altitude": 2.5} #fitter.SetObservatory(observatory) #Read in the spectrum and store as an xypoint x, y, c, e = numpy.loadtxt("Spectrum.dat", unpack=True) data = DataStructures.xypoint(x=x*u.angstrom, y=y, cont=c, err=e) #The following are parameters that should be in the fits header for an observation angle = 37.4 #Zenith distance pressure = 796.8 #Pressure, in hPa humidity = 54.0 #Percent humidity, at the observatory altitude temperature = 292.5 #Temperature in Kelvin resolution = 60000.0 #Resolution lambda/delta-lambda #Define variables to be fit, and give initial guesses. # We will just fit the relative humidity in this example. fitter.FitVariable({"h2o": humidity}) #Adjust parameters that will not be fit, but are important
def telfitter(watm_in, satm_in, a0ucut, inparam, night, order, args, masterbeam, logger): ''' Produce synthetic telluric template from fit to telluric standard observation. How and why it works is detailed in comments throughout the code. Inputs: watm_in : Wavelength scale of telluric standard spectrum satm_in : Corresponding flux of telluric standard spectrum a0ucut : Corresponding uncertainty of telluric standard spectrum inparam : Class containing variety of information (e.g. on observing conditions) night : Date of observation in YYYYMMDD order : Echelle order, as characterized by file index (as opposed to m number; for conversion between the two, see Stahl et al. 2021) args : Information as input by user from command line masterbeam : A or B frame Outputs: wavefitted : Wavelength scale of synethetic telluric spectrum satmTel : Corresponding flux of synthetic telluric spectrum names : Descriptors of Telfit parameters parfitted : Values of best-fit Telfit parameters wcont1 : Wavelength scale corresponding to best-fit continuum (from intermediate Telfit step) cont1 : Flux corresponding to best-fit continuum (from intermediate Telfit step) ''' os.environ['PYSYN_CDBS'] = inparam.cdbsloc fitter = TelluricFitter(debug=False, print_lblrtm_output=args.debug) #Set the observatory location with a keyword DCT_props = {"latitude": 34.744, "altitude": 2.36} #altitude in km McD_props = {"latitude": 30.710, "altitude": 2.07} GaminiS_props = {"latitude": -30.241, "altitude": 2.72} if inparam.obses[night] == 'DCT': fitter.SetObservatory(DCT_props) elif inparam.obses[night] == 'McD': fitter.SetObservatory(McD_props) elif inparam.obses[night] == 'GeminiS': fitter.SetObservatory(GaminiS_props) else: sys.exit( 'TELFIT OBSERVATORY ERROR, OLNY SUPPORT DCT, McD & GeminiS IN THIS VERSION!' ) # Read in data watm_in = watm_in / 10 # AA --> nm data = DataStructures.xypoint(x=watm_in, y=satm_in, cont=None, err=a0ucut) # input wavelength in nm # DCT data has parameters describing night of observation that the McDonald data does not. if inparam.temps[night] != 'NOINFO': # If such information is available: angle = np.float(inparam.zds[night]) #Zenith distance pressure = np.float(inparam.press[night]) #Pressure, in hPa humidity = np.float(inparam.humids[night] ) #Percent humidity, at the observatory altitude temperature = np.float( inparam.temps[night]) + 273.15 #Temperature in Kelvin if (order <= 4): resolution = 55000.0 #Resolution lambda/delta-lambda else: resolution = 45000.0 #Resolution lambda/delta-lambda # Ideally, we'd fit resolution as well since that varies across the detector. # But in practice the Telfit's resolution fits often diverge to unphysically high values. # Ultimately, we only want accurate estimates for the chemical compositions, which are unaffacted # by fixing the resolution at 45000. The final telluric template we'll be synthesizing from this # will be at a set high resolution, anyway, and when we need/have needed to estimate resolution # across the detector, we (have) done so via the fits of the telluric and stellar templates to the # observed A0 and GJ281 spectra. # Only 3 molecules present in chosen IGRINS orders' wavelength range are H2O, CH4, and CO. if (3 < order < 9) & (args.band == 'K'): num_fit = 4 # Only 3 molecules present in chosen IGRINS orders' wavelength range are H2O, CH4, and CO. fitter.FitVariable({ "h2o": humidity, "ch4": 1.8, "co": 5e-3, "n2o": 5e-2 }) #Adjust parameters that will not be fit, but are important fitter.AdjustValue({"angle": angle,\ "pressure": pressure,\ "temperature": temperature,\ "resolution": resolution, "wavestart": data.x[0]-0.001,\ "waveend": data.x[-1]+0.001,\ "co2": 3.675e2,\ "o3": 7.6e-4,\ "o2": 2.1e5,\ "no": 0.,\ "so2": 5e-9,\ "no2": 5e-9,\ "nh3": 5e-9,\ "hno3": 1.56e-7}) #Set bounds on the variables being fit fitter.SetBounds({"h2o": [1.0, 99.0],\ "ch4": [.1, 10.0],\ "n2o": [1e-5,1e2],\ "co": [ 1e-6,1e2]}) elif (order >= 9 or order <= 3) & (args.band == 'K'): num_fit = 4 # Only molecules present in chosen IGRINS orders' wavelength range are H2O, CH4, N2O, and CO2. fitter.FitVariable({ "h2o": humidity, "ch4": 1.8, "co2": 3.675e2, "n2o": 5e-2 }) #Adjust parameters that will not be fit, but are important fitter.AdjustValue({"angle": angle,\ "pressure": pressure,\ "temperature": temperature,\ "resolution": resolution, "wavestart": data.x[0]-0.001,\ "waveend": data.x[-1]+0.001,\ "co": 5e-3,\ "o3": 7.6e-4,\ "o2": 2.1e5,\ "no": 0.,\ "so2": 5e-9,\ "no2": 5e-9,\ "nh3": 5e-9,\ "hno3": 1.56e-7}) #Set bounds on the variables being fit fitter.SetBounds({"h2o": [1.0, 99.0],\ "ch4": [.1, 10.0],\ "n2o": [1e-5,1e2],\ "co2": [1.0, 1e4]}) elif args.band == 'H': num_fit = 3 fitter.FitVariable({ "h2o": humidity, "ch4": 1.8, "co": 5e-3, "co2": 3.675e2, "n2o": 5e-2 }) #Adjust parameters that will not be fit, but are important fitter.AdjustValue({"angle": angle,\ "pressure": pressure,\ "temperature": temperature,\ "resolution": resolution, "wavestart": data.x[0]-0.001,\ "waveend": data.x[-1]+0.001,\ "o3": 7.6e-4,\ "o2": 2.1e5,\ "no": 0.,\ "so2": 5e-9,\ "no2": 5e-9,\ "nh3": 5e-9,\ "hno3": 1.56e-7}) #Set bounds on the variables being fit fitter.SetBounds({"h2o": [1.0, 99.0],\ "ch4": [.1, 10.0],\ "n2o": [1e-6,1e2],\ "co": [1e-6,1e2],\ "co2": [1.0, 1e4]}) elif inparam.zds[ night] != 'NOINFO': # If GeminiS data, some but not all parameters are in fits file. # If parameters are not in fits file, use initial guesses and letting them vary. # Guesses are taken from mean of parameters from DCT GJ281 data. angle = np.float(inparam.zds[night]) #Zenith distance humidity = np.float(inparam.humids[night] ) #Percent humidity, at the observatory altitude if (order <= 4): resolution = 55000.0 #Resolution lambda/delta-lambda else: resolution = 45000.0 #Resolution lambda/delta-lambda # Only 3 molecules present in chosen IGRINS orders' wavelength range are H2O, CH4, and CO. if (3 < order < 9) & (args.band == 'K'): num_fit = 4 # Only 3 molecules present in chosen IGRINS orders' wavelength range are H2O, CH4, and CO. fitter.FitVariable({ "h2o": humidity, "ch4": 1.8, "co": 5e-3, "n2o": 5e-2, "pressure": 1023., "temperature": 280.87 }) #Adjust parameters that will not be fit, but are important fitter.AdjustValue({"angle": angle,\ "resolution": resolution, "wavestart": data.x[0]-0.001,\ "waveend": data.x[-1]+0.001,\ "co2": 3.675e2,\ "o3": 7.6e-4,\ "o2": 2.1e5,\ "no": 0.,\ "so2": 5e-9,\ "no2": 5e-9,\ "nh3": 5e-9,\ "hno3": 1.56e-7}) #Set bounds on the variables being fit fitter.SetBounds({"h2o": [1.0, 99.0],\ "ch4": [.1, 10.0],\ "n2o": [1e-5,1e2],\ "temperature": [265.,300.],\ "pressure": [1010.,1035.],\ "co": [ 1e-6,1e2]}) elif (order >= 9 or order <= 3) & (args.band == 'K'): num_fit = 4 # Only molecules present in chosen IGRINS orders' wavelength range are H2O, CH4, N2O, and CO2. fitter.FitVariable({ "h2o": humidity, "ch4": 1.8, "co2": 3.675e2, "n2o": 5e-2, "pressure": 1023., "temperature": 280.87 }) #Adjust parameters that will not be fit, but are important fitter.AdjustValue({"angle": angle,\ "resolution": resolution, "wavestart": data.x[0]-0.001,\ "waveend": data.x[-1]+0.001,\ "co": 5e-3,\ "o3": 7.6e-4,\ "o2": 2.1e5,\ "no": 0.,\ "so2": 5e-9,\ "no2": 5e-9,\ "nh3": 5e-9,\ "hno3": 1.56e-7}) #Set bounds on the variables being fit fitter.SetBounds({"h2o": [1.0, 99.0],\ "ch4": [.1, 10.0],\ "n2o": [1e-5,1e2],\ "temperature": [265.,300.],\ "pressure": [1010.,1035.],\ "co2": [1.0, 1e4]}) elif args.band == 'H': num_fit = 3 fitter.FitVariable({ "h2o": humidity, "ch4": 1.8, "co": 5e-3, "co2": 3.675e2, "n2o": 5e-2, "pressure": 1023., "temperature": 280.87 }) #Adjust parameters that will not be fit, but are important fitter.AdjustValue({"angle": angle,\ "resolution": resolution, "wavestart": data.x[0]-0.001,\ "waveend": data.x[-1]+0.001,\ "o3": 7.6e-4,\ "o2": 2.1e5,\ "no": 0.,\ "so2": 5e-9,\ "no2": 5e-9,\ "nh3": 5e-9,\ "hno3": 1.56e-7}) #Set bounds on the variables being fit fitter.SetBounds({"h2o": [1.0, 99.0],\ "ch4": [.1, 10.0],\ "n2o": [1e-6,1e2],\ "co": [1e-6,1e2],\ "temperature": [265.,300.],\ "pressure": [1010.,1035.],\ "co2": [1.0, 1e4]}) else: # If parameters are not in fits file, use initial guesses and letting them vary. # Guesses are taken from mean of parameters from DCT GJ281 data. if (order <= 4): resolution = 55000.0 #Resolution lambda/delta-lambda else: resolution = 45000.0 #Resolution lambda/delta-lambda # Only 3 molecules present in chosen IGRINS orders' wavelength range are H2O, CH4, and CO. if (3 < order < 9) & (args.band == 'K'): num_fit = 6 fitter.FitVariable({ "h2o": 43., "ch4": 1.8, "co": 5e-3, "n2o": 5e-2, "angle": 39., "pressure": 1023., "temperature": 280.87 }) #Adjust parameters that will not be fit, but are important fitter.AdjustValue({"resolution": resolution,\ "wavestart": data.x[0]-0.001,\ "waveend": data.x[-1]+0.001,\ "co2": 3.675e2,\ "o3": 7.6e-4,\ "o2": 2.1e5,\ "no": 0.,\ "so2": 5e-9,\ "no2": 5e-9,\ "nh3": 5e-9,\ "hno3": 1.56e-7}) #Set bounds on the variables being fit fitter.SetBounds({"h2o": [1.0, 99.0],\ "ch4": [.1,10.0],\ "n2o": [1e-5,1e2],\ "temperature": [265.,300.],\ "angle": [1.,75.],\ "pressure": [1010.,1035.],\ "co": [ 1e-6,1e2]}) elif (order >= 9 or order <= 3) & (args.band == 'K'): num_fit = 7 # Only molecules present in chosen IGRINS orders' wavelength range are H2O, CH4, N2O, and CO2. fitter.FitVariable({ "h2o": 43., "ch4": 1.8, "co2": 3.675e2, "n2o": 5e-2, "angle": 39., "pressure": 1023., "temperature": 280.87 }) #Adjust parameters that will not be fit, but are important fitter.AdjustValue({"resolution": resolution,\ "wavestart": data.x[0]-0.001,\ "waveend": data.x[-1]+0.001,\ "co": 5e-3,\ "o3": 7.6e-4,\ "o2": 2.1e5,\ "no": 0.,\ "so2": 5e-9,\ "no2": 5e-9,\ "nh3": 5e-9,\ "hno3": 1.56e-7}) #Set bounds on the variables being fit fitter.SetBounds({"h2o": [1.0, 99.0],\ "ch4": [.1,10.0],\ "temperature": [265.,300.],\ "angle": [1.,75.],\ "n2o":[1e-5,1e2],\ "pressure": [1010.,1035.],\ "co2": [1.0, 1e4]}) elif args.band == 'H': num_fit = 6 fitter.FitVariable({ "h2o": 43., "ch4": 1.8, "co": 5e-3, "co2": 3.675e2, "n2o": 5e-2, "angle": 39., "pressure": 1023., "temperature": 280.87 }) #Adjust parameters that will not be fit, but are important fitter.AdjustValue({"resolution": resolution,\ "wavestart": data.x[0]-0.001,\ "waveend": data.x[-1]+0.001,\ "o3": 7.6e-4,\ "o2": 2.1e5,\ "no": 0.,\ "so2": 5e-9,\ "no2": 5e-9,\ "nh3": 5e-9,\ "hno3": 1.56e-7}) #Set bounds on the variables being fit fitter.SetBounds({"h2o": [1.0, 99.0],\ "ch4": [.1,10.0],\ "n2o": [1e-6,1e2],\ "co": [1e-6,1e2],\ "temperature": [265.,300.],\ "angle": [1.,75.],\ "pressure": [1010.,1035.],\ "co2": [ 1,1e4]}) try: if args.debug: model = fitter.Fit(data=data, resolution_fit_mode="SVD", adjust_wave="model", air_wave=False) else: model = suppress_Fit(fitter, data) except TypeError: return [np.nan], [np.nan], [np.nan], [np.nan], [np.nan], [np.nan] ''' resolution_fit_mode = SVD ought to give faster, more accurate fits for the deep telluric lines we mostly see in K band air_wave = False because data in vacuum wavelengths adjust_wave = From Telfit comments: "Can be set to either 'data' or 'model'. To wavelength calibrate the data to the telluric lines, set to 'data'. If you think the wavelength calibration is good on the data (such as Th-Ar lines in the optical), then set to 'model' Note that currently, the vacuum --> air conversion for the telluric model is done in a very approximate sense, so adjusting the data wavelengths may introduce a small (few km/s) offset from what it should be. That is fine for relative RVs, but probably not for absolute RVs." As it turns out, the model internal to Telfit is still not very precise in wavelength space, since it relies on the HITRAN database. Some lines are accurate to 1 m/s, but some less so. Hence, our procedure is as follows: 1) Fit the A0 spectrum using the Livingston telluric template. Get out a precisely calibrated wavelength solution for the spectrum. (This is done in A0Fitter, not Telfitter) 2) Use that wavelength solution was input for Telfit, and let the wavelength scale of the model vary with respect to it. 3) Using Telfit's best fit parameters, generate a telluric template at high resolution. Note: Telfit's default when generating a model is to employ a vacuum/air conversion. In order to avoid that, I have manually edited one of Telfit's files. 4) To properly calibrate this template in wavelength space, we fit it to a Telfit'd version of the Livingston telluric template, only allowing the wavelength solution to vary. ''' #Get the improved continuum from the fitter cont1 = fitter.data.cont wcont1 = model.x * 10 # nm-->AA # chi_new = np.sum((satm_in - model.y*cont1)**2. / model.u**2.) # chi_new = chisq / (len(model.y) - num_fit) if args.plotfigs: fig, axes = plt.subplots(1, 1, figsize=(6, 3), facecolor='white', dpi=300) axes.plot(10 * watm_in, satm_in, color='black', alpha=.8, label='data', lw=0.7) axes.plot(10 * model.x, model.y * cont1, color='tab:red', alpha=.8, label='model fit', lw=0.7) axes.plot(10 * model.x, cont1, color='tab:blue', alpha=.8, label='blaze fit', lw=0.7) axes.xaxis.set_minor_locator(AutoMinorLocator(5)) axes.yaxis.set_minor_locator(AutoMinorLocator(2)) axes.tick_params(axis='both', which='both', labelsize=6, right=True, top=True, direction='in') axes.set_ylabel(r'Flux', size=6, style='normal', family='sans-serif') axes.set_xlabel(r'Wavelength [$\AA$]', size=6, style='normal', family='sans-serif') axes.legend(fontsize=5, edgecolor='white') axes.set_title('A0Telfit_Order{}_{}_{}.png'.format( order, night, masterbeam), size=6, style='normal', family='sans-serif') # fig.text(0.65, 0.2, r'$\rm \chi^{{2}}_{{\nu}}$ = {:1.2f}'.format(chi_new), # size=6, style='normal', family='sans-serif') fig.savefig('{}/figs_{}/A0Telfit_Order{}_{}_{}.png'.format( inparam.outpath, args.band, order, night, masterbeam), format='png', bbox_inches='tight', overwrite=True) ############### Generate template with these parameters but at higher resolution names = [ "pressure", "temperature", "angle", "resolution", 'wavestart', 'waveend', "h2o", "co2", "o3", "n2o", "co", "ch4", "o2", "no", "so2", "no2", "nh3", "hno3" ] parfitted = np.ones_like(names, dtype=float) for k in range(len(names)): parfitted[k] = np.float(fitter.GetValue(names[k])) fitter2 = TelluricFitter(debug=False, print_lblrtm_output=args.debug) if inparam.obses[night] == 'DCT': fitter2.SetObservatory(DCT_props) elif inparam.obses[night] == 'McD': fitter2.SetObservatory(McD_props) elif inparam.obses[night] == 'GeminiS': fitter2.SetObservatory(GaminiS_props) # Compute telluric template with highest resolution of Livingston template. # Add extra space at ends to make sure template covers wider range than data. Livingston_minimum_wsep = .035 / 10 IGRINS_minimum_wsep = .130 # <-- This would compute template with IGRINS resolution, sensibly coarser than Livingston newwave = np.arange( np.min(watm_in) - 2.5, np.max(watm_in) + 2.5, Livingston_minimum_wsep) #in nm data2 = DataStructures.xypoint(x=newwave, y=None, cont=None, err=None) params = {} for k in range(len(names)): params[names[k]] = np.float(parfitted[k]) params['wavestart'] = data2.x[0] - 0.001 params['waveend'] = data2.x[-1] + 0.001 fitter2.AdjustValue(params) fitter2.ImportData(data2) # Call the modeller. On rare occasions, this returns an error. I have no idea what is causing this error, as the # FORTRAN readout is quite unhelpful and anyone else who apepars to have experienced this problem had it randomly go away at some point. # If this happens, simply deliver NAN arrays, and in later parts of the RV analysis A0 fits from the nearest compatible observation will be used. try: if args.debug: model2 = fitter2.GenerateModel(parfitted, nofit=True, air_wave=False) else: model2 = suppress_GenerateModel(fitter2, parfitted, args) except TypeError: return [np.nan], [np.nan], [np.nan], [np.nan], [np.nan], [np.nan] watm_save = watm_in.copy() satm_save = satm_in.copy() newwave1 = newwave[(newwave > watm_in[0] - 1.0) & (newwave < watm_in[-1] + 1.0)] # Parameters for reproducing Livingston template with Telfit if args.band == 'K': telparsdict = { '1': np.array([ 1.01469894e+03, 2.86278974e+02, 2.57160505e+01, 6.00000000e+05, 2.42400187e+03, 2.50999640e+03, 1.44631214e+01, 3.67500000e+02, 7.60000000e-04, 5.00000000e-02, 7.06751837e+00, 2.21814724e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '2': np.array([ 1.01000565e+03, 2.86022221e+02, 1.02518785e+01, 6.00000000e+05, 2.39100388e+03, 2.47599903e+03, 1.60984192e+01, 3.67500000e+02, 7.60000000e-04, 5.00000000e-02, 9.51814513e-02, 2.80273169e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '3': np.array([ 1.01208206e+03, 2.83372443e+02, 1.30994741e+01, 6.00000000e+05, 2.36000083e+03, 2.44399739e+03, 2.03888166e+01, 3.67500000e+02, 7.60000000e-04, 5.00000000e-02, 1.78694998e-01, 2.63207260e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '4': np.array([ 1.01044914e+03, 3.09376975e+02, 5.00457250e+01, 6.00000000e+05, 2.32700464e+03, 2.41199998e+03, 2.23826025e+00, 3.67500000e+02, 7.60000000e-04, 5.00000000e-02, 1.44749523e-01, 1.78797014e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '5': np.array([ 1.01028238e+03, 2.80000941e+02, 5.02034212e+01, 6.00000000e+05, 2.29700026e+03, 2.38099471e+03, 2.00001482e+01, 3.67500000e+02, 7.60000000e-04, 5.00000000e-02, 5.04544799e-03, 1.79978608e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '6': np.array([ 1.01003245e+03, 2.82986081e+02, 4.69226227e+01, 6.00000000e+05, 2.26700430e+03, 2.35099941e+03, 1.83825870e+01, 3.67500000e+02, 7.60000000e-04, 5.00000000e-02, 1.33618090e-01, 1.75621792e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '7': np.array([ 1.01676398e+03, 2.80004945e+02, 5.00497774e+01, 6.00000000e+05, 2.23800235e+03, 2.32199758e+03, 1.99995159e+01, 3.67500000e+02, 7.60000000e-04, 5.00000000e-02, 5.00089318e-03, 1.79986683e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '8': np.array([ 1.02000000e+03, 2.80000000e+02, 5.00000000e+01, 6.00000000e+05, 2.21000474e+03, 2.29299818e+03, 2.00000000e+01, 3.67500000e+02, 7.60000000e-04, 5.00000000e-02, 5.00000000e-03, 1.80000000e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '9': np.array([ 1.01010028e+03, 2.83521347e+02, 4.94584722e+01, 6.00000000e+05, 2.20299994e+03, 2.24499864e+03, 1.62628503e+01, 2.09026148e+02, 7.60000000e-04, 5.00000000e-02, 5.00000000e-03, 1.84204544e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '10': np.array([ 1.02057631e+03, 2.80065296e+02, 4.97608855e+01, 6.00000000e+05, 2.17600055e+03, 2.21800093e+03, 1.99862859e+01, 3.75841930e+02, 7.60000000e-04, 5.00000000e-02, 5.00000000e-03, 1.77986802e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '11': np.array([ 1.01175621e+03, 2.80258981e+02, 5.01757000e+01, 6.00000000e+05, 2.15000090e+03, 2.19200022e+03, 1.94041560e+01, 3.17393616e+02, 7.60000000e-04, 1.54427423e-01, 5.00000000e-03, 1.69487644e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '12': np.array([ 1.01939759e+03, 3.03543029e+02, 5.16166925e+01, 6.00000000e+05, 2.12399934e+03, 2.16599710e+03, 4.88527175e+00, 3.62082287e+02, 7.60000000e-04, 4.24872185e-01, 5.00000000e-03, 1.76259774e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '13': np.array([ 1.01289532e+03, 2.97875731e+02, 3.68795863e+01, 6.00000000e+05, 2.09899925e+03, 2.14099888e+03, 8.74545147e+00, 4.70227887e+02, 7.60000000e-04, 4.02655042e-01, 5.00000000e-03, 4.78062748e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '14': np.array([ 1.01088512e+03, 2.82651527e+02, 4.97893186e+01, 6.00000000e+05, 2.07500079e+03, 2.11599814e+03, 1.70464824e+01, 3.75172445e+02, 7.60000000e-04, 3.21242442e-01, 5.00000000e-03, 1.80000000e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '15': np.array([ 1.01015623e+03, 3.09948799e+02, 3.32690854e+01, 6.00000000e+05, 2.05100270e+03, 2.09199736e+03, 4.38724419e+00, 3.98166105e+02, 7.60000000e-04, 4.99995596e-02, 5.00000000e-03, 1.79687720e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '16': np.array([ 1.01020405e+03, 2.99035342e+02, 6.85450975e+01, 6.00000000e+05, 2.02800148e+03, 2.06800000e+03, 3.16442794e+00, 1.73470928e+02, 7.60000000e-04, 5.00000000e-02, 5.00000000e-03, 1.80000000e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), } elif args.band == 'H': telparsdict = { '1': np.array([ 1.01099787e+03, 2.77972664e+02, 1.74812183e+01, 6.00000000e+05, 1.76500270e+03, 1.84299940e+03, 2.59940438e+01, 3.67500000e+02, 7.60000000e-04, 5.00000000e-02, 5.00000000e-03, 2.43429720e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '2': np.array([ 1.01488875e+03, 2.81026692e+02, 5.96311953e+01, 6.00000000e+05, 1.76800166e+03, 1.80500074e+03, 1.22804054e+01, 2.59351413e+03, 7.60000000e-04, 5.00000000e-02, 5.00000000e-03, 1.33456803e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '3': np.array([ 1.01303120e+03, 2.85356979e+02, 4.55001103e+01, 6.00000000e+05, 1.75099969e+03, 1.78799814e+03, 1.24741562e+01, 2.10505343e+02, 7.60000000e-04, 5.00000000e-02, 5.00000000e-03, 1.74585431e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '4': np.array([ 1.01010048e+03, 2.79724074e+02, 4.64849179e+01, 6.00000000e+05, 1.73399959e+03, 1.77100071e+03, 1.89163537e+01, 2.06761848e+03, 7.60000000e-04, 5.00000000e-02, 5.00000000e-03, 2.04884875e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '5': np.array([ 1.01201335e+03, 2.80571184e+02, 5.12883875e+01, 6.00000000e+05, 1.71800032e+03, 1.75399961e+03, 1.61496968e+01, 2.30222114e+03, 7.60000000e-04, 5.00000000e-02, 5.00000000e-03, 1.88354988e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '6': np.array([ 1.01002060e+03, 2.79834802e+02, 4.92317644e+01, 6.00000000e+05, 1.70199977e+03, 1.73800017e+03, 1.94448331e+01, 1.44128653e+03, 7.60000000e-04, 5.00000000e-02, 5.00000000e-03, 1.78512125e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '7': np.array([ 1.02349233e+03, 2.82841942e+02, 4.84055036e+01, 6.00000000e+05, 1.68600077e+03, 1.72200035e+03, 1.56440323e+01, 2.29711325e+03, 7.60000000e-04, 5.00000000e-02, 5.00000000e-03, 1.75546431e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '8': np.array([ 1.02535183e+03, 2.84690731e+02, 6.38909473e+01, 6.00000000e+05, 1.67000105e+03, 1.70600005e+03, 8.62895867e+00, 2.93112111e+03, 7.60000000e-04, 5.00000000e-02, 5.00000000e-03, 1.08243949e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '9': np.array([ 1.01005569e+03, 2.85368493e+02, 4.69600789e+01, 6.00000000e+05, 1.65500012e+03, 1.68999935e+03, 1.34436019e+01, 6.84155129e+01, 7.60000000e-04, 5.00000000e-02, 5.00000000e-03, 1.76576528e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '10': np.array([ 1.01048567e+03, 2.97058150e+02, 4.33608311e+01, 6.00000000e+05, 1.64000122e+03, 1.67500013e+03, 6.53445899e+00, 4.08655037e+02, 7.60000000e-04, 5.00000000e-02, 5.00000000e-03, 1.90785867e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '11': np.array([ 1.02491139e+03, 2.85559174e+02, 4.08532668e+01, 6.00000000e+05, 1.62600054e+03, 1.65999850e+03, 2.00545756e+01, 4.11449098e+02, 7.60000000e-04, 5.00000000e-02, 5.00000000e-03, 1.88659154e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '12': np.array([ 1.01003290e+03, 3.09888667e+02, 4.54061158e+01, 6.00000000e+05, 1.61100114e+03, 1.64599985e+03, 3.64022137e+00, 3.81309830e+02, 7.60000000e-04, 5.00000000e-02, 5.00000000e-03, 1.91039536e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '13': np.array([ 1.01342132e+03, 3.08696226e+02, 4.20101993e+01, 6.00000000e+05, 1.59700075e+03, 1.63200043e+03, 1.87270207e+00, 3.92945895e+02, 7.60000000e-04, 5.00000000e-02, 5.00000000e-03, 1.57637068e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '14': np.array([ 1.01388819e+03, 3.08306291e+02, 3.84989496e+01, 6.00000000e+05, 1.58299931e+03, 1.61799908e+03, 3.67128385e+00, 4.27356912e+02, 7.60000000e-04, 5.00000000e-02, 5.00000000e-03, 2.23969000e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '15': np.array([ 1.01014958e+03, 3.09907179e+02, 4.01340902e+01, 6.00000000e+05, 1.57000020e+03, 1.60399922e+03, 3.71266195e+00, 4.13591102e+02, 7.60000000e-04, 5.00000000e-02, 5.00000000e-03, 4.45900885e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '16': np.array([ 1.02110710e+03, 2.95717915e+02, 5.10020728e+01, 6.00000000e+05, 1.55600027e+03, 1.59000003e+03, 3.33875074e+01, 3.23470908e+02, 7.60000000e-04, 5.00000000e-02, 5.00000000e-03, 4.38118995e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '17': np.array([ 1.01016410e+03, 3.07744028e+02, 4.29708519e+01, 6.00000000e+05, 1.54300110e+03, 1.57699913e+03, 3.10294397e+00, 3.89081633e+02, 7.60000000e-04, 5.00000000e-02, 5.00000000e-03, 1.80000000e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '18': np.array([ 1.02523331e+03, 2.80364698e+02, 4.91323141e+01, 6.00000000e+05, 1.52999979e+03, 1.56400054e+03, 2.00474401e+01, 3.58065516e+02, 7.60000000e-04, 5.00000000e-02, 5.00000000e-03, 1.80000000e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '19': np.array([ 1.01003349e+03, 2.81022630e+02, 4.79869931e+01, 6.00000000e+05, 1.51799946e+03, 1.55100021e+03, 2.00658592e+01, 3.75629220e+02, 7.60000000e-04, 5.00000000e-02, 5.00000000e-03, 1.80000000e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '20': np.array([ 1.01002788e+03, 2.79946092e+02, 4.98150515e+01, 6.00000000e+05, 1.50500004e+03, 1.53799890e+03, 1.98874188e+01, 3.63105569e+02, 7.60000000e-04, 5.00000000e-02, 5.00000000e-03, 2.08959964e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '21': np.array([ 1.01198904e+03, 2.75314098e+02, 4.99899615e+01, 6.00000000e+05, 1.49300048e+03, 1.52599904e+03, 2.58987815e+01, 5.00528381e+02, 7.60000000e-04, 5.00000000e-02, 5.00000000e-03, 2.74730881e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '22': np.array([ 1.01503380e+03, 2.77013372e+02, 5.99130176e+01, 6.00000000e+05, 1.48099946e+03, 1.51400039e+03, 1.72525774e+01, 2.29210179e+01, 7.60000000e-04, 5.00000000e-02, 5.00000000e-03, 2.36183449e+00, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), '23': np.array([ 1.01136214e+03, 2.86167482e+02, 5.33964947e+01, 6.00000000e+05, 1.47015399e+03, 1.50200068e+03, 9.83566652e+00, 2.71708854e+02, 7.60000000e-04, 5.00000000e-02, 5.00000000e-03, 1.06492323e-01, 2.10000000e+05, 0.00000000e+00, 5.00000000e-09, 5.00000000e-09, 5.00000000e-09, 1.56000000e-07 ]), } fitterL = TelluricFitter(debug=False, print_lblrtm_output=args.debug) NSO_props = {"latitude": 31.958, "altitude": 2.096} #alt in km fitterL.SetObservatory(NSO_props) dataL = DataStructures.xypoint(x=newwave1, y=None, cont=None, err=None) parfittedL = telparsdict[str(order)] paramsL = {} for k in range(len(names)): paramsL[names[k]] = np.float(parfittedL[k]) paramsL['wavestart'] = dataL.x[0] # nm paramsL['waveend'] = dataL.x[-1] # nm fitterL.AdjustValue(paramsL) fitterL.ImportData(data2) try: if args.debug: modelL = fitterL.GenerateModel(parfittedL, nofit=True, air_wave=False) else: modelL = suppress_GenerateModel(fitterL, parfittedL, args) except TypeError: return [np.nan], [np.nan], [np.nan], [np.nan], [np.nan], [np.nan] global x, satmLivGen, watm_Liv, satm_Liv satmTel = rebin_jv(model2.x * 10, model2.y, newwave1 * 10, True, logger=logger) # nm --> AA satmLivGen = rebin_jv(modelL.x * 10, modelL.y, newwave1 * 10, True, logger=logger) # nm --> AA watmLivGen = newwave1.copy() watmLivGen *= 10 # nm --> AA # Fit wavelength scale to Telfit'd Livingston x = np.arange(len(satmLivGen)) initguess = np.polyfit(x, watmLivGen, 6) # print('watmLivGen=\n', watmLivGen) # print('x=\n', x) watm_Liv = inparam.watm[(inparam.watm > watmLivGen[0] + 1) & (inparam.watm < watmLivGen[-1] - 1)] satm_Liv = inparam.satm[(inparam.watm > watmLivGen[0] + 1) & (inparam.watm < watmLivGen[-1] - 1)] dpar = np.abs(initguess) * 10 dpar[-1] = 5 waveparfit = wavefit(initguess, dpar) f = np.poly1d(waveparfit) wavefitted = f(x) satmTel[( satmTel < 1e-4 )] = 0. # set very low points to zero so that they don't go to NaN when taken to an exponent by template power in fmodel_chi return wavefitted, satmTel, names, parfitted, wcont1, cont1
if __name__ == "__main__": # Initialize fitter fitter = TelluricFitter(debug=False) #Set the observatory location with a keyword fitter.SetObservatory("McDonald") #Can also give a python dictionary like below #observatory = {"latitude": 30.5, # "altitude": 2.5} #fitter.SetObservatory(observatory) #Read in the spectrum and store as an xypoint x, y, c, e = numpy.loadtxt("Spectrum.dat", unpack=True) data = DataStructures.xypoint(x=x*u.nanometer, y=y, cont=c, err=e) #The following are parameters that should be in the fits header for an observation angle = 37.4 #Zenith distance pressure = 796.8 #Pressure, in hPa humidity = 54.0 #Percent humidity, at the observatory altitude temperature = 292.5 #Temperature in Kelvin resolution = 60000.0 #Resolution lambda/delta-lambda #Define variables to be fit, and give initial guesses. # We will just fit the relative humidity in this example. fitter.FitVariable({"h2o": humidity}) #Adjust parameters that will not be fit, but are important
if __name__ == "__main__": # Initialize fitter fitter = TelluricFitter() fitter.SetObservatory("CTIO") # Read in the fits file using astropy # This is not guaranteed to work! fname = "fitsfile.fits" outfilename = "Corrected.fits" orders = [] hdulist = pyfits.open(fname) header = hdulist[0].header for hdu in hdulist[1:]: data = hdu.data order = DataStructures.xypoint(x=data['wavelength'], y=data['flux'], cont=data['continuum'], err=data['error']) orders.append(order) hdulist.close() # Read in the appropriate parameters: angle = float(header['ZD']) resolution = 80000.0 humidity = header['OUTHUM'] pressure = header['OUTPRESS'] temperature = header['OUTTEMP'] + 273.15 # Convert to Kelvin # Adjust the atmosphere profile using GDAS data height, Pres, Temp, h2o = GetProfile("GDAS_Atmosphere.dat") fitter.EditAtmosphereProfile("Temperature", height, Temp) fitter.EditAtmosphereProfile("Pressure", height, Pres)