Coverage for src/susi/model/grating.py: 100%

32 statements  

« prev     ^ index     » next       coverage.py v7.5.0, created at 2025-06-13 14:15 +0000

1#!/usr/bin/env python3 

2# -*- coding: utf-8 -*- 

3""" 

4Routines to obtain incidence angle, angle of diffraction and dispersion on the camera 

5 

6@authors: iglesias, feller, hoelken 

7""" 

8from typing import Union 

9import numpy as np 

10import math 

11 

12 

13#: Grating constant [nm] 

14GRATING_D = 1667 

15#: Spread angle of diffraction (AOD) - angle of incidence (AOI) [deg] 

16GRATING_SPREAD = 8.23 

17#: Grating blaze wavelength 

18GRATING_WLB = 1820 

19#: Camera pixel size [um] 

20PX_SIZE = 12 

21#: Camera focal length M4+M5 [mm] 

22FOC_LENGTH = 2615 

23# micro meter to nano meter scale 

24UM2NM = 1e-3 

25 

26 

27def order(wl: float, wlb: int = GRATING_WLB) -> float: 

28 """ 

29 Return incidence angle for a given wavelength 

30 

31 Parameters 

32 ---------- 

33 wl: float 

34 wavelength [nm] 

35 wlb: optional 

36 blaze wavelength [nm] 

37 

38 Returns 

39 ------- 

40 diffraction order (n) 

41 """ 

42 return np.round(wlb / wl) 

43 

44 

45def incidence_angle(wl: float, d: int = GRATING_D, theta: float = GRATING_SPREAD, wlb: int = GRATING_WLB) -> float: 

46 """ 

47 Return incidence angle for a given wavelength 

48 

49 Parameters 

50 ---------- 

51 wl: float 

52 wavelength [nm] 

53 d: optional 

54 grating constant [nm] 

55 theta: optional 

56 spread between angles of incidence and diffraction [deg] 

57 wlb: optional 

58 blaze wavelength [nm] 

59 

60 Returns 

61 ------- 

62 float 

63 angle of incidence [deg] 

64 """ 

65 n = order(wl, wlb) 

66 theta = math.radians(theta) 

67 a = 2 * d**2 * (1 + np.cos(theta)) 

68 b = n * wl * d * (1 + np.cos(theta)) 

69 c = (n * wl)**2 - (d * np.sin(theta))**2 

70 s = (b - np.sqrt(b**2 - a * c)) / a 

71 return math.degrees(np.arcsin(s)) 

72 

73 

74def diffraction_angle(wl: Union[float, np.array], aoi: float) -> Union[float, np.array]: 

75 """ 

76 Return angle of diffraction 

77 

78 Parameters 

79 ---------- 

80 wl: float or numpy.array 

81 wavelength [nm] 

82 aoi: float 

83 angle of incidence [deg] 

84 

85 Returns 

86 ------- 

87 float or numpy.array 

88 angle of diffraction [deg] 

89 """ 

90 return np.degrees(np.arcsin((order(wl) * wl / GRATING_D) - math.sin(math.radians(aoi)))) 

91 

92 

93def linear_dispersion(wl, aod: float = None, f: float = FOC_LENGTH, px: int = PX_SIZE) -> float: 

94 """ 

95 Return linear dispersion on camera 

96 

97 Parameters 

98 ---------- 

99 wl: float 

100 wavelength [nm] 

101 aod: float, optional 

102 angle of diffraction [deg] 

103 f: optional 

104 spectrograph camera focal length [mm] 

105 px: optional 

106 camera pixel pitch [mu] 

107 

108 Returns 

109 ------- 

110 float 

111 dispersion [nm/px] 

112 """ 

113 if aod is None: 

114 aod = diffraction_angle(wl, incidence_angle(wl)) 

115 return GRATING_D * math.cos(math.radians(aod)) * px * UM2NM / (f * order(wl)) 

116 

117 

118def efficiency(wl: Union[float, np.array], wlb: int = GRATING_WLB, n: int = None) -> Union[float, np.array]: 

119 """ 

120 Return grating efficiency 

121 

122 The efficiencies are rough estimates, based on simple sinc approximation 

123 

124 Parameters 

125 ---------- 

126 wl: float or numpy.array 

127 wavelength [nm] 

128 wlb: float, optional 

129 blaze wavelength [nm] 

130 n: int, optional 

131 grating order 

132 default: round(wlb / wl) 

133 

134 Returns 

135 ------- 

136 float 

137 grating efficiency 

138 """ 

139 

140 if n is None: 

141 n = order(wl, wlb) 

142 return np.sinc(np.pi * (n - wlb / wl)) 

143 

144 

145def wavelength(aoi: Union[float, np.array], aod: Union[float, np.array], 

146 n: Union[int, np.array]) -> Union[float, np.array]: 

147 """ 

148 Return wavelength for given angle(s) of incidence, angle(s) of diffraction 

149 and grating order 

150 

151 Parameters 

152 ---------- 

153 aoi: float or numpy.array 

154 angle of incidence [deg] 

155 

156 aod: float or numpy.array 

157 angle of diffraction [deg] 

158 

159 n: int or numpy.array 

160 grating order 

161 

162 Returns 

163 ------- 

164 float or numpy.array 

165 wavelength [nm] 

166 """ 

167 return GRATING_D * (np.sin(np.radians(aoi)) + np.sin(np.radians(aod))) / n