def get_measured_temperature(self, starname, date, Tmax, instrument=None, N=7, addmode='simple', feh=None, vsini=None): """ Get the measured temperature by doing a weighted sum over temperatures near the given one (which I find by hand) :param starname: The name of the star :param Tmax: The temperature to search near :param date: The date the observation was taken :param instrument: The instrument used (this function automatically finds it if not given) :param N: The number of temperature points to take :param addmode: The way the individual order CCFs were co-added. :param feh: The metallicity to use. If not given, it finds whatever gives the highest ccf peak. :param vsini: The vsini to use. If not given, it finds whatever gives the highest ccf peak. :return: A pandas DataFrame with the starname, date, instrument, and model parameters for the temperatures near the requested one """ if instrument is None: # Find this star/date in all of the interfaces found = False df_list = [] for inst in self._interfaces.keys(): interface = self._interfaces[inst] if starname in interface.list_stars() and date in interface.list_dates(starname): found = True df = self.get_measured_temperature(starname, date, Tmax, instrument=inst, N=N) df_list.append(df) if not found: warnings.warn('Star ({}) not found for date ({}) in any CCF interfaces!'.format(starname, date)) return None return pd.concat(df_list, ignore_index=True) # Check that the star/date combination are in the requested interface if starname not in self._interfaces[instrument].list_stars(): raise KeyError('Star ({}) not in instrument ({})'.format(starname, instrument)) if date not in self._interfaces[instrument].list_dates(starname): # Try date +/- 1 before failing (in case of civil/UT date mismatch or something) from datetime import datetime, timedelta year, month, day = [int(s) for s in date.split('-')] for inc in [-1, 1]: t = datetime(year, month, day) + timedelta(inc) test_date = '{}-{:02d}-{:02d}'.format(t.year, t.month, t.day) if test_date in self._interfaces[instrument].list_dates(starname): return self.get_measured_temperature(starname, test_date, Tmax, instrument=instrument, N=N, addmode=addmode) raise KeyError( 'Date ({}) not in CCF interface for star {} and instrument {}'.format(date, starname, instrument)) # Get CCF information from the requested instrument/star/date combo interface = self._interfaces[instrument] logging.info('{}, {}, {}, {}'.format(starname, date, instrument, addmode)) df = interface._compile_data(starname=starname, date=date, addmode=addmode, read_ccf=True) #df['ccf_max'] = df.ccf.map(np.max) Already done now # Get the parameters and RV of the CCF with the highest peak (which has temperature = Tmax) #print(df) requested = df.loc[df['T'] == Tmax] if feh is not None: requested = requested.loc[requested['[Fe/H]'] == feh] if vsini is not None: requested = requested.loc[requested['vsini'] == vsini] i = np.argmax(requested.ccf_max) vsini = requested.loc[i, 'vsini'].item() metal = requested.loc[i, '[Fe/H]'].item() logg = requested.loc[i, 'logg'].item() idx = requested.loc[i, 'ccf'].argmax() rv = interface.velocities[idx] # Now, get the CCF height for the N/2 temperatures on either side of Tmax N = roundodd(N) d = defaultdict(list) for T in np.arange(Tmax - 100 * (N - 1) / 2, Tmax + 100 * (N - 1) / 2 + 1, 100): requested = df.loc[(df['T'] == T) & (df.vsini == vsini) & (df['[Fe/H]'] == metal) & (df.logg == logg)] if len(requested) == 0: warnings.warn('No matches for T = {} with star/date = {}/{}!'.format(T, starname, date)) d['Star'].append(starname) d['Date'].append(date) d['Instrument'].append(instrument) d['Temperature'].append(T) d['vsini'].append(vsini) d['logg'].append(logg) d['[Fe/H]'].append(metal) d['rv'].append(rv) d['CCF'].append(np.nan) d['significance'].append(np.nan) continue if len(requested) > 1: requested = requested.sort_values(by='ccf_max').tail(1) # Save the best parameters for this temperature d['Star'].append(starname) d['Date'].append(date) d['Instrument'].append(instrument) d['Temperature'].append(T) d['vsini'].append(requested['vsini'].item()) d['logg'].append(requested['logg'].item()) d['[Fe/H]'].append(requested['[Fe/H]'].item()) idx = np.argmin(np.abs(interface.velocities - rv)) d['rv'].append(rv) ccf = requested['ccf'].item() d['CCF'].append(ccf[idx]) # Measure the detection significance std = mad(ccf) mean = np.median(ccf) d['significance'].append((d['CCF'][-1] - mean) / std) # Do the weighted sum. summary = CCF_Systematics.get_Tmeas(pd.DataFrame(data=d), include_actual=False) # Put the star, date, and instrument back in the dataframe before returning summary['Star'] = starname summary['Date'] = date summary['Instrument'] = instrument summary['addmode'] = addmode return summary
def find_best_pars(df, velocity='highest', vel_arr=np.arange(-900.0, 900.0, 0.1), N=1): """ Find the 'best-fit' parameters for each combination of primary and secondary star :param df: the dataframe to search in :keyword velocity: The velocity to measure the CCF at. The default is 'highest', and uses the maximum of the ccf :keyword vel_arr: The velocities to interpolate each ccf at :keyword N: The number of parameters to return :return: a dataframe with keys of primary, secondary, and the parameters """ # Make sure N is odd if N % 2 == 0: logging.warn('N must be an odd number. Changing N from {} --> {}'.format(N, N + 1)) N += 1 # Get the names of the primary and secondary stars primary_names = pd.unique(df.Primary) secondary_names = pd.unique(df.Secondary) # Find the ccf value at the given velocity def val_fcn(ccf, idx=None, search_indices=None): if idx is None: if search_indices is None: idx = np.argmax(ccf) else: idx = np.argmax(ccf[search_indices]) idx = search_indices[idx] rv = vel_arr[idx] sigma = np.std(ccf[np.abs(vel_arr - rv) > 200]) return ccf[idx], ccf[idx] / sigma, rv if velocity == 'highest': vals = df['CCF'].map(val_fcn) df['ccf_max'] = vals.map(lambda l: l[0]) df['significance'] = vals.map(lambda l: l[1]) df['rv'] = vals.map(lambda l: l[2]) else: # idx = np.argmin(np.abs(vel_arr - velocity)) idx = np.where(np.abs(vel_arr - velocity) <= 5)[0] vals = df['CCF'].map(lambda c: val_fcn(c, search_indices=idx)) df['ccf_max'] = vals.map(lambda l: l[0]) df['significance'] = vals.map(lambda l: l[1]) df['rv'] = vals.map(lambda l: l[2]) #print(df[['Secondary', 'rv']]) # Find the best parameter for each combination d = defaultdict(list) groups = df.groupby(('Primary', 'Secondary')) for group in groups.groups.keys(): primary = group[0] secondary = group[1] g = groups.get_group(group) best = g.loc[g.ccf_max == g.ccf_max.max()] T = best['Temperature'].item() vsini = best['vsini'].item() logg = best['logg'].item() metal = best['[Fe/H]'].item() rv = best['rv'].item() Tmin = T - (N - 1) * 50 Tmax = T + (N - 1) * 50 for Ti in range(Tmin, Tmax + 1, 100): good = g.loc[ (g['Temperature'] == Ti) & (g['vsini'] == vsini) & (g['logg'] == logg) & (g['[Fe/H]'] == metal)] if len(good) == 0: logging.warn('No matches for T = {} with primary/secondary = {}/{}!'.format(Ti, primary, secondary)) d['Primary'].append(primary) d['Secondary'].append(secondary) d['Temperature'].append(Ti) d['vsini'].append(vsini) d['logg'].append(logg) d['[Fe/H]'].append(metal) d['rv'].append(rv) d['CCF'].append(np.nan) d['significance'].append(np.nan) continue # print len(good) best = good.loc[good.ccf_max == good.ccf_max.max()] #best = good if len(best) != 1 or any(np.isnan(best['CCF'].item())): print best print good print good.ccf_max print good.ccf_max.max() continue # Save the best parameters for this temperature d['Primary'].append(primary) d['Secondary'].append(secondary) d['Temperature'].append(best['Temperature'].item()) d['vsini'].append(best['vsini'].item()) d['logg'].append(best['logg'].item()) d['[Fe/H]'].append(best['[Fe/H]'].item()) idx = np.argmin(np.abs(vel_arr - rv)) d['rv'].append(rv) d['CCF'].append(best['CCF'].item()[idx]) # d['rv'].append(best['rv'].item()) #d['CCF'].append(best.ccf_max.item()) # Measure the detection significance std = mad(best.CCF.item()) mean = np.median(best.CCF.item()) d['significance'].append((d['CCF'][-1] - mean) / std) return pd.DataFrame(data=d)