Hide keyboard shortcuts

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 scipy.interpolate import interp1d 

3from lsst.sims.maf.metrics import BaseMetric 

4 

5# Modifying from Knut Olson's fork at: 

6# https://github.com/knutago/sims_maf_contrib/blob/master/tutorials/CrowdingMetric.ipynb 

7 

8__all__ = ['CrowdingM5Metric', 'CrowdingMagUncertMetric'] 

9 

10 

11def _compCrowdError(magVector, lumFunc, seeing, singleMag=None): 

12 """ 

13 Compute the photometric crowding error given the luminosity function and best seeing. 

14 

15 Parameters 

16 ---------- 

17 magVector : np.array 

18 Stellar magnitudes. 

19 lumFunc : np.array 

20 Stellar luminosity function. 

21 seeing : float 

22 The best seeing conditions. Assuming forced-photometry can use the best seeing conditions 

23 to help with confusion errors. 

24 singleMag : float (None) 

25 If singleMag is None, the crowding error is calculated for each mag in magVector. If 

26 singleMag is a float, the crowding error is interpolated to that single value. 

27 

28 Returns 

29 ------- 

30 np.array 

31 Magnitude uncertainties. 

32 

33 Equation from Olsen, Blum, & Rigaut 2003, AJ, 126, 452 

34 """ 

35 lumAreaArcsec = 3600.0 ** 2 

36 lumVector = 10 ** (-0.4 * magVector) 

37 coeff = np.sqrt(np.pi / lumAreaArcsec) * seeing / 2. 

38 myInt = (np.add.accumulate((lumVector ** 2 * lumFunc)[::-1]))[::-1] 

39 temp = np.sqrt(myInt) / lumVector 

40 if singleMag is not None: 

41 interp = interp1d(magVector, temp) 

42 temp = interp(singleMag) 

43 crowdError = coeff * temp 

44 return crowdError 

45 

46 

47class CrowdingM5Metric(BaseMetric): 

48 """Return the magnitude at which the photometric error exceeds crowding_error threshold. 

49 """ 

50 def __init__(self, crowding_error=0.1, filtername='r', seeingCol='seeingFwhmGeom', 

51 metricName=None, maps=['StellarDensityMap'], **kwargs): 

52 """ 

53 Parameters 

54 ---------- 

55 crowding_error : float, opt 

56 The magnitude uncertainty from crowding in magnitudes. Default 0.1 mags. 

57 filtername: str, opt 

58 The bandpass in which to calculate the crowding limit. Default r. 

59 seeingCol : str, opt 

60 The name of the seeing column. 

61 m5Col : str, opt 

62 The name of the m5 depth column. 

63 maps : list of str, opt 

64 Names of maps required for the metric. 

65 

66 Returns 

67 ------- 

68 float 

69 The magnitude of a star which has a photometric error of `crowding_error` 

70 """ 

71 

72 cols = [seeingCol] 

73 units = 'mag' 

74 self.crowding_error = crowding_error 

75 self.filtername = filtername 

76 self.seeingCol = seeingCol 

77 if 'metricName' is not None: 

78 metricName = 'Crowding to Precision %.2f' % (crowding_error) 

79 super().__init__(col=cols, maps=maps, units=units, metricName=metricName, **kwargs) 

80 

81 def run(self, dataSlice, slicePoint=None): 

82 # Set magVector to the same length as starLumFunc (lower edge of mag bins) 

83 magVector = slicePoint[f'starMapBins_{self.filtername}'][1:] 

84 # Pull up density of stars at this point in the sky 

85 lumFunc = slicePoint[f'starLumFunc_{self.filtername}'] 

86 # Calculate the crowding error using the best seeing value (in any filter?) 

87 crowdError = _compCrowdError(magVector, lumFunc, 

88 seeing=min(dataSlice[self.seeingCol]) ) 

89 # Locate at which point crowding error is greater than user-defined limit 

90 aboveCrowd = np.where(crowdError >= self.crowding_error)[0] 

91 

92 if np.size(aboveCrowd) == 0: 

93 return max(magVector) 

94 else: 

95 crowdMag = magVector[max(aboveCrowd[0]-1,0)] 

96 return crowdMag 

97 

98 

99class CrowdingMagUncertMetric(BaseMetric): 

100 """ 

101 Given a stellar magnitude, calculate the mean uncertainty on the magnitude from crowding. 

102 """ 

103 def __init__(self, rmag=20., seeingCol='seeingFwhmGeom', units='mag', 

104 metricName=None, filtername='r', maps=['StellarDensityMap'], **kwargs): 

105 """ 

106 Parameters 

107 ---------- 

108 rmag : float 

109 The magnitude of the star to consider. 

110 

111 Returns 

112 ------- 

113 float 

114 The uncertainty in magnitudes caused by crowding for a star of rmag. 

115 """ 

116 

117 self.filtername = filtername 

118 self.seeingCol = seeingCol 

119 self.rmag = rmag 

120 if 'metricName' is not None: 

121 metricName = 'CrowdingError at %.2f' % (rmag) 

122 super().__init__(col=[seeingCol], maps=maps, units=units, 

123 metricName=metricName, **kwargs) 

124 

125 def run(self, dataSlice, slicePoint=None): 

126 magVector = slicePoint[f'starMapBins_{self.filtername}'][1:] 

127 lumFunc = slicePoint[f'starLumFunc_{self.filtername}'] 

128 # Magnitude uncertainty given crowding 

129 dmagCrowd = _compCrowdError(magVector, lumFunc, 

130 dataSlice[self.seeingCol], singleMag=self.rmag) 

131 result = np.mean(dmagCrowd) 

132 return result