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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

import numpy as np 

from scipy.interpolate import interp1d 

from lsst.sims.maf.metrics import BaseMetric 

 

# Modifying from Knut Olson's fork at: 

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

 

__all__ = ['CrowdingM5Metric', 'CrowdingMagUncertMetric'] 

 

 

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

""" 

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

 

Parameters 

---------- 

magVector : np.array 

Stellar magnitudes. 

lumFunc : np.array 

Stellar luminosity function. 

seeing : float 

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

to help with confusion errors. 

singleMag : float (None) 

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

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

 

Returns 

------- 

np.array 

Magnitude uncertainties. 

 

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

""" 

lumAreaArcsec = 3600.0 ** 2 

lumVector = 10 ** (-0.4 * magVector) 

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

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

temp = np.sqrt(myInt) / lumVector 

if singleMag is not None: 

interp = interp1d(magVector, temp) 

temp = interp(singleMag) 

crowdError = coeff * temp 

return crowdError 

 

 

class CrowdingM5Metric(BaseMetric): 

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

""" 

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

metricName=None, **kwargs): 

""" 

Parameters 

---------- 

crowding_error : float, opt 

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

filtername: str, opt 

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

seeingCol : str, opt 

The name of the seeing column. 

m5Col : str, opt 

The name of the m5 depth column. 

maps : list of str, opt 

Names of maps required for the metric. 

 

Returns 

------- 

float 

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

""" 

maps = ['StellarDensityMap'] 

cols=[seeingCol] 

units = 'mag' 

self.crowding_error = crowding_error 

self.filtername = filtername 

self.seeingCol = seeingCol 

if 'metricName' is not None: 

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

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

 

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

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

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

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

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

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

crowdError = _compCrowdError(magVector, lumFunc, 

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

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

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

 

if np.size(aboveCrowd) == 0: 

return max(magVector) 

else: 

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

return crowdMag 

 

 

class CrowdingMagUncertMetric(BaseMetric): 

""" 

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

""" 

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

metricName=None, **kwargs): 

""" 

Parameters 

---------- 

rmag : float 

The magnitude of the star to consider. 

 

Returns 

------- 

float 

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

""" 

maps = ['StellarDensityMap'] 

self.rmag = rmag 

if 'metricName' is not None: 

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

super().__init__(seeingCol=seeingCol, maps=maps, units=units, 

metricName=metricName, **kwargs) 

 

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

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

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

# Magnitude uncertainty given crowding 

dmagCrowd = _compCrowdError(magVector, lumFunc, 

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

result = np.mean(dmagCrowd) 

return result