Coverage for pesummary/gw/file/calibration.py: 57.6%

59 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-05-02 08:42 +0000

1# Licensed under an MIT style license -- see LICENSE.md 

2 

3import numpy as np 

4from scipy.interpolate import interp1d 

5from pesummary import conf 

6from pesummary.utils.utils import check_file_exists_and_rename 

7from pesummary.utils.dict import Dict 

8 

9__author__ = ["Charlie Hoy <charlie.hoy@ligo.org>"] 

10 

11 

12def _spline_angle_xform(delta_psi): 

13 """Returns the angle in degrees corresponding to the spline 

14 calibration parameters delta_psi. Code taken from lalinference.bayespputils 

15 

16 Parameters 

17 ---------- 

18 delta_psi: array 

19 calibration phase uncertainty 

20 """ 

21 rotation = (2.0 + 1.0j * delta_psi) / (2.0 - 1.0j * delta_psi) 

22 return 180.0 / np.pi * np.arctan2(np.imag(rotation), np.real(rotation)) 

23 

24 

25def _interpolate_spline_model( 

26 frequencies, data, interpolated_frequencies, nfreqs=100, xform=None, 

27 level=0.9, pbar=None 

28): 

29 """Interpolate calibration posterior estimates for a spline model in log 

30 space. Code based upon same function in lalinference.bayespputils 

31 

32 Parameters 

33 ---------- 

34 frequencies: array 

35 The spline control points 

36 data: ndarray 

37 Array of posterior samples at each spline control point 

38 interpolated_frequencies: array 

39 Array of frequencies you wish to evaluate the interpolant for 

40 nfreqs: int, optional 

41 Number of points to evaluate the interpolates spline. Default 100 

42 xform: func, optional 

43 Function to transform the spline 

44 """ 

45 interpolated_data = np.zeros((np.asarray(data).shape[0], nfreqs)) 

46 for num, samp in enumerate(data): 

47 interp = interp1d( 

48 frequencies, samp, kind="cubic", fill_value=0., bounds_error=False 

49 )(interpolated_frequencies) 

50 if xform is not None: 

51 interp = xform(interp) 

52 interpolated_data[num] = interp 

53 if pbar is not None: 

54 pbar.update(1) 

55 

56 mean = np.mean(interpolated_data, axis=0) 

57 lower = np.quantile(interpolated_data, (1 - level) / 2., axis=0) 

58 upper = np.quantile(interpolated_data, (1 + level) / 2., axis=0) 

59 return mean, lower, upper 

60 

61 

62def interpolate_calibration_posterior_from_samples( 

63 log_frequencies, amplitudes, phases, nfreqs=100, level=0.9, **kwargs 

64): 

65 """Interpolate calibration posterior estimates for a spline model in log 

66 space and return the amplitude and phase uncertainties. Code based upon same 

67 function in lalinference.bayespputils 

68 

69 Parameters 

70 ---------- 

71 log_frequencies: array 

72 The spline control points. 

73 amplitudes: ndarray 

74 Array of amplitude posterior samples at each of the spline control 

75 points 

76 phases: ndarray 

77 Array of phase posterior samples at each of the spline control points 

78 nfreqs: int, optional 

79 Number of points to evaluate the interpolates spline. Default 100 

80 **kwargs: dict 

81 All kwargs passed to _interpolate_spline_model 

82 """ 

83 frequencies = np.exp(log_frequencies) 

84 interpolated_frequencies = np.logspace( 

85 np.min(log_frequencies), np.max(log_frequencies), nfreqs, base=np.e 

86 ) 

87 amp_mean, amp_lower, amp_upper = ( 

88 1 + np.array( 

89 _interpolate_spline_model( 

90 log_frequencies, np.column_stack(amplitudes), 

91 np.log(interpolated_frequencies), nfreqs=nfreqs, xform=None, 

92 level=level, **kwargs 

93 ) 

94 ) 

95 ) 

