I'm trying to reproduce something in python that works correctly in IDL. (This is part of a larger program, so just using IDL is not an option.)
What I'm trying to do is fit a spectrum which has not been wavelength-calibrated, to another which already has, and thereby get a wavelength dispersion solution for the uncalibrated spectrum.
IDL Code:
function fit_wav, x, p, wav=w, flux=f ;our function for mpfitfun
;eval a polynomial function for wavelength and interpolate
;to the wavelength of the fiducial spectrum
wav = poly(w, p)
return, interpol(f, wav, x)
end
function normalize, arr ;scale an array into [0,1]
return, (arr - min(arr)) / (max(arr) - min(arr))
end
readcol, '~/Desktop/fiducial.txt', wavelength, flux, format='F,F'
readcol, '~/Desktop/to_fit.txt', uncalibrated, format='F'
npix = n_elements(uncalibrated)
pix = [0d:npix-1]
p_init = [1.95, 0.5/npix, 0.]
coef = mpfitfun('fit_wav', wavelength, normalize(flux), 1., $
p_init, functargs={wav:pix, flux:normalize(uncalibrated)})
print, coef
;plot the results
calib_wav = poly(pix, coef)
cgplot, calib_wav, normalize(uncalibrated), xra=minmax([calib_wav, wavelength])
cgplot, /overplot, wavelength, normalize(flux), color='red'
end
The output for this looks like (fiducial is red, fit is black):
1.9652186 0.00070232586 -1.5183943e-07
The python code is essentially the same:
from astropy.modeling import Fittable1DModel, Parameter, fitting
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import interp1d
class WavModel(Fittable1DModel):
'''Our model for fitting the wavelength dispersion solution
based on an existing calibrated spectrum'''
a = Parameter(name='a',default=0.)
b = Parameter(name='b',default=1.)
c = Parameter(name='c',default=0.)
def __init__(self, a, b, c, spec=None, **kwargs):
self.spec = spec
self.pix = np.arange(spec.size, dtype=float)
super(WavModel, self).__init__(a, b, c, **kwargs)
def evaluate(self, x, a, b, c):
wav = a + self.pix * b + self.pix * self.pix * c
return interp1d(wav, self.spec, bounds_error=False, fill_value=0.)(x)
fit_deriv = None
normalize = lambda arr: (arr - arr.min()) / (arr.max() - arr.min()) #scale arr into [0,1]
wavelength, flux = np.loadtxt('fiducial.txt').T
uncalibrated = np.loadtxt('to_fit.txt')
npix = uncalibrated.size
pix = np.arange(npix, dtype=float)
fitter = fitting.LevMarLSQFitter()
p_init = WavModel(1.95, 0.5/npix, 0., spec=normalize(uncalibrated))
coef = fitter(p_init, wavelength, normalize(flux)).parameters
print coef
#now we plot!
calib_wav = coef[0] + coef[1] * pix + coef[2] * pix * pix
plt.plot(calib_wav, normalize(uncalibrated), 'b')
plt.plot(wavelength, normalize(flux), 'r')
plt.savefig('python_test.png')
The results of THIS, however, are somewhat different:
[ 1.98065438e+00 3.70817592e-04 1.06795564e-06]
The IDL version is definitely the correct one, since the wavelength limit of the detector is somewhere around 2.5 microns...
I've pasted the data here and here. Any suggestions on how to get the python fit to look like the IDL fit? Thank you so much!