def joebvpfit(wave, flux, sig, linepars, flags): xtol = 1e-11 gtol = 1e-11 # Only feed to the fitter the parameters that go into the model partofit = linepars[:5] parinfo = prepparinfo(partofit, flags) # Save the velocity windows to add back to the parameter array vlim1 = linepars[5] vlim2 = linepars[6] # Get atomic data lam, fosc, gam = atomicdata.setatomicdata(linepars[0]) cfg.lams = lam cfg.fosc = fosc cfg.gam = gam # Set fit regions cfg.fitidx = fitpix(wave, linepars) # Prep parameters for fitter partofit = unfoldpars(partofit) modelvars = {'x': wave, 'y': flux, 'err': sig} # Do the fit and translate the parameters back into the received format m = nmpfit.mpfit(voigterrfunc, partofit, functkw=modelvars, parinfo=parinfo, nprint=1, quiet=0, fastnorm=1, ftol=1e-10, xtol=xtol, gtol=gtol) if m.status <= 0: print('Fitting error:', m.errmsg) fitpars = foldpars(m.params) fiterrors = foldpars(m.perror) # Add velocity windows back to parameter array fitpars.append(vlim1) fitpars.append(vlim2) print('\nFit results: \n') for i in range(len(fitpars[0])): print( jbg.tabdelimrow([ round(fitpars[0][i], 2), jbg.decimalplaces(fitpars[3][i], 5), jbg.roundto(fitpars[1][i], 5), jbg.roundto(fitpars[2][i], 5), jbg.roundto(fitpars[4][i], 5) ])[:-2]) print( jbg.tabdelimrow([ ' ', ' ', ' ', round(fiterrors[1][i], 3), round(fiterrors[2][i], 3), round(fiterrors[4][i], 3) ])) return fitpars, fiterrors
def joebvpfit(wave,flux,sig,linepars,flags): xtol=1e-11 gtol=1e-11 # Only feed to the fitter the parameters that go into the model partofit=linepars[:5] parinfo=prepparinfo(partofit,flags) # Save the velocity windows to add back to the parameter array vlim1=linepars[5] ; vlim2=linepars[6] # Get atomic data lam,fosc,gam=atomicdata.setatomicdata(linepars[0]) cfg.lams=lam ; cfg.fosc=fosc ; cfg.gam=gam # Set fit regions cfg.fitidx = fitpix(wave, linepars) # Prep parameters for fitter partofit=unfoldpars(partofit) modelvars={'x':wave,'y':flux,'err':sig} # Do the fit and translate the parameters back into the received format m=nmpfit.mpfit(voigterrfunc,partofit,functkw=modelvars,parinfo=parinfo,nprint=1,quiet=0,fastnorm=1,ftol=1e-10,xtol=xtol,gtol=gtol) if m.status <= 0: print('Fitting error:',m.errmsg) fitpars=foldpars(m.params) fiterrors = foldpars(m.perror) # Add velocity windows back to parameter array fitpars.append(vlim1) ; fitpars.append(vlim2) print('\nFit results: \n') for i in range(len(fitpars[0])): print(jbg.tabdelimrow([round(fitpars[0][i],2),jbg.decimalplaces(fitpars[3][i],5),jbg.roundto(fitpars[1][i],5),jbg.roundto(fitpars[2][i],5),jbg.roundto(fitpars[4][i],5)])[:-2]) print(jbg.tabdelimrow([' ',' ',' ',round(fiterrors[1][i],3),round(fiterrors[2][i],3),round(fiterrors[4][i],3)])) return fitpars,fiterrors
def stevebvpfit(wave, flux, sig, flags, linepars=None, xall=None): if linepars is None: raise ValueError("Must pass parameters in linepars.") return # Only feed to the fitter the parameters that go into the model partofit = linepars[:5] parinfo = prepparinfo(linepars, flags) npar = len(parinfo) ## Be sure that PARINFO is of the right type if parinfo is not None: if type(parinfo) != list: raise ValueError("PARINFO must be a list of dictionaries.") return else: if type(parinfo[0]) != dict: raise ValueError("PARINFO must be a list of dictionaries.") return ## If the parameters were not specified at the command line, then ## extract them from PARINFO if xall is None: xall = parameterInformationFunction(parinfo, "value") if xall is None: raise ValueError( 'either xall or PARINFO(*)["value"] must be supplied.') xall = np.ravel(np.array(xall).T) # function that deals with tied and fixed parameters and creates # indices list to go from non-redundant to redundant array: indices_and_friends = various_indices(parinfo) pfixed, ptied, ifree, indices = indices_and_friends indices = indices.astype(np.int) ifree = np.squeeze(ifree) ## Compose only VARYING parameters x = np.squeeze(xall)[np.squeeze( pfixed == 0)] ## x is the set of free parameters ## LIMITED parameters ? limited = parameterInformationFunction(parinfo, "limited", default=[0, 0], n=npar) limits = parameterInformationFunction(parinfo, "limits", default=[0.0, 0.0], n=npar) if (limited is not None) and (limits is not None): ## Error checking on limits in parinfo wh = np.nonzero((limited[:, 0] & (xall < limits[:, 0])) | (limited[:, 1] & (xall > limits[:, 1]))) if len(wh[0]) > 0: raise ValueError("parameters are not within PARINFO limits") return wh = np.nonzero((limited[:, 0] & limited[:, 1]) & (limits[:, 0] >= limits[:, 1]) & (pfixed == 0)) if len(wh[0]) > 0: raise ValueError("PARINFO parameter limits are not consistent") return freeanduntied = np.where(np.squeeze(pfixed == 0))[0] ## Transfer structure values to local variables qulim = np.take(limited[:, 1], freeanduntied) ulim = np.take(limits[:, 1], freeanduntied) qllim = np.take(limited[:, 0], freeanduntied) llim = np.take(limits[:, 0], freeanduntied) # Save the velocity windows to add back to the parameter array vlim1 = linepars[5] vlim2 = linepars[6] # Get atomic data lam, fosc, gam = atomicdata.setatomicdata(linepars[0]) cfg.lams = lam cfg.fosc = fosc cfg.gam = gam # Set fit regions cfg.fitidx = fitpix(wave, linepars) # Prep parameters for fitter partofit = unfoldpars(partofit) modelvars = {"l": wave, "y": flux, "err": sig} bnds = ( np.squeeze(np.where(qllim == 1, llim, -np.inf)), np.squeeze(np.where(qulim == 1, ulim, np.inf)), ) arg = [ xall, ifree, indices, modelvars["l"], modelvars["y"], modelvars["err"] ] # here is where fitting happens: cache = {"x": np.nan, "resid": np.nan, "jac": np.nan} def cached_resid(x, cache): if np.all(x == cache["x"]): return cache["resid"] cache["x"] = x.copy() resid, jac = stevevoigterrfunc_w_jac(x, *arg) cache["resid"] = resid cache["jac"] = jac.T return resid def cached_jac(x, cache): if np.all(x == cache["x"]): return cache["jac"] cache["x"] = x.copy() resid, jac = stevevoigterrfunc_w_jac(x, *arg) cache["resid"] = resid cache["jac"] = jac.T return jac.T m = least_squares( cached_resid, x, bounds=bnds, args=(cache, ), kwargs={}, verbose=True, jac=cached_jac, ) if m.status < 0: print("Fitting error:", m.message) done = True elif m.status == 0: done = False else: done = True xall_fitted = xall.copy() xall_fitted[ifree] = m["x"][indices] fitpars = foldpars(xall_fitted) # This is a super-hacky fix to the issue of parameters having Jacobians of 0. # It could obscure other problems and so it would make sense to replace it # at some point. badvarmask = np.all(np.isclose(m.jac, 1e-10), axis=0) if np.any(badvarmask): try: (bad_var_ind, ) = np.where(badvarmask) # Replacing bad columns with value near 0. m.jac[:, bad_var_ind] = 1e-5 # Giving rest wavelength and redshift of bad lines. Also really janky in and # of itself so that's fun. bad_var_indices_mask = np.in1d(indices, bad_var_ind) bad_ifree = ifree[bad_var_indices_mask] bad_comp_inds = np.unique(bad_ifree // 5) for comp in bad_comp_inds: bad_wav = fitpars[0][comp] bad_red = fitpars[3][comp] print("Bad component of {0:.5f} at {1:.5f}.".format( np.float(bad_wav), np.float(bad_red))) except IndexError: pass perr = calc_perrors(m.jac, freeanduntied, numpars=xall.size) fitperr = foldpars(perr) for par in perr: par = np.ravel(np.array(par)) # Adding in reduced chi-squared bit: rchi2 = 2 * m.cost / (len(cfg.fitidx) - len(freeanduntied)) # Add velocity windows back to parameter array fitpars.append(vlim1) fitpars.append(vlim2) # prints results in terminal/notebook/whatever thing you have python running from print("\nFit results: \n") for i in range(len(fitpars[0])): print( jbg.tabdelimrow([ round(fitpars[0][i], 2), jbg.decimalplaces(fitpars[3][i], 5), jbg.roundto(fitpars[1][i], 5), jbg.roundto(fitpars[2][i], 5), jbg.roundto(fitpars[4][i], 5), ])[:-2]) print( jbg.tabdelimrow([ " ", " ", " ", round(fitperr[1][i], 3), round(fitperr[2][i], 3), round(fitperr[4][i], 3), ])) print("\nReduced chi-squared: {0:f}".format(rchi2)) return fitpars, fitperr, rchi2, done