def _monotonic_slopes(delta, h, deriv): # c.f. scipy PCHIP implementation # build m = dy/dx (default value is 0) m = numpy.zeros( len(delta) + 1, object if delta.dtype == object else float) # delta,h live on intervals; x,y,m on knots # exclude ends, and points where delta[i-1] and delta[i] differ in sign or vanish idx = numpy.arange(1, len(m) - 1)[(delta[1:] * delta[:-1]) > 0] w1 = 2 * h[idx] + h[idx - 1] w2 = h[idx] + 2 * h[idx - 1] m[idx] = (w1 + w2) / (w1 / delta[idx - 1] + w2 / delta[idx]) # endpoints for i, hi, deltai in [(0, h[:2], delta[:2]), (-1, h[:-3:-1], delta[:-3:-1])]: if deriv[i] is not None: m[i] = deriv[i] continue m[i] = ((2 * hi[0] + hi[1]) * deltai[0] - hi[0] * deltai[1]) / (hi[0] + hi[1]) if m[i] * deltai[0] <= 0: m[i] = 0 elif deltai[0] * deltai[1] <= 0 and _gvar.fabs( m[i]) > 3 * _gvar.fabs(deltai[0]): m[i] = 3 * deltai[0] return m
def sinc1d_complex(x, h, a, dx, fwhm): """The "complex" version of the sinc (understood as the Fourier Transform of a boxcar function from 0 to MPD). This is the real sinc function when ones wants to fit both the real part and the imaginary part of the spectrum. :param x: 1D array of float64 giving the positions where the function is evaluated :param h: Height :param a: Amplitude :param dx: Position of the center :param fwhm: FWHM of the sinc """ width = gvar.fabs(fwhm) / orb.constants.FWHM_SINC_COEFF width /= np.pi width /= 2.### X = (x-dx) / (2*width) s1dc_re = h + a * np.sin(X) / (X) s1dc_re[X == 0] = h + a s1dc_im = - h + a * (np.cos(X) - 1.) / (X) s1dc_im[X == 0] = 0 return (s1dc_re, s1dc_im)
def sinc1d_flux(a, fwhm): """Compute flux of a 1D sinc. :param a: Amplitude :param fwhm: FWHM """ width = fwhm / orb.constants.FWHM_SINC_COEFF return a * gvar.fabs(width)
def gaussian1d_flux(a, fwhm): """Compute flux of a 1D Gaussian. :param a: Amplitude :param fwhm: FWHM """ width = fwhm / orb.constants.FWHM_COEFF return a * gvar.fabs(math.sqrt(2*math.pi) * width)
def vel2sigma(vel, lines, axis_step): """Convert a velocity in km/s to a broadening in channels. :param vel: velocity in km/s :param lines: line position in the unit of axis_step :param axis_step: axis step size """ sigma = lines * vel / orb.constants.LIGHT_VEL_KMS sigma /= axis_step # convert sigma cm-1->pix return gvar.fabs(sigma)
def sigma2vel(sigma, lines, axis_step): """Convert a broadening in channels to a velocity in km/s :param sigma: broadening in channels :param lines: line position in the unit of axis_step :param axis_step: axis step size """ vel = sigma * axis_step # convert sigma pix->cm-1 # convert sigma cm-1-> km/s vel = orb.constants.LIGHT_VEL_KMS * vel / lines return gvar.fabs(vel)
def _steffen_slopes(delta, h, deriv): # c.f. scipy PCHIP implementation # build m = dy/dx (default value is 0) m = numpy.zeros( len(delta) + 1, object if delta.dtype == object else float) # delta,h live on intervals; x,y,m on knots # interior points p = (delta[:-1] * h[1:] + delta[1:] * h[:-1]) / (h[:-1] + h[1:]) idx = (numpy.sign(delta[:-1]) * numpy.sign(delta[1:]) <= 0) p[idx] *= 0 idx = _gvar.fabs(p) > 2 * _gvar.fabs(delta[:-1]) p[idx] = 2 * delta[:-1][idx] idx = _gvar.fabs(p) > 2 * _gvar.fabs(delta[1:]) p[idx] = 2 * delta[1:][idx] m[1:-1] = p # endpoints for i, hi, deltai in [(0, h[:2], delta[:2]), (-1, h[:-3:-1], delta[:-3:-1])]: if deriv[i] is not None: m[i] = deriv[i] continue m[i] = ((2 * hi[0] + hi[1]) * deltai[0] - hi[0] * deltai[1]) / (hi[0] + hi[1]) if m[i] * deltai[0] <= 0: m[i] = 0 elif deltai[0] * deltai[1] <= 0 and _gvar.fabs( m[i]) > 3 * _gvar.fabs(deltai[0]): m[i] = 3 * deltai[0] return m
def fast_w2pix(w, axis_min, axis_step): """Fast conversion of wavelength/wavenumber to pixel :param w: wavelength/wavenumber :param axis_min: min axis wavelength/wavenumber :param axis_step: axis step size in wavelength/wavenumber """ w_ = (w - axis_min) if np.any(gvar.sdev(w_) != 0.): w_ = gvar.gvar(gvar.mean(w_), gvar.sdev(w_)) return gvar.fabs(w_) / axis_step
def sincgauss1d_complex(x, h, a, dx, fwhm, sigma): """The "complex" version of the sincgauss (dawson definition). This is the real sinc*gauss function when ones wants to fit both the real part and the imaginary part of the spectrum. :param x: 1D array of float64 giving the positions where the function is evaluated :param h: Height :param a: Amplitude :param dx: Position of the center :param fwhm: FWHM of the sinc :param sigma: Sigma of the gaussian. """ if np.size(sigma) > 1: if np.any(sigma != sigma[0]): raise Exception('Only one value of sigma can be passed') else: sigma = sigma[0] sigma = np.fabs(sigma) fwhm = np.fabs(fwhm) broadening_ratio = np.fabs(sigma / fwhm) max_broadening_ratio = gvar.mean(broadening_ratio) + gvar.sdev( broadening_ratio) if broadening_ratio < 1e-2: return sinc1d_complex(x, h, a, dx, fwhm) if np.isclose(gvar.mean(sigma), 0.): return sinc1d_complex(x, h, a, dx, fwhm) if max_broadening_ratio > 7: return gaussian1d_complex(x, h, a, dx, sigma * (2. * gvar.sqrt(2. * gvar.log(2.)))) width = gvar.fabs(fwhm) / orb.constants.FWHM_SINC_COEFF width /= np.pi ### a_ = sigma / np.sqrt(2) / width b_ = ((x - dx) / np.sqrt(2) / sigma) if b_.dtype == np.float128: b_ = b_.astype(float) sg1c = orb.cgvar.sincgauss1d_complex(a_, b_) return (h + a * sg1c[0], h + a * sg1c[1])
def sincgauss1d(x, h, a, dx, fwhm, sigma): """Return a 1D sinc convoluted with a gaussian of parameter sigma. If sigma == 0 returns a pure sinc. :param x: 1D array of float64 giving the positions where the sinc is evaluated :param h: Height :param a: Amplitude :param dx: Position of the center :param fwhm: FWHM of the sinc :param sigma: Sigma of the gaussian. """ if np.size(sigma) > 1: if np.any(sigma != sigma[0]): raise Exception('Only one value of sigma can be passed') else: sigma = sigma[0] sigma = np.fabs(sigma) fwhm = np.fabs(fwhm) broadening_ratio = np.fabs(sigma / fwhm) max_broadening_ratio = gvar.mean(broadening_ratio) + gvar.sdev( broadening_ratio) if broadening_ratio < 1e-2: return sinc1d(x, h, a, dx, fwhm) if np.isclose(gvar.mean(sigma), 0.): return sinc1d(x, h, a, dx, fwhm) if max_broadening_ratio > 7: return gaussian1d(x, h, a, dx, sigma * (2. * gvar.sqrt(2. * gvar.log(2.)))) width = gvar.fabs(fwhm) / orb.constants.FWHM_SINC_COEFF width /= np.pi ### a_ = sigma / math.sqrt(2) / width b_ = (x - dx) / math.sqrt(2) / sigma return h + a * orb.cgvar.sincgauss1d(a_, b_)
def mertz1d(x, h, a, dx, fwhm, ratio): """Complex ILS when Mertz ramp is used during the Fourier transform. :param x: 1D array of float64 giving the positions where the function is evaluated :param h: Height :param a: Amplitude :param dx: Position of the center :param fwhm: FWHM of the sinc :param ratio: Ratio of the shortest side over the longest side of the interferogram """ width = gvar.fabs(fwhm) / orb.constants.FWHM_SINC_COEFF width /= np.pi width /= 2.### X = (x-dx) / (2*width) f_real = h + a * np.sin(X) / X f_imag = h + a * (np.cos(X) / X - 1./((X**2)*ratio) * np.sin(ratio*X)) return (f_real, f_imag)