def __init__(self, Spectrum, init_cont=True, n_anchors=4): self.__dict__.update(Spectrum.__dict__) self.wave = Spectrum.wave self.flux = Spectrum.flux self.Spectrum = Spectrum if init_cont: cont_model = ContinuumModel(n_anchors=n_anchors) cont_pars = cont_model.guess(self.flux, x=self.wave) for yname in cont_model.ynames: flux_range = np.max(self.flux) - np.min(self.flux) ymin = cont_pars[yname].value - (flux_range / 2) ymax = cont_pars[yname].value + (flux_range / 2) cont_pars[yname].set(min=ymin, max=ymax) self.cont_model = cont_model self.cont_model_pars = cont_pars self.complete_model = cont_model self.all_pars = cont_pars self.peaks = [] self.n_anchors = n_anchors self.n_lines = 0 self.num_prior_lines = 0 self.source_names = [] self.add_source("Telluric", similar={'b': 2}) self.add_source("Nontelluric", similar=None)
def SplineManualRegion(self, n_anchors=5): """ Fit spline continuum from regions selected by users Up to 99 regions #:param n_regions: int, max number of data regions, user can break in the middle :param n_anchors: int, number of anchor points :return: continuum, coordinates of anchor points """ # Select continuum regions to fit anc create a "marker" array # boundaries have to be from spectrum boundary_points = self.SelectPoints( n=99 * 2, nearest=True, y_message="Please Select Continuum Regions") boundary_x = boundary_points.T[0] data2fit = np.zeros_like(self.wave) idx = 0 while idx < len(boundary_x) - 1: data2fit[(self.wave >= boundary_x[idx]) & (self.wave <= boundary_x[idx + 1])] = 1 idx = idx + 2 # tmp check if boundaries work # plt.scatter(self.wave, self.flux, s=10, color="0.5") # plt.scatter(boundary_points.T[0], boundary_points.T[1], marker="X", color="orange", s=100) # plt.scatter(self.wave[data2fit == 1], self.flux[data2fit == 1], s=10, color="r") # plt.show() # Fit spline continuum model wave2fit, flux2fit = self.wave[data2fit == 1], self.flux[data2fit == 1] continuum_model = ContinuumModel(n_anchors=n_anchors, verbose=0) pars_guess = continuum_model.guess(self.flux, x=self.wave) result = continuum_model.fit(data=flux2fit, params=pars_guess, x=wave2fit, weights=np.ones_like(wave2fit)) # generate continuum and scipy.CubicSpline using the fitted parameters params2report = result.params x_points, y_points = [], [] for i in range(n_anchors): x_points.append(params2report["x_%i" % (i)].value) y_points.append(params2report["y_%i" % (i)].value) spline_continuum = CubicSpline(np.asarray(x_points), np.asarray(y_points)) anchor_points = np.asarray([x_points, y_points]).T return spline_continuum, anchor_points
def __init__(self, Spectrum, init_cont=True, n_anchors=4): self.__dict__.update(Spectrum.__dict__) self.wave = Spectrum.wave self.flux = Spectrum.flux if init_cont: cont_model = ContinuumModel(n_anchors=n_anchors) cont_pars = cont_model.guess(self.flux, x=self.wave) self.model = cont_model self.model_pars = cont_pars self.num_sources = 0 self.source_names = []
class Continuum(): '''A class that has multiple methods for fitting different types of continua. Args: Spectrum (EdiblesSpectrum): The input EiblesSpectrum data method (str): The method of continuum fitting. spline (default), alphashape, polynomial ''' def __init__(self, Spectrum, method='spline', *args, **kwargs): self.method = method self.Spectrum = Spectrum if method == 'spline': self.spline(*args, **kwargs) elif method == 'alphashape': self.alphashape(*args, **kwargs) elif method == 'polynomial': self.polynomial(*args, **kwargs) def spline(self, *args, **kwargs): print("method: ", self.method) n_anchors = kwargs['n_anchors'] self.model = ContinuumModel(n_anchors=n_anchors) cont_pars = self.model.guess(self.Spectrum.flux, x=self.Spectrum.wave) self.result = self.model.fit(data=self.Spectrum.flux, params=cont_pars, x=self.Spectrum.wave) print(self.result.params) self.result.plot_fit() plt.show() def alphashape(self): print("method: ", self.method) def polynomial(self): print("method: ", self.method)
def testModels(filename="tests/HD170740_w860_redl_20140915_O12.fits"): method = 'least_squares' sp = EdiblesSpectrum(filename, noDATADIR=True) sp.getSpectrum(xmin=7661, xmax=7670) n_anchors = 4 cont_model = ContinuumModel(n_anchors=n_anchors) assert len(cont_model.param_names) == n_anchors * 2 assert cont_model.n_anchors == n_anchors with pytest.raises(TypeError): assert ContinuumModel() with pytest.raises(TypeError): assert ContinuumModel(n_anchors=11) cont_pars = cont_model.guess(sp.flux, x=sp.wave) assert len(cont_pars) == len(cont_model.param_names) for name in cont_model.param_names: assert cont_pars[name].value is not None voigt = VoigtModel(prefix='voigt_') assert voigt.prefix == 'voigt_' assert len(voigt.param_names) == 4 voigt_pars = voigt.guess(sp.flux, x=sp.wave) assert len(voigt_pars) == len(voigt.param_names) result = voigt.fit(data=sp.flux, params=voigt_pars, x=sp.wave, method=method) assert len(result.params) == n_anchors out = voigt.eval(data=sp.flux, params=result.params, x=sp.wave) assert len(out) == len(sp.flux)
def fit_NaI_Lines(target, date): """A function to fit Na I 2S-2P doublet lines - still very basic NaI1 = 3302.368 -> from edibles_linelist_atoms NaI2 = 3302.978 -> from edibles_linelist_atoms NaI_blue_diff = 0.61 -> lab data from https://www.pa.uky.edu/~peter/newpage/ NaI3 = 5889.951 -> from edibles_linelist_atoms NaI4 = 5895.924 -> from edibles_linelist_atoms NaI_red_diff = 5.975 -> lab data from https://www.pa.uky.edu/~peter/newpage/ blue_red_diff = 2587.583 -> NaI3 - NaI1 """ wavelength = 3300 pythia = EdiblesOracle() bluelist = pythia.GetObsListByWavelength(wavelength, OrdersOnly=True) files = [] for filename in bluelist: if target in filename: if date in filename: files.append(filename) print(files) sp = EdiblesSpectrum(files[0]) print(sp.target) sp.getSpectrum(xmin=3300, xmax=3305) sigma = np.std(sp.flux) prominence = sigma peaks, _ = find_peaks(-sp.flux, prominence=prominence) peak_wavelengths = [sp.wave[i] for i in peaks] # ######################################################################### cont = ContinuumModel(n_anchors=4) cont_pars = cont.guess(sp.flux, x=sp.wave) # ######################################################################### voigt1 = VoigtModel(prefix='v1_') # voigt1_pars = voigt1.make_params(lam_0=3302, b=1, d=0.001, tau_0=0.01) voigt1_pars = voigt1.guess(sp.flux, x=sp.wave) # ######################################################################### voigt2 = VoigtModel(prefix='v2_') voigt2_pars = voigt2.make_params(lam_0=peak_wavelengths[1], b=1, d=0.001, tau_0=0.01) voigt2_pars['v2_lam_0'].set(expr='v1_lam_0 + 0.61') voigt2_pars['v2_b'].set(expr='v1_b') # ######################################################################### model = cont * voigt1 * voigt2 pars = cont_pars + voigt1_pars + voigt2_pars result = model.fit(data=sp.flux, params=pars, x=sp.wave) # ######################################################################### result.params.pretty_print() print('Ratio: ', result.params['v1_tau_0'] / result.params['v2_tau_0']) print(result.fit_report()) plt.subplot(121) result.plot_fit() plt.title('Na I 3303') print() print() # ######################################################################### # ######################################################################### wavelength = 5890 pythia = EdiblesOracle() redlist = pythia.GetObsListByWavelength(wavelength, OrdersOnly=True) files = [] for filename in redlist: if target in filename: if date in filename: files.append(filename) print(files) sp = EdiblesSpectrum(files[1]) print(sp.target) sp.getSpectrum(xmin=5885, xmax=5900) # ######################################################################### cont = ContinuumModel(n_anchors=4) cont_pars = cont.guess(sp.flux, x=sp.wave) # ######################################################################### prominence = (np.max(sp.flux) - np.min(sp.flux)) * 0.5 peaks, _ = find_peaks(-sp.flux, prominence=prominence) peak_wavelengths = [sp.wave[i] for i in peaks] voigt3 = VoigtModel(prefix='v3_') voigt3_pars = voigt3.make_params(lam_0=peak_wavelengths[0], b=1, d=0.001, tau_0=0.4) # voigt3_pars = voigt3.guess(sp.flux, x=sp.wave) # ######################################################################### voigt4 = VoigtModel(prefix='v4_') voigt4_pars = voigt4.make_params(lam_0=5896, b=1, d=0.001, tau_0=0.1) voigt4_pars['v4_lam_0'].set(expr='v3_lam_0 + 5.975') voigt4_pars['v4_b'].set(expr='v3_b') # ######################################################################### model = cont * voigt3 * voigt4 pars = cont_pars + voigt3_pars + voigt4_pars result = model.fit(data=sp.flux, params=pars, x=sp.wave) # ######################################################################### result.params.pretty_print() print('Ratio: ', result.params['v3_tau_0'] / result.params['v4_tau_0']) print(result.fit_report()) plt.subplot(122) result.plot_fit() plt.title('Na I 5890') plt.show()
class Continuum: """A class that has multiple methods for fitting different types of continua. Args: Spectrum (EdiblesSpectrum): The input EiblesSpectrum data plot (bool): If true, plots the continuum fit verbose (int): If > 0, display more status messages with open('eggs.csv', newline='') as csvfile: print(', '.join(row)) """ def __init__(self, Spectrum, method="spline", plot=False, verbose=0, *args, **kwargs): # check existing the available continuum csv files if Spectrum.continuum_filename: verbos_count = 0 with open(Spectrum.continuum_filename) as csvfile: spamreader = csv.reader(csvfile, delimiter=' ', quotechar='|') for row in spamreader: if len(row) > 0: if row[0] == '######': verbos_count += 1 verbos = verbos_count self.method = method self.Spectrum = Spectrum self.plot = plot self.verbose = verbose if method == "spline": self.spline(*args, **kwargs) elif method == "alphashape": self.alphashape(*args, **kwargs) elif method == "polynomial": self.polynomial(*args, **kwargs) def spline(self, *args, **kwargs): """A spline function through a set number of anchor points Args: n_anchors (int): The number of anchor points in the spline """ if self.verbose > 0: print("method: ", self.method) print() n_anchors = kwargs["n_anchors"] self.model = ContinuumModel(n_anchors=n_anchors) cont_pars = self.model.guess(self.Spectrum.flux, x=self.Spectrum.wave) self.result = self.model.fit(data=self.Spectrum.flux, params=cont_pars, x=self.Spectrum.wave) if self.plot: self.result.plot_fit() plt.show() def alphashape(self): if self.verbose > 0: print("method: ", self.method) print() print("This method is not available yet.") def polynomial(self): if self.verbose > 0: print("method: ", self.method) print() print("This method is not available yet.") def add_to_csv(self, user, comments=False): # Tests not in testing folder beceause we dont want to write the testing data assert isinstance(cont.model, ContinuumModel) assert isinstance(user, str) assert len(user) > 0, "A name must be entered" assert isinstance(comments, str) assert isinstance(cont.model.n_anchors, int) assert isinstance(datetime.now(), datetime) csv_file = self.Spectrum.filename.replace(".fits", ".csv").replace( "/DR4/data/", "/DR4/continuum/") line1 = "method=" + self.method + ", " + "n_anchors=" + str( self.model.n_anchors) line2 = ("datetime=" + str(datetime.now()) + ", " + "user="******", " + "Comments: " + comments) x_points = [ self.result.params[xname].value for xname in self.model.xnames ] y_points = [ self.result.params[yname].value for yname in self.model.ynames ] header = line1 + "\n" + line2 with open(csv_file, mode="a") as f: np.savetxt(f, (x_points, y_points), delimiter=",", header=header, comments="# ") f.write("\n") if self.verbose > 0: print("Appended to file!") if self.verbose > 1: print("File appended to: " + csv_file)
import numpy as np import matplotlib.pyplot as plt from edibles.models import ContinuumModel # ################################################################################# # Example 1 x = np.linspace(0, 3) y = x**3 - 3 * x**2 + 1 cont_model = ContinuumModel(n_anchors=4) cont_pars = cont_model.guess(y, x=x) # ############################## # Show initial model out = cont_model.eval(data=y, params=cont_pars, x=x) y_param_names = [] for i in range(cont_model.n_anchors): y_param_names.append('y_' + str(i)) x_param_names = [] for i in range(cont_model.n_anchors): x_param_names.append('x_' + str(i)) init_y = [] for par_name in y_param_names: init_y.append(cont_pars[par_name].value)
from edibles.utils.edibles_spectrum import EdiblesSpectrum from edibles.models import ContinuumModel, VoigtModel filename = "/HD170740/RED_860/HD170740_w860_redl_20140915_O12.fits" method = 'least_squares' sp = EdiblesSpectrum(filename) print(sp.target) sp.getSpectrum(xmin=7661, xmax=7670) # ################################################################################# cont_model = ContinuumModel(n_anchors=4) cont_pars = cont_model.guess(sp.flux, x=sp.wave) model = cont_model pars = cont_pars result = model.fit(data=sp.flux, params=pars, x=sp.wave, method=method) out = cont_model.eval(data=sp.flux, params=result.params, x=sp.wave) resid = sp.flux - out # result.plot_fit() # plt.show() # ################################################################################# voigt1 = VoigtModel(prefix='voigt1_') voigt1_pars = voigt1.guess(resid, x=sp.wave)
class Continuum: """A class that has multiple methods for fitting different types of continua. Args: Spectrum (EdiblesSpectrum): The input EiblesSpectrum data method (str): The method of fitting plot (bool): If true, plots the continuum fit verbose (int): If > 0, display more status messages """ def __init__(self, Spectrum, method="None", plot=False, verbose=0, *args, **kwargs): # check existing the available continuum csv files try: if Spectrum.continuum_filename: self.num_saved_continua = 0 with open(Spectrum.continuum_filename) as f: for row in f: if "######" in row: self.num_saved_continua += 1 except AttributeError: if verbose > 0: print("No previously saved data") self.method = method self.Spectrum = Spectrum self.plot = plot self.verbose = verbose if method == "spline": self.spline(*args, **kwargs) elif method == "alphashape": self.alphashape(*args, **kwargs) elif method == "polynomial": self.polynomial(*args, **kwargs) def spline(self, *args, **kwargs): """A spline function through a set number of anchor points Args: n_anchors (int): The number of anchor points in the spline """ if self.verbose > 0: print("method: ", self.method) print() n_anchors = kwargs["n_anchors"] self.model = ContinuumModel(n_anchors=n_anchors) self.cont_pars = self.model.guess(self.Spectrum.flux, x=self.Spectrum.wave) self.result = self.model.fit( data=self.Spectrum.flux, params=self.cont_pars, x=self.Spectrum.wave ) if self.plot: self.result.plot_fit() plt.show() def alphashape(self): """A function that uses alphashape to find a continuum. Note: Currently not implemented """ if self.verbose > 0: print("method: ", self.method) print() print("This method is not available yet.") def polynomial(self): """A function that uses a polynomial to find a continuum. Note: Currently not implemented """ if self.verbose > 0: print("method: ", self.method) print() print("This method is not available yet.") def prebuilt_model(self, chosen_save_num=None, plot=False, verbose=0): """A function that generates continua based on data saved in csv files. Args: chosen_save_num (int): The 'save number' of the continuum data, default=None. If None, the function will create all saved models and (possibly) plot them. plot (bool): If True, plot the model(s) once it is created verbose (int): If > 0, print more information about the data """ # assert self.num_saved_continua is not 0, otherwise there is no known continuum point assert self.num_saved_continua > 0, "There is no saved continuum." # read and parse file contents saves_counter = 0 saves_dict = {} with open(self.Spectrum.continuum_filename, mode="r") as f: for line in f: line = line.split("\n")[0] # initialize new save group if len(line) > 0: if line == "######": name = "save" + str(saves_counter) saves_counter += 1 saves_dict[name] = {"x": None, "y": None} # update dict elif line[0:2] == "# ": key, val = line.split("# ")[1].split("=") if key == "n_anchors": val = int(val) if key == "datetime": val = datetime.strptime(val, "%Y-%m-%d %H:%M:%S.%f") saves_dict[name][key] = val else: if saves_dict[name]["x"] is None: saves_dict[name]["x"] = [ float(item) for item in line.split(",") ] else: saves_dict[name]["y"] = [ float(item) for item in line.split(",") ] if chosen_save_num is not None: assert chosen_save_num < self.num_saved_continua, ( "There are only " + str(self.num_saved_continua) + " saved continua." ) chosen_save = saves_dict["save" + str(chosen_save_num)] if verbose > 0: print("Number of saved continuum datasets: ", saves_counter) print("Save chosen: save" + str(chosen_save_num)) pprint(chosen_save) cont_model = ContinuumModel(n_anchors=chosen_save['n_anchors']) params = cont_model.make_params() for i in range(cont_model.n_anchors): params['%sx_%i' % (cont_model.prefix, i)].set(value=chosen_save["x"][i], vary=False) params['%sy_%i' % (cont_model.prefix, i)].set(value=chosen_save["y"][i], vary=False) params = update_param_vals(params, cont_model.prefix) out = cont_model.eval(params=params, x=self.Spectrum.wave) if plot: plt.plot(self.Spectrum.wave, self.Spectrum.flux) plt.plot(self.Spectrum.wave, out) plt.scatter(chosen_save["x"], chosen_save["y"], marker="x", s=80, color="k") plt.show() return out else: for j in range(saves_counter): chosen_save = saves_dict["save" + str(j)] try: method = str(chosen_save["method"]) except: method = "unknown" try: n_anchors = str(chosen_save["n_anchors"]) except: n_anchors = "unknown" try: date_time = str(chosen_save["datetime"]) except: date_time = "unknown" try: user = str(chosen_save["user"]) except: user = "******" try: comments = str(chosen_save["comments"]) except: comments = "None" print("Continuum group {a}/{b}".format(a = j, b=saves_counter-1)) print("Method: " + method + ", n_anchors: " + n_anchors + ";") print("Added by: " + user + ", at time:" + date_time + ";") print("Comments: " + comments) if plot: cont_model = ContinuumModel(n_anchors=chosen_save['n_anchors']) params = cont_model.make_params() for i in range(cont_model.n_anchors): params['%sx_%i' % (cont_model.prefix, i)].set(value=chosen_save["x"][i], vary=False) params['%sy_%i' % (cont_model.prefix, i)].set(value=chosen_save["y"][i], vary=False) params = update_param_vals(params, cont_model.prefix) out = cont_model.eval(params=params, x=self.Spectrum.wave) plt.plot(self.Spectrum.wave, self.Spectrum.flux) plt.plot(self.Spectrum.wave, out) plt.scatter(chosen_save["x"], chosen_save["y"], marker="x", s=80, color="k") plt.show() print("Please make your selection back in the script.") def add_to_csv(self, user, comments): """A function that saves the continuum model parameters to a csv file. Each save appears as follows: | ###### | # method=spline | # n_anchors=4 | # datetime=2020-10-06 10:56:02.192865 | # user=First Last | # comments=This is a comment. | x1, x2, x3, x4 | y1, y2, y3, y4 Args: user (str): The name of the person adding the data comments (str): Any comments the user wishes to make about the data to be saved Note: The data is saved to the ediblesdr4 github repository, with the same filepath as the original FITS file. """ # Tests not in testing folder beceause we dont want to write the testing data assert isinstance(self.model, ContinuumModel) assert isinstance(user, str) assert len(user) > 0, "A name must be entered" assert isinstance(comments, str) assert isinstance(self.model.n_anchors, int) assert isinstance(datetime.now(), datetime) csv_file = self.Spectrum.filename.replace(".fits", ".csv").replace( "/DR4/data/", "/DR4/continuum/" ) x_points = [self.result.params[xname].value for xname in self.model.xnames] y_points = [self.result.params[yname].value for yname in self.model.ynames] with open(csv_file, mode="a") as f: f.write("######\n") f.write("# method=" + str(self.method) + "\n") f.write("# n_anchors=" + str(self.model.n_anchors) + "\n") f.write("# datetime=" + str(datetime.now()) + "\n") f.write("# user="******"\n") f.write("# comments=" + str(comments) + "\n") np.savetxt(f, (x_points, y_points), delimiter=",") f.write("\n") if self.verbose > 0: print("Appended to file!") if self.verbose > 1: print("File appended to: " + csv_file)