96 phase_mean, phase_lower, phase_upper = np.array( 

97 _interpolate_spline_model( 

98 log_frequencies, np.column_stack(phases), 

99 np.log(interpolated_frequencies), nfreqs=nfreqs, 

100 xform=_spline_angle_xform, level=level, **kwargs 

101 ) 

102 ) * (np.pi / 180) 

103 return np.column_stack( 

104 [ 

105 interpolated_frequencies, amp_mean, phase_mean, amp_lower, 

106 phase_lower, amp_upper, phase_upper 

107 ] 

108 ) 

109 

110 

111class CalibrationDict(Dict): 

112 """Class to handle a dictionary of calibration data 

113 

114 Parameters 

115 ---------- 

116 detectors: list 

117 list of detectors 

118 data: nd list 

119 list of calibration samples for each detector. Each of the columns 

120 should represent Frequency, Median Mag, Phase (Rad), -1 Sigma Mag, 

121 -1 Sigma Phase, +1 Sigma Mag, +1 Sigma Phase 

122 

123 Attributes 

124 ---------- 

125 detectors: list 

126 list of detectors stored in the dictionary 

127 

128 Methods 

129 ------- 

130 plot: 

131 Generate a plot based on the calibration samples stored 

132 """ 

133 def __init__(self, *args): 

134 _columns = [ 

135 "frequencies", "magnitude", "phase", "magnitude_lower", 

136 "phase_lower", "magnitude_upper", "phase_upper" 

137 ] 

138 super(CalibrationDict, self).__init__( 

139 *args, value_class=Calibration, value_columns=_columns 

140 ) 

141 

142 @property 

143 def detectors(self): 

144 return list(self.keys()) 

145 

146 

147class Calibration(np.ndarray): 

148 """Class to handle Calibration data 

149 """ 

150 def __new__(cls, input_array): 

151 obj = np.asarray(input_array).view(cls) 

152 if obj.shape[1] != 7: 

153 raise ValueError( 

154 "Invalid input data. See the docs for instructions" 

155 ) 

156 return obj 

157 

158 @classmethod 

159 def read(cls, path_to_file, IFO=None, **kwargs): 

160 """Read in a file and initialize the Calibration class 

161 

162 Parameters 

163 ---------- 

164 path_to_file: str 

165 the path to the file you wish to load 

166 IFO: str, optional 

167 name of the IFO which relates to the input file 

168 **kwargs: dict 

169 all kwargs are passed to the np.genfromtxt method 

170 """ 

171 try: 

172 f = np.genfromtxt(path_to_file, **kwargs) 

173 return cls(f) 

174 except Exception: 

175 raise 

176 

177 @classmethod 

178 def from_spline_posterior_samples( 

179 cls, log_frequencies, amplitudes, phases, **kwargs 

180 ): 

181 """Interpolate calibration posterior estimates for a spline model in log 

182 space and initialize the Calibration class 

183 

184 Parameters 

185 ---------- 

186 """ 

187 samples = interpolate_calibration_posterior_from_samples( 

188 log_frequencies, amplitudes, phases, level=0.68, nfreqs=300, 

189 **kwargs 

190 ) 

191 return cls(samples) 

192 

193 def save_to_file(self, file_name, comments="#", delimiter=conf.delimiter): 

194 """Save the calibration data to file 

195 

196 Parameters 

197 ---------- 

198 file_name: str 

199 name of the file name that you wish to use 

200 comments: str, optional 

201 String that will be prepended to the header and footer strings, to 

202 mark them as comments. Default is '#'. 

203 delimiter: str, optional 

204 String or character separating columns. 

205 """ 

206 check_file_exists_and_rename(file_name) 

207 header = [ 

208 "Frequency", "Median Mag", "Phase (Rad)", "-1 Sigma Mag", 

209 "-1 Sigma Phase", "+1 Sigma Mag", "+1 Sigma Phase" 

210 ] 

211 np.savetxt( 

212 file_name, self, delimiter=delimiter, comments=comments, 

213 header=delimiter.join(header) 

214 ) 

215 

216 def __array_finalize__(self, obj): 

217 if obj is None: 

218 return