Coverage for python/lsst/sims/maf/metrics/phaseGapMetric.py : 23%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1import numpy as np
2from .baseMetric import BaseMetric
3from lsst.sims.maf.utils import m52snr
5__all__ = ['PhaseGapMetric', 'PeriodicQualityMetric']
7class PhaseGapMetric(BaseMetric):
8 """
9 Measure the maximum gap in phase coverage for observations of periodic variables.
11 Parameters
12 ----------
13 col: str, opt
14 Name of the column to use for the observation times (MJD)
15 nPeriods: int, opt
16 Number of periods to test
17 periodMin: float, opt
18 Minimum period to test, in days.
19 periodMax: float, opt
20 Maximum period to test, in days
21 nVisitsMin: int, opt
22 Minimum number of visits necessary before looking for the phase gap.
23 """
24 def __init__(self, col='observationStartMJD', nPeriods=5, periodMin=3., periodMax=35., nVisitsMin=3,
25 metricName='Phase Gap', **kwargs):
26 self.periodMin = periodMin
27 self.periodMax = periodMax
28 self.nPeriods = nPeriods
29 self.nVisitsMin = nVisitsMin
30 super(PhaseGapMetric, self).__init__(col, metricName=metricName, units='Fraction, 0-1', **kwargs)
32 def run(self, dataSlice, slicePoint=None):
33 if len(dataSlice) < self.nVisitsMin:
34 return self.badval
35 # Create 'nPeriods' evenly spaced periods within range of min to max.
36 step = (self.periodMax-self.periodMin)/self.nPeriods
37 if step == 0:
38 periods = np.array([self.periodMin])
39 else:
40 periods = np.arange(self.nPeriods)
41 periods = periods/np.max(periods)*(self.periodMax-self.periodMin)+self.periodMin
42 maxGap = np.zeros(self.nPeriods, float)
44 for i, period in enumerate(periods):
45 # For each period, calculate the phases.
46 phases = (dataSlice[self.colname] % period)/period
47 phases = np.sort(phases)
48 # Find the largest gap in coverage.
49 gaps = np.diff(phases)
50 start_to_end = np.array([1.0 - phases[-1] + phases[0]], float)
51 gaps = np.concatenate([gaps, start_to_end])
52 maxGap[i] = np.max(gaps)
54 return {'periods':periods, 'maxGaps':maxGap}
56 def reduceMeanGap(self, metricVal):
57 """
58 At each slicepoint, return the mean gap value.
59 """
60 return np.mean(metricVal['maxGaps'])
62 def reduceMedianGap(self, metricVal):
63 """
64 At each slicepoint, return the median gap value.
65 """
66 return np.median(metricVal['maxGaps'])
68 def reduceWorstPeriod(self, metricVal):
69 """
70 At each slicepoint, return the period with the largest phase gap.
71 """
72 worstP = metricVal['periods'][np.where(metricVal['maxGaps'] == metricVal['maxGaps'].max())]
73 return worstP
75 def reduceLargestGap(self, metricVal):
76 """
77 At each slicepoint, return the largest phase gap value.
78 """
79 return np.max(metricVal['maxGaps'])
82# To fit a periodic source well, you need to cover the full phase, and fit the amplitude.
83class PeriodicQualityMetric(BaseMetric):
84 def __init__(self, mjdCol='observationStartMJD', period=2., m5Col='fiveSigmaDepth',
85 metricName='PhaseCoverageMetric', starMag=20, **kwargs):
86 self.mjdCol = mjdCol
87 self.m5Col = m5Col
88 self.period = period
89 self.starMag = starMag
90 super(PeriodicQualityMetric, self).__init__([mjdCol, m5Col], metricName=metricName,
91 units='Fraction, 0-1', **kwargs)
93 def _calc_phase(self, dataSlice):
94 """1 is perfectly balanced phase coverage, 0 is no effective coverage.
95 """
96 angles = dataSlice[self.mjdCol] % self.period
97 angles = angles/self.period * 2.*np.pi
98 x = np.cos(angles)
99 y = np.sin(angles)
101 snr = m52snr(self.starMag, dataSlice[self.m5Col])
102 x_ave = np.average(x, weights=snr)
103 y_ave = np.average(y, weights=snr)
105 vector_off = np.sqrt(x_ave**2+y_ave**2)
106 return 1.-vector_off
108 def _calc_amp(self, dataSlice):
109 """Fractional SNR on the amplitude, testing for a variety of possible phases
110 """
111 phases = np.arange(0, np.pi, np.pi/8.)
112 snr = m52snr(self.starMag, dataSlice[self.m5Col])
113 amp_snrs = np.sin(dataSlice[self.mjdCol]/self.period*2*np.pi + phases[:, np.newaxis])*snr
114 amp_snr = np.min(np.sqrt(np.sum(amp_snrs**2, axis=1)))
116 max_snr = np.sqrt(np.sum(snr**2))
117 return amp_snr/max_snr
119 def run(self, dataSlice, slicePoint=None):
120 amplitude_fraction = self._calc_amp(dataSlice)
121 phase_fraction = self._calc_phase(dataSlice)
122 return amplitude_fraction * phase_fraction