tree wide: way too many changes, im sorry

This commit is contained in:
2024-08-05 13:00:54 +02:00
parent 51e0d81c28
commit 7f014938a9
8 changed files with 1604 additions and 87 deletions
+4
View File
@@ -62,6 +62,7 @@ class AstrodataGUI(QtWidgets.QMainWindow):
self.btOpenSavedFlareCountFile.clicked.connect(self.btOpenSavedFlareCountFileClicked) self.btOpenSavedFlareCountFile.clicked.connect(self.btOpenSavedFlareCountFileClicked)
self.flaredetectorPreview.periodsCalculated.connect(self.updatePeriods) self.flaredetectorPreview.periodsCalculated.connect(self.updatePeriods)
self.flaredetectorPreview.epochCalculated.connect(self.updateEpochPeriod)
self.setupCustomSimbadQueries() self.setupCustomSimbadQueries()
self.show() self.show()
@@ -205,6 +206,9 @@ class AstrodataGUI(QtWidgets.QMainWindow):
def updatePeriods(self, periods: list): def updatePeriods(self, periods: list):
self.edPlotFoldPeriod.setText(str(periods[0].value)) self.edPlotFoldPeriod.setText(str(periods[0].value))
def updateEpochPeriod(self, epoch: float):
self.edPlotFoldEpochTime.setText(str(epoch))
def btCombineSeqClicked(self): def btCombineSeqClicked(self):
#self.gbPlotOptionsNormalize.setEnabled(False) #self.gbPlotOptionsNormalize.setEnabled(False)
mainName = self.listDownloaded.currentItem().text() mainName = self.listDownloaded.currentItem().text()
+27 -5
View File
@@ -4,32 +4,54 @@ import concurrent.futures
from ..util.MinimalLightCurve import read from ..util.MinimalLightCurve import read
import pandas as pd import pandas as pd
from ..flaredetector.flaredetector import calculateFlareFitsForLightcurve from ..flaredetector.flaredetector import calculateFlareFitsForLightcurve
from ..flaredetector.util import getTotalValidDataInSeconds from ..flaredetector.util import *
def getFlareCount(filesDict): def getFlareCount(filesDict):
lc = read(filesDict["FilePath"]) lc = read(filesDict["FilePath"])
lc.flux = lc["sap_flux"] lc.flux = lc["sap_flux"]
lc.flux_err = lc["sap_flux_err"] lc.flux_err = lc["sap_flux_err"]
sapPeaks, sapFits = calculateFlareFitsForLightcurve(lc.flatten(), minimalLC=True) sapPeaks, sapFits = calculateFlareFitsForLightcurve(lc.flatten())
sapValSec, sapTds = getTotalValidDataInSeconds(lc, "sap_flux") sapValSec, sapTds = getTotalValidDataInSeconds(lc, "sap_flux")
sapPeriodogram = lc.to_periodogram()
sapPeakPeriod = sapPeriodogram.period[findMaxIndices(sapPeriodogram, num=4, distance=100, sortByHighest=True)[0]]
sapEpochTime = getEpochTime(lc)
sapFoldedLC = lc.fold(period=sapPeakPeriod, epoch_time=sapEpochTime)
sapPhase, sapSineFit, sapFitType = getFoldedBestFit(sapFoldedLC)
sapMinima, sapMaxima = getFoldedFitPeakValley(sapSineFit)
sapminPhasesBoundsIndices, sapmaxPhasesBoundsIndices = getPhaseRangesNearPeak((sapMinima, sapMaxima), sapPhase)
lc.flux = lc["pdcsap_flux"] lc.flux = lc["pdcsap_flux"]
lc.flux_err = lc["pdcsap_flux_err"] lc.flux_err = lc["pdcsap_flux_err"]
pdcsapPeaks, pdcsapFits = calculateFlareFitsForLightcurve(lc.flatten(), minimalLC=True) pdcsapPeaks, pdcsapFits = calculateFlareFitsForLightcurve(lc.flatten())
pdcsapValSec, pdcsapTds = getTotalValidDataInSeconds(lc, "pdcsap_flux") pdcsapValSec, pdcsapTds = getTotalValidDataInSeconds(lc, "pdcsap_flux")
pdcsapPeriodogram = lc.to_periodogram()
pdcsapPeakPeriod = pdcsapPeriodogram.period[findMaxIndices(pdcsapPeriodogram, num=4, distance=100, sortByHighest=True)[0]]
pdcsapEpochTime = getEpochTime(lc)
pdcsapFoldedLC = lc.fold(period=pdcsapPeakPeriod, epoch_time=pdcsapEpochTime)
pdcsapPhase, pdcsapSineFit, pdcsapFitType = getFoldedBestFit(pdcsapFoldedLC)
pdcsapMinima, pdcsapMaxima = getFoldedFitPeakValley(pdcsapSineFit)
pdcsapminPhasesBoundsIndices, pdcsapmaxPhasesBoundsIndices = getPhaseRangesNearPeak((pdcsapMinima, pdcsapMaxima), pdcsapPhase)
filesDict["sapPeaks"] = sapPeaks filesDict["sapPeaks"] = sapPeaks
filesDict["sapPeaksCount"] = len(sapPeaks) filesDict["sapPeaksCount"] = len(sapPeaks)
filesDict["sapFits"] = sapFits filesDict["sapFits"] = sapFits
filesDict["sapValidTimespans"] = sapTds filesDict["sapValidTimespans"] = sapTds
filesDict["sapValidSeconds"] = sapValSec filesDict["sapValidSeconds"] = sapValSec
filesDict["sapPeriod"] = sapPeakPeriod.value
filesDict["sapPeriodMinima"] = sapMinima
filesDict["sapPeriodMinimaBoundaries"] = sapminPhasesBoundsIndices
filesDict["sapPeriodMaxima"] = sapMaxima
filesDict["sapPeriodMaximaBoundaries"] = sapmaxPhasesBoundsIndices
filesDict["pdcsapPeaks"] = pdcsapPeaks filesDict["pdcsapPeaks"] = pdcsapPeaks
filesDict["pdcsapPeaksCount"] = len(pdcsapPeaks) filesDict["pdcsapPeaksCount"] = len(pdcsapPeaks)
filesDict["pdcsapFits"] = pdcsapFits filesDict["pdcsapFits"] = pdcsapFits
filesDict["pdcsapValidTimespans"] = pdcsapTds filesDict["pdcsapValidTimespans"] = pdcsapTds
filesDict["pdcsapValidSeconds"] = pdcsapValSec filesDict["pdcsapValidSeconds"] = pdcsapValSec
filesDict["pdcsapPeriod"] = pdcsapPeakPeriod.value
filesDict["pdcsapPeriodMinima"] = pdcsapMinima
filesDict["pdcsapPeriodMinimaBoundaries"] = pdcsapminPhasesBoundsIndices
filesDict["pdcsapPeriodMaxima"] = pdcsapMaxima
filesDict["pdcsapPeriodMaximaBoundaries"] = pdcsapmaxPhasesBoundsIndices
del lc del lc
if(filesDict["StarName"] == "CD-51 13128" and filesDict["Sequence"] == 1):
print(filesDict)
return filesDict return filesDict
class CalcAllFlaresThread(QThread): class CalcAllFlaresThread(QThread):
+145 -2
View File
@@ -43,6 +43,8 @@ class FlareSummaryPlotGUI(QWidget):
self.btShowFlaresPerStar.clicked.connect(self.btShowFlaresPerStarClicked) self.btShowFlaresPerStar.clicked.connect(self.btShowFlaresPerStarClicked)
self.btShowFlaresPerStarNormalized = QPushButton("Show Flares/Star Normalized") self.btShowFlaresPerStarNormalized = QPushButton("Show Flares/Star Normalized")
self.btShowFlaresPerStarNormalized.clicked.connect(self.btShowFlaresPerStarNormalizedClicked) self.btShowFlaresPerStarNormalized.clicked.connect(self.btShowFlaresPerStarNormalizedClicked)
self.btShowPeriods = QPushButton("Show Mean Periods")
self.btShowPeriods.clicked.connect(self.btShowPeriodsClicked)
self.cbKepler = QCheckBox("Kepler") self.cbKepler = QCheckBox("Kepler")
self.cbKepler.setChecked(True) self.cbKepler.setChecked(True)
@@ -68,6 +70,7 @@ class FlareSummaryPlotGUI(QWidget):
self.buttonGridLayout.addWidget(self.btShowFlaresPerFile, 0, 1) self.buttonGridLayout.addWidget(self.btShowFlaresPerFile, 0, 1)
self.buttonGridLayout.addWidget(self.btShowFlaresPerStar, 0, 2) self.buttonGridLayout.addWidget(self.btShowFlaresPerStar, 0, 2)
self.buttonGridLayout.addWidget(self.btShowFlaresPerStarNormalized, 0, 3) self.buttonGridLayout.addWidget(self.btShowFlaresPerStarNormalized, 0, 3)
self.buttonGridLayout.addWidget(self.btShowPeriods, 0, 4)
self.buttonGridLayout.addWidget(QLabel("Sources: "), 1, 0) self.buttonGridLayout.addWidget(QLabel("Sources: "), 1, 0)
self.buttonGridLayout.addWidget(self.cbKepler, 1, 1) self.buttonGridLayout.addWidget(self.cbKepler, 1, 1)
@@ -169,7 +172,18 @@ class FlareSummaryPlotGUI(QWidget):
'pdcsapFits', 'pdcsapFits',
'pdcsapPeaks', 'pdcsapPeaks',
'sapFits', 'sapFits',
'sapPeaks']) 'sapPeaks',
'sapPeriod',
'sapPeriodMinima',
'sapPeriodMinimaBoundaries',
'sapPeriodMaxima',
'sapPeriodMaximaBoundaries',
'pdcsapValidTimespans',
'pdcsapPeriod',
'pdcsapPeriodMinima',
'pdcsapPeriodMinimaBoundaries',
'pdcsapPeriodMaxima',
'pdcsapPeriodMaximaBoundaries'])
showSourceFilter = np.full(len(data), False) showSourceFilter = np.full(len(data), False)
@@ -248,7 +262,19 @@ class FlareSummaryPlotGUI(QWidget):
'pdcsapFits', 'pdcsapFits',
'pdcsapPeaks', 'pdcsapPeaks',
'sapFits', 'sapFits',
'sapPeaks']) 'sapValidTimespans',
'sapPeaks',
'sapPeriod',
'sapPeriodMinima',
'sapPeriodMinimaBoundaries',
'sapPeriodMaxima',
'sapPeriodMaximaBoundaries',
'pdcsapValidTimespans',
'pdcsapPeriod',
'pdcsapPeriodMinima',
'pdcsapPeriodMinimaBoundaries',
'pdcsapPeriodMaxima',
'pdcsapPeriodMaximaBoundaries'])
showSourceFilter = np.full(len(data), False) showSourceFilter = np.full(len(data), False)
@@ -330,3 +356,120 @@ class FlareSummaryPlotGUI(QWidget):
self.figureAxis.set_xlabel("Star Number") self.figureAxis.set_xlabel("Star Number")
self.figureAxis.legend() self.figureAxis.legend()
self.figure.canvas.draw_idle() self.figure.canvas.draw_idle()
def btShowPeriodsClicked(self):
data = self.starFLareDictList.drop(columns=['Distance',
'DistanceUnit',
'FilePath',
'RotVel',
'RotVelUnit',
'Sequence',
'pdcsapFits',
'pdcsapPeaks',
'sapFits',
'sapValidTimespans',
'sapPeaks',
'sapPeriodMinima',
'sapPeriodMinimaBoundaries',
'sapPeriodMaxima',
'sapPeriodMaximaBoundaries',
'pdcsapValidTimespans',
'pdcsapPeriodMinima',
'pdcsapPeriodMinimaBoundaries',
'pdcsapPeriodMaxima',
'pdcsapPeriodMaximaBoundaries',
'sapPeaksCount',
'pdcsapPeaksCount',
'sapValidSeconds',
'pdcsapValidSeconds'])
showSourceFilter = np.full(len(data), False)
if(self.cbKepler.isChecked()):
showKepler = data["Source"] == "Kepler"
showSourceFilter |= showKepler
if(self.cbK2.isChecked()):
showK2 = data["Source"] == "K2"
showSourceFilter |= showK2
if(self.cbTESS.isChecked()):
showTESS = data["Source"] == "TESS"
showSourceFilter |= showTESS
print(data["sapPeriod"])
data = data[showSourceFilter].groupby(["StarName", "SpType"],
as_index=False).agg({"sapPeriod": "mean",
"pdcsapPeriod": "mean"})
x = np.arange(start=1, stop=len(data)+1, step=1, dtype=int)
if(self.cbSpTypeL.isChecked()):
Lfilter = data["SpType"].str.startswith("L")
Lfilter &= showSourceFilter
if(self.cbSpTypeM.isChecked()):
Mfilter = data["SpType"].str.startswith("M")
Mfilter &= showSourceFilter
if(self.cbSpTypeK.isChecked()):
Kfilter = data["SpType"].str.startswith("K")
Kfilter &= showSourceFilter
if(self.cbSpTypeG.isChecked()):
Gfilter = data["SpType"].str.startswith("G")
Gfilter &= showSourceFilter
if(self.cbSpTypeF.isChecked()):
Ffilter = data["SpType"].str.startswith("F")
Ffilter &= showSourceFilter
if(self.cbSpTypeUnknown.isChecked()):
Unknownfilter = data["SpType"].str.startswith("-")
Unknownfilter &= showSourceFilter
self.figureAxis.clear()
if(self.cbSpTypeL.isChecked()):
if(np.any(Lfilter)):
self.figureAxis.scatter(x[Lfilter], data[Lfilter]["sapPeriod"],
marker="o", color="brown", label="L SAP Period")
self.figureAxis.scatter(x[Lfilter], data[Lfilter]["pdcsapPeriod"],
marker="x", color="brown", label="L PDCSAP Period")
if(self.cbSpTypeM.isChecked()):
if(np.any(Mfilter)):
self.figureAxis.scatter(x[Mfilter], data[Mfilter]["sapPeriod"],
marker="o", color="red", label="M SAP Period")
self.figureAxis.scatter(x[Mfilter], data[Mfilter]["pdcsapPeriod"],
marker="x", color="red", label="M PDCSAP Period")
if(self.cbSpTypeK.isChecked()):
if(np.any(Kfilter)):
self.figureAxis.scatter(x[Kfilter], data[Kfilter]["sapPeriod"],
marker="o", color="orange", label="K SAP Period")
self.figureAxis.scatter(x[Kfilter], data[Kfilter]["pdcsapPeriod"],
marker="x", color="orange", label="K PDCSAP Period")
if(self.cbSpTypeG.isChecked()):
if(np.any(Gfilter)):
self.figureAxis.scatter(x[Gfilter], data[Gfilter]["sapPeriod"],
marker="o", color="yellow", label="G SAP Period")
self.figureAxis.scatter(x[Gfilter], data[Gfilter]["pdcsapPeriod"],
marker="x", color="yellow", label="G PDCSAP Period")
if(self.cbSpTypeF.isChecked()):
if(np.any(Ffilter)):
self.figureAxis.scatter(x[Ffilter], data[Ffilter]["sapPeriod"],
marker="o", color="greenyellow", label="F SAP Period")
self.figureAxis.scatter(x[Ffilter], data[Ffilter]["pdcsapPeriod"],
marker="x", color="greenyellow", label="F PDCSAP Period")
if(self.cbSpTypeUnknown.isChecked()):
if(np.any(Unknownfilter)):
self.figureAxis.scatter(x[Unknownfilter], data[Unknownfilter]["sapPeriod"],
marker="o", color="gray", label="Unknown SAP Period")
self.figureAxis.scatter(x[Unknownfilter], data[Unknownfilter]["pdcsapPeriod"],
marker="x", color="gray", label="Unknown PDCSAP Period")
self.figureAxis.set_ylabel("Period / days")
self.figureAxis.set_xlabel("Star Number")
self.figureAxis.legend()
self.figure.canvas.draw_idle()
pass
def btShowNumMinimaMaximaClicked(self):
pass
def btShowFlaresInMinimaMaximaClicked(self):
pass
def btShowFlaresInMinimaMaximaPerMinimaMaximaClicked(self):
pass
+1 -1
View File
@@ -254,7 +254,7 @@ class FlaredetectorWidget(QtWidgets.QWidget):
cycle, foldedIndex = convertStarndardIndexToFoldedIndex(lc, peak["StandardIndex"]) cycle, foldedIndex = convertStarndardIndexToFoldedIndex(lc, peak["StandardIndex"])
self.figureAxis.plot(lc.phase[lc.cycle == cycle][foldedIndex].value, self.figureAxis.plot(lc.phase[lc.cycle == cycle][foldedIndex].value,
lc.flux[lc.cycle == cycle][foldedIndex], "x", color="red") lc.flux[lc.cycle == cycle][foldedIndex], "x", color="red")
phase, sineFit = getFoldedBestFit(lc) phase, sineFit, _ = getFoldedBestFit(lc)
self.figureAxis.plot(phase, sineFit, color="red") self.figureAxis.plot(phase, sineFit, color="red")
minPhasesBoundsIndices, maxPhasesBoundsIndices = getPhaseRangesNearPeak(getFoldedFitPeakValley(sineFit), phase) minPhasesBoundsIndices, maxPhasesBoundsIndices = getPhaseRangesNearPeak(getFoldedFitPeakValley(sineFit), phase)
plotPhaseRangesNearPeak((minPhasesBoundsIndices, maxPhasesBoundsIndices), phase, ax=self.figureAxis) plotPhaseRangesNearPeak((minPhasesBoundsIndices, maxPhasesBoundsIndices), phase, ax=self.figureAxis)
+9 -2
View File
@@ -37,12 +37,19 @@ def calculateFitForFlare(lightcurve, peakIndex):
popt, pcov = curve_fit(lambda x, l, w: GaussExpo(x, Peak, l, w), flareFitDataPoints, flareData) popt, pcov = curve_fit(lambda x, l, w: GaussExpo(x, Peak, l, w), flareFitDataPoints, flareData)
pcovDiag = np.diag(pcov) pcovDiag = np.diag(pcov)
fit = GaussExpo(flareFitDataPoints, Peak, *popt) fit = GaussExpo(flareFitDataPoints, Peak, *popt)
rss = compureRSS(fit, flareData)
aic = computeAIC(rss, 2, len(flareData))
bic = computeBIC(rss, 2, len(flareData))
r2 = 1 - rss/computeTotalSoS(flareData)
#print("Fit r2: ", r2)
if(r2 < 0.8):
raise Exception("Fit varies too much")
flareFitDataPoints = flareFitDataPoints + peakIndex flareFitDataPoints = flareFitDataPoints + peakIndex
return np.array(Peak+1), flareFitDataPoints, np.array(fit+1) return np.array(Peak+1), flareFitDataPoints, np.array(fit+1)
def calculateFlareFitsForLightcurve(lightcurve, num=100, distance=1, height=0.05, minimalLC=False): def calculateFlareFitsForLightcurve(lightcurve, num=100, distance=1, height=0.05):
maxIndices = findMaxIndices(lightcurve, num, distance=distance, height=height, maxIndices = findMaxIndices(lightcurve, num, distance=distance, height=height,
sortByHighest=True, minimalLC=minimalLC) sortByHighest=True)
peaks = [] peaks = []
fits = [] fits = []
for index in maxIndices: for index in maxIndices:
+111 -76
View File
@@ -1,21 +1,15 @@
import numpy as np import numpy as np
from numpy.polynomial.polynomial import Polynomial from numpy.polynomial.polynomial import Polynomial
from scipy.signal import find_peaks from scipy.signal import find_peaks, argrelextrema
from scipy.optimize import curve_fit from scipy.optimize import curve_fit
def findMaxIndices(lc, num=3, distance=100, height=(None, None), sortByHighest=False, def findMaxIndices(lc, num=3, distance=100, height=(None, None), sortByHighest=False):
minimalLC=False): if(hasattr(lc, "power")):
if(minimalLC): data = lc.power
elif(hasattr(lc, "flux")):
data = lc.flux data = lc.flux
else: else:
from lightkurve.periodogram import Periodogram as OrigPG return np.zeros(num)-1
from lightkurve.lightcurve import LightCurve as OrigLC
if(isinstance(lc, OrigPG)):
data = lc.power
elif(isinstance(lc, OrigLC)):
data = lc.flux
else:
return np.zeros(num)-1
peak_indices, peak_dict = find_peaks(data, height=height, peak_indices, peak_dict = find_peaks(data, height=height,
distance=distance) distance=distance)
peak_heights = peak_dict["peak_heights"] peak_heights = peak_dict["peak_heights"]
@@ -88,16 +82,22 @@ def singleSine(t, A, w, p, c):
def fitSingleSine(phase, flux): def fitSingleSine(phase, flux):
initGuess = [np.ptp(flux)/2, 2 * np.pi / (np.max(phase) - np.min(phase)), 0, np.mean(flux)] initGuess = [np.ptp(flux)/2, 2 * np.pi / (np.max(phase) - np.min(phase)), 0, np.mean(flux)]
popt, _ = curve_fit(singleSine, phase, flux, p0=initGuess, maxfev=100000) popt, _ = curve_fit(singleSine, phase, flux, p0=initGuess, maxfev=300)
return singleSine(phase, *popt) return singleSine(phase, *popt)
def polynomial(phase, flux, degree):
return Polynomial.fit(phase, flux, degree).convert().coef
def fitPolynomial(phase, flux, degree): def fitPolynomial(phase, flux, degree):
return sum(p * phase**i for i, p in enumerate(Polynomial.fit(phase, flux, degree).convert().coef)) return sum(p * phase**i for i, p in enumerate(polynomial(phase, flux, degree)))
def compureRSS(fit, flux): def compureRSS(fit, flux):
residuals = flux - fit residuals = flux - fit
return np.sum(residuals**2) return np.sum(residuals**2)
def computeTotalSoS(flux):
return np.sum((flux - np.mean(flux))**2)
def computeAIC(rss, numParams, numDataPoints): def computeAIC(rss, numParams, numDataPoints):
return 2 * numParams + numDataPoints * np.log(rss/numDataPoints) return 2 * numParams + numDataPoints * np.log(rss/numDataPoints)
@@ -113,30 +113,46 @@ def getFoldedBestFit(foldedLc):
flux = foldedLc.normalize().flux flux = foldedLc.normalize().flux
phase = foldedLc.phase[filt].value phase = foldedLc.phase[filt].value
flux = flux[filt] flux = flux[filt]
#popt, pcov = curve_fit(sine, phase, flux[filt], maxfev=100000) polyDegree = 7
singleFit = fitSingleSine(phase, flux) fitThreshold = 0.0
polyFit = fitPolynomial(phase, flux, 10) smoothed_flux = np.convolve(flux, np.ones(len(flux)//100)/(len(flux)//100), mode="valid")
peaks, _ = find_peaks(smoothed_flux, height=np.mean(smoothed_flux))
#print("Num peaks: ", peaks)
singleRSS = compureRSS(singleFit, flux) try:
polyRSS = compureRSS(polyFit, flux) retFit = fitSingleSine(phase, flux)
r2 = 1 - (compureRSS(retFit, flux)/computeTotalSoS(flux))
fitType = "sine"
#print("R2: ", r2)
if(r2 < fitThreshold):
raise Exception("Sine fit is suboptimal")
#print("Single sine preferred")
except:
retFit = fitPolynomial(phase, flux, polyDegree)
fitType = "poly"
#print("Polynomial preferred")
singleAIC = computeAIC(singleRSS, 4, len(flux)) retFit *= multi
singleBIC = computeBIC(singleRSS, 4, len(flux)) return phase, retFit, fitType
polyAIC = computeAIC(polyRSS, 10, len(flux))
polyBIC = computeBIC(polyRSS, 10, len(flux))
if (singleAIC < polyAIC and singleBIC < polyBIC):
print("Single sine preferred")
sineFit = singleFit
else:
print("Polynomial preferred")
sineFit = polyFit
sineFit *= multi
return phase, sineFit
def getFoldedFitPeakValley(sineFit): def getFoldedFitPeakValley(sineFit):
return np.argmin(sineFit), np.argmax(sineFit) minima = argrelextrema(sineFit, np.less)[0]
maxima = argrelextrema(sineFit, np.greater)[0]
#print(minima)
#print(maxima)
if(len(minima) > 0 and len(maxima) > 0):
if(minima[0] < maxima[0] and minima[-1] > maxima[-1]):
if(sineFit[minima[0]] > sineFit[minima[-1]]):
minima = np.delete(minima, 0)
else:
minima = np.delete(minima, -1)
elif(maxima[0] < minima[0] and maxima[-1] > minima[-1]):
if(sineFit[maxima[0]] > sineFit[maxima[-1]]):
maxima = np.delete(maxima, -1)
else:
maxima = np.delete(maxima, 0)
return minima, maxima
def findNearestIndexOfValue(array, value): def findNearestIndexOfValue(array, value):
array = np.asarray(array) array = np.asarray(array)
@@ -144,57 +160,76 @@ def findNearestIndexOfValue(array, value):
return idx return idx
def getPhaseRangesNearPeak(maxArgs, phase): def getPhaseRangesNearPeak(maxArgs, phase):
minPhase = phase[maxArgs[0]] minPhases = phase[maxArgs[0]]
maxPhase = phase[maxArgs[1]] maxPhases = phase[maxArgs[1]]
totalPhase = abs(phase[0]) + abs(phase[-1]) totalPhase = abs(phase[0]) + abs(phase[-1])
phasePart = totalPhase * 0.3 / 2 phasePart = totalPhase * 0.3 / (len(minPhases) + len(maxPhases))
minPhaseBounds = [] minPhasesBounds = []
if(minPhase - phasePart < phase[0]): for minPhase in minPhases:
minPhaseBounds.append((phase[0], minPhase + phasePart)) minPhaseBounds = []
minPhaseBounds.append((phase[-1] + (minPhase - phasePart - phase[0]), phase[-1])) if(minPhase - phasePart < phase[0]):
elif(minPhase + phasePart > phase[-1]): minPhaseBounds.append((phase[0], minPhase + phasePart))
minPhaseBounds.append((minPhase - phasePart, phase[-1])) minPhaseBounds.append((phase[-1] + (minPhase - phasePart - phase[0]), phase[-1]))
minPhaseBounds.append((phase[0], phase[0] + (minPhase + phasePart - phase[-1]))) elif(minPhase + phasePart > phase[-1]):
else: minPhaseBounds.append((minPhase - phasePart, phase[-1]))
minPhaseBounds.append((minPhase - phasePart, minPhase + phasePart)) minPhaseBounds.append((phase[0], phase[0] + (minPhase + phasePart - phase[-1])))
else:
minPhaseBounds.append((minPhase - phasePart, minPhase + phasePart))
minPhasesBounds.append(minPhaseBounds)
maxPhaseBounds = [] maxPhasesBounds = []
if(maxPhase - phasePart < phase[0]): for maxPhase in maxPhases:
maxPhaseBounds.append((phase[0], maxPhase + phasePart)) maxPhaseBounds = []
maxPhaseBounds.append((phase[-1] + (maxPhase - phasePart - phase[0]), phase[-1])) if(maxPhase - phasePart < phase[0]):
elif(maxPhase + phasePart > phase[-1]): maxPhaseBounds.append((phase[0], maxPhase + phasePart))
maxPhaseBounds.append((maxPhase - phasePart, phase[-1])) maxPhaseBounds.append((phase[-1] + (maxPhase - phasePart - phase[0]), phase[-1]))
maxPhaseBounds.append((phase[0], phase[0] + (maxPhase + phasePart - phase[-1]))) elif(maxPhase + phasePart > phase[-1]):
else: maxPhaseBounds.append((maxPhase - phasePart, phase[-1]))
maxPhaseBounds.append((maxPhase - phasePart, maxPhase + phasePart)) maxPhaseBounds.append((phase[0], phase[0] + (maxPhase + phasePart - phase[-1])))
else:
maxPhaseBounds.append((maxPhase - phasePart, maxPhase + phasePart))
maxPhasesBounds.append(maxPhaseBounds)
minPhaseBoundsIndices = [] minPhasesBoundsIndices = []
for l in minPhaseBounds: for minPhaseBounds in minPhasesBounds:
minPhaseBoundsIndices.append([findNearestIndexOfValue(phase, lv) for lv in l]) minPhaseBoundsIndices = []
for l in minPhaseBounds:
minPhaseBoundsIndices.append([findNearestIndexOfValue(phase, lv) for lv in l])
minPhasesBoundsIndices.append(minPhaseBoundsIndices)
maxPhaseBoundsIndices = [] maxPhasesBoundsIndices = []
for l in maxPhaseBounds: for maxPhaseBounds in maxPhasesBounds:
maxPhaseBoundsIndices.append([findNearestIndexOfValue(phase, lv) for lv in l]) maxPhaseBoundsIndices = []
for l in maxPhaseBounds:
maxPhaseBoundsIndices.append([findNearestIndexOfValue(phase, lv) for lv in l])
maxPhasesBoundsIndices.append(maxPhaseBoundsIndices)
return minPhaseBoundsIndices, maxPhaseBoundsIndices return minPhasesBoundsIndices, maxPhasesBoundsIndices
def plotPhaseRangesNearPeak(indices, phase, ax=None): def plotPhaseRangesNearPeak(indices, phase, ax=None):
minPhaseBoundsIndices = indices[0] minPhasesBoundsIndices = indices[0]
maxPhaseBoundsIndices = indices[1] maxPhasesBoundsIndices = indices[1]
if(ax is None): if(ax is None):
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
for pair in minPhaseBoundsIndices: for minPhaseBoundsIndices in minPhasesBoundsIndices:
for l in pair: for pair in minPhaseBoundsIndices:
plt.axes.axvline(phase[l], color="violet") for l in pair:
for pair in maxPhaseBoundsIndices: plt.axes.axvline(phase[l], color="violet")
for l in pair: for maxPhaseBoundsIndices in maxPhasesBoundsIndices:
plt.axes.axvline(phase[l], color="orange") for pair in maxPhaseBoundsIndices:
for l in pair:
plt.axes.axvline(phase[l], color="orange")
else: else:
for pair in minPhaseBoundsIndices: for minPhaseBoundsIndices in minPhasesBoundsIndices:
for l in pair: for pair in minPhaseBoundsIndices:
ax.axvline(phase[l], color="violet") for l in pair:
for pair in maxPhaseBoundsIndices: ax.axvline(phase[l], color="violet")
for l in pair: for maxPhaseBoundsIndices in maxPhasesBoundsIndices:
ax.axvline(phase[l], color="orange") for pair in maxPhaseBoundsIndices:
for l in pair:
ax.axvline(phase[l], color="orange")
def getEpochTime(lc):
return lc.time.value[argrelextrema(np.asarray(lc.flux.value), np.less, order=500)[0]][0]
+203
View File
@@ -17,6 +17,10 @@ from astropy.time.formats import TimeFromEpoch
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class LightkurveWarning(Warning):
"""Class for all Lightkurve warnings."""
pass
class TimeBKJD(TimeFromEpoch): class TimeBKJD(TimeFromEpoch):
name = 'bkjd' name = 'bkjd'
unit = 1.0 unit = 1.0
@@ -402,6 +406,14 @@ class TessQualityFlags(QualityFlags):
32768: "Insufficient Targets for Error Correction Exclude", 32768: "Insufficient Targets for Error Correction Exclude",
} }
def validate_method(method, supported_methods):
method = method.lower()
if method in supported_methods:
return method
raise ValueError(
"method '{}' is not supported; "
"must be one of {}".format(method, supported_methods)
)
def _is_dict_like(data1): def _is_dict_like(data1):
return hasattr(data1, "keys") and callable(getattr(data1, "keys")) return hasattr(data1, "keys") and callable(getattr(data1, "keys"))
@@ -879,6 +891,195 @@ class LightCurve(TimeSeries):
return flatten_lc, trend_lc return flatten_lc, trend_lc
return flatten_lc return flatten_lc
def to_periodogram(self, method="lombscargle", **kwargs):
supported_methods = ["ls", "bls", "lombscargle", "boxleastsquares"]
method = validate_method(method.replace(" ", ""), supported_methods)
if method in ["bls", "boxleastsquares"]:
from .MinimalPeriodogram import BoxLeastSquaresPeriodogram
return BoxLeastSquaresPeriodogram.from_lightcurve(lc=self, **kwargs)
else:
from .MinimalPeriodogram import LombScarglePeriodogram
return LombScarglePeriodogram.from_lightcurve(lc=self, **kwargs)
def fold(
self,
period=None,
epoch_time=None,
epoch_phase=0,
wrap_phase=None,
normalize_phase=False,
):
# Lightkurve v1.x assumed that `period` was given in days if no unit
# was specified. We maintain this behavior for backwards-compatibility.
if period is not None and not isinstance(period, Quantity):
period *= u.day
if epoch_time is not None and not isinstance(epoch_time, Time):
epoch_time = Time(
epoch_time, format=self.time.format, scale=self.time.scale
)
if (
epoch_phase is not None
and not isinstance(epoch_phase, Quantity)
and not normalize_phase
):
epoch_phase *= u.day
if wrap_phase is not None and not isinstance(wrap_phase, Quantity):
wrap_phase *= u.day
# Warn if `epoch_time` appears to use the wrong format
if epoch_time is not None and epoch_time.value > 2450000:
if self.time.format == "bkjd":
warnings.warn(
"`epoch_time` appears to be given in JD, "
"however the light curve time uses BKJD "
"(i.e. JD - 2454833).",
LightkurveWarning,
)
elif self.time.format == "btjd":
warnings.warn(
"`epoch_time` appears to be given in JD, "
"however the light curve time uses BTJD "
"(i.e. JD - 2457000).",
LightkurveWarning,
)
ts = super().fold(
period=period,
epoch_time=epoch_time,
epoch_phase=epoch_phase,
wrap_phase=wrap_phase,
normalize_phase=normalize_phase,
)
# The folded time would pass the `TimeSeries` validation check if
# `normalize_phase=True`, so creating a `FoldedLightCurve` object
# requires the following three-step workaround:
# 1. Give the folded light curve a valid time column again
with ts._delay_required_column_checks():
folded_time = ts.time.copy()
ts.remove_column("time")
ts.add_column(self.time, name="time", index=0)
# 2. Create the folded object
lc = FoldedLightCurve(data=ts)
# 3. Restore the folded time
with lc._delay_required_column_checks():
lc.remove_column("time")
lc.add_column(folded_time, name="time", index=0)
# Add extra column and meta data specific to FoldedLightCurve
lc.add_column(
self.time.copy(), name="time_original", index=len(self._required_columns)
)
lc.meta["PERIOD"] = period
lc.meta["EPOCH_TIME"] = epoch_time
lc.meta["EPOCH_PHASE"] = epoch_phase
lc.meta["WRAP_PHASE"] = wrap_phase
lc.meta["NORMALIZE_PHASE"] = normalize_phase
lc.sort("time")
return lc
def normalize(self, unit="unscaled"):
validate_method(unit, ["unscaled", "percent", "ppt", "ppm"])
median_flux = np.nanmedian(self.flux)
std_flux = np.nanstd(self.flux)
# If the median flux is within half a standard deviation from zero, the
# light curve is likely zero-centered and normalization makes no sense.
if (median_flux == 0) or (
np.isfinite(std_flux) and (np.abs(median_flux) < 0.5 * std_flux)
):
warnings.warn(
"The light curve appears to be zero-centered "
"(median={:.2e} +/- {:.2e}); `normalize()` will divide "
"the light curve by a value close to zero, which is "
"probably not what you want."
"".format(median_flux, std_flux),
LightkurveWarning,
)
# If the median flux is negative, normalization will invert the light
# curve and makes no sense.
if median_flux < 0:
warnings.warn(
"The light curve has a negative median flux ({:.2e});"
" `normalize()` will therefore divide by a negative "
"number and invert the light curve, which is probably"
"not what you want".format(median_flux),
LightkurveWarning,
)
# Create a new light curve instance and normalize its values
lc = self.copy()
lc.flux = lc.flux / median_flux
lc.flux_err = lc.flux_err / median_flux
if not lc.flux.unit:
lc.flux *= u.dimensionless_unscaled
if not lc.flux_err.unit:
lc.flux_err *= u.dimensionless_unscaled
# Set the desired relative (dimensionless) units
if unit == "percent":
lc.flux = lc.flux.to(u.percent)
lc.flux_err = lc.flux_err.to(u.percent)
elif unit in ("ppt", "ppm"):
lc.flux = lc.flux.to(unit)
lc.flux_err = lc.flux_err.to(unit)
lc.meta["NORMALIZED"] = True
return lc
def remove_nans(self, column: str = "flux"):
return self[~np.isnan(self[column])] # This will return a sliced copy
class FoldedLightCurve(LightCurve):
@property
def phase(self):
"""Alias for `LightCurve.time`."""
return self.time
@property
def cycle(self):
cycle_epoch_start = self.epoch_time - self.period / 2
result = np.asarray(np.floor(((self.time_original - cycle_epoch_start) / self.period).value), dtype=int)
result = result - result.min()
return result
@property
def odd_mask(self):
return self.cycle % 2 == 1
@property
def even_mask(self):
return ~self.odd_mask
def _set_xlabel(self, kwargs):
if "xlabel" not in kwargs:
kwargs["xlabel"] = "Phase"
if isinstance(self.time, TimeDelta):
kwargs["xlabel"] += f" [{self.time.format.upper()}]"
return kwargs
def plot(self, **kwargs):
kwargs = self._set_xlabel(kwargs)
return super(FoldedLightCurve, self).plot(**kwargs)
def scatter(self, **kwargs):
kwargs = self._set_xlabel(kwargs)
return super(FoldedLightCurve, self).scatter(**kwargs)
def errorbar(self, **kwargs):
kwargs = self._set_xlabel(kwargs)
return super(FoldedLightCurve, self).errorbar(**kwargs)
def plot_river(self, **kwargs):
ax = super(FoldedLightCurve, self).plot_river(
period=self.period, epoch_time=self.epoch_time, **kwargs
)
return ax
class KeplerLightCurve(LightCurve): class KeplerLightCurve(LightCurve):
"""Subclass of :class:`LightCurve <lightkurve.lightcurve.LightCurve>` """Subclass of :class:`LightCurve <lightkurve.lightcurve.LightCurve>`
to represent data from NASA's Kepler and K2 mission.""" to represent data from NASA's Kepler and K2 mission."""
@@ -1418,3 +1619,5 @@ try:
except registry.IORegistryError as exc: except registry.IORegistryError as exc:
print(exc) print(exc)
pass # necessary to enable autoreload during debugging pass # necessary to enable autoreload during debugging
File diff suppressed because it is too large Load Diff