def fit_model(self):#Only use class-wide variables. import scipy.optimize import numpy as np import tayph.functions as fun import tayph.util as ut import pdb A_start = np.max(np.abs(self.ccf)) C_start = np.nanmedian(self.ccf) W_start = 5.0#km/s. A2_start = A_start * (-0.2) W2_start = 12.0#km/s. startparams = [A_start,self.l_fit,self.vsini_fit,W_start,C_start,A2_start,W2_start] if self.S == 0 and self.D > 0: #Only one high-order component. startparams+=[0,0] if self.S == 1 and self.D > 0: startparams+=self.D*[0]#Half-D for A and half-D for W, = 1*D. if self.S == 2 and self.D > 0: startparams+=2*self.D*[0]#D parameters for A and another D for w, = 2*D. result = scipy.optimize.leastsq(evaluate_shadow,startparams,args = (self.rv,self.ccf*self.ccf_mask,self.T,self.p,self.aRstar,self.vsys,self.inclination,self.n_components,self.offset,self.S,self.D)) # alternatively you can do this with closure variables in f if you like self.model = evaluate_shadow(result[0],self.rv,self.ccf*0.0,self.T,self.p,self.aRstar,self.vsys,self.inclination,self.n_components,self.offset,self.S,self.D,leastsq=False) # void,matched_ds_model=match_shadow(self.rv,self.ccf,self.ccf_mask,self.dp,self.model,self.maskHW) # ut.save_stack('test1.fits',[self.model,matched_ds_model]) #This appears to be correct now to within a percent? self.out_result = result[0] self.l_final = result[0][1] self.vsini_final = result[0][2] print(f'------Fitted lambda and v sin(i) from entire feature: {self.l_final}, {self.vsini_final}') self.v_star_fit = fun.local_v_star(self.p,self.aRstar,self.inclination,self.vsini_final,self.l_final)+self.vsys
def spinorbit(params,primer,vsys,aRstar,inclination): import numpy as np import tayph.functions as fun phases = np.array([primer[0][1],primer[1][1]]) RVs = np.array([primer[0][0],primer[1][0]]) l = params[0] vsini = params[1] v_star = fun.local_v_star(phases,aRstar,inclination,vsini,l)+vsys diff = v_star - RVs return(diff)
def __init__(self, fig, ax, rv, ccf, primer, dp, outname): """We initialize with a figure object, three axis objects (in a list) an rv axis, the CCF, the user-generated prior on the doppler shadow (the two) points, and the pointer to the data in order to load physics.""" import tayph.system_parameters as sp import numpy as np import pdb import scipy.interpolate as interpol import tayph.functions as fun import tayph.util as ut import sys #Upon initialization, we pass the keywords onto self. nexp = np.shape(ccf)[0] nrv = np.shape(ccf)[1] self.rv = rv self.ccf = ccf self.p = sp.phase(dp) if len(self.p) != nexp: print('ERROR IN FIT_DOPPLER_MODEL __INIT__:') print('The height of the CCF does not match nexp.') sys.exit() transit = sp.transit(dp) - 1.0 self.T = abs(transit / max(abs(transit))) self.ax = ax self.aRstar = sp.paramget('aRstar', dp) self.vsys = sp.paramget('vsys', dp) self.RVp = sp.RV(dp) + self.vsys self.inclination = sp.paramget('inclination', dp) self.n_components = 1 self.maskHW = 10.0 #Default masking half-width self.offset = 0.0 self.D = 0 #Initial degree of polynomial fitting. self.S = 0 self.dp = ut.check_path(dp, exists=True) self.outpath = self.dp / (outname + '.pkl') #Translate the pivot to RV-phase points. #Need to interpolate on phase only, because the primer was already defined in RV space. p_i = interpol.interp1d(np.arange(nexp, dtype=float), self.p) p1 = float(p_i(primer[0][1])) p2 = float(p_i(primer[1][1])) v1 = primer[0][0] v2 = primer[1][0] self.primer = [[v1, p1], [v2, p2]] self.fit_spinorbit() # self.primer = a*self.rv+b self.v_star_primer = fun.local_v_star(self.p, self.aRstar, self.inclination, self.vsini_fit, self.l_fit) + self.vsys # ax[0].plot(v_star_primer,fun.findgen(nexp),'--',color='black',label='Spin-orbit fit to primer') self.mask_ccf() self.fit_model()
def evaluate_shadow(params, rv, ccf, transit, phase, aRstar, vsys, inclination, n_components, offset_second_component, S, D, leastsq=True): import numpy as np import tayph.functions as fun import pdb import matplotlib.pyplot as plt """This evaluates the doppler shadow model. Primary usage is in leastsq and in evaluation of its output. Still need to write good documentation and tests. If leastsq is true, it returns the flattened difference between the input CCF and the model. If it is set to false, it just returns the model; and the ccf object is ignored. Offset is the velocity offset of the second component.""" modelled_ccf = ccf * 0.0 modelled_ccf[np.isnan(modelled_ccf)] = 0.0 nexp = np.shape(ccf)[0] #From https://www.aanda.org/articles/aa/pdf/2016/04/aa27794-15.pdf A = params[0] #Amplitude l = params[1] #Spin-orbit angle vsini = params[2] #Stellar vsini W = params[3] C = params[4] A2 = params[5] W2 = params[6] v_star = fun.local_v_star(phase, aRstar, inclination, vsini, l) + vsys for i in range(nexp): A_poly, W_poly = evaluate_poly(phase[i], params, S, D) modelled_ccf[ i, :] = transit[i] * A * A_poly * np.exp(-(rv - v_star[i])**2 / (2.0 * (W * W_poly)**2)) + C if n_components == 2: modelled_ccf[i, :] += transit[i] * A2 * np.exp( -(rv - v_star[i] - offset_second_component)**2 / (2.0 * W2**2)) if leastsq == True: diffs = modelled_ccf - ccf diffs[np.isnan(diffs)] = 0.0 return diffs.flatten() # it expects a 1D array out. else: return modelled_ccf