Coverage for pesummary/gw/file/formats/bilby.py: 51.3%

115 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 pesummary.core.file.formats.bilby import Bilby as CoreBilby 

5from pesummary.gw.file.formats.base_read import GWSingleAnalysisRead 

6from pesummary.gw.plots.latex_labels import GWlatex_labels 

7from pesummary.utils.utils import logger 

8 

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

10 

11 

12def read_bilby( 

13 path, disable_prior=False, latex_dict=GWlatex_labels, 

14 complex_params=["matched_filter_snr", "optimal_snr"], **kwargs 

15): 

16 """Grab the parameters and samples in a bilby file 

17 

18 Parameters 

19 ---------- 

20 path: str 

21 path to the result file you wish to read in 

22 disable_prior: Bool, optional 

23 if True, do not collect prior samples from the `bilby` result file. 

24 Default False 

25 complex_params: list, optional 

26 list of parameters stored in the bilby result file which are complex 

27 and you wish to store the `amplitude` and `angle` as seperate 

28 posterior distributions 

29 latex_dict: dict, optional 

30 list of latex labels for each parameter 

31 """ 

32 from pesummary.core.file.formats.bilby import ( 

33 read_bilby as _read_bilby 

34 ) 

35 

36 return _read_bilby( 

37 path, disable_prior=disable_prior, latex_dict=latex_dict, 

38 complex_params=complex_params, _bilby_class=Bilby, **kwargs 

39 ) 

40 

41 

42def prior_samples_from_file(path, cls="BBHPriorDict", nsamples=5000, **kwargs): 

43 """Return a dict of prior samples from a `bilby` prior file 

44 

45 Parameters 

46 ---------- 

47 path: str 

48 path to a `bilby` prior file 

49 cls: str, optional 

50 class you wish to read in the prior file 

51 nsamples: int, optional 

52 number of samples to draw from a prior file. Default 5000 

53 """ 

54 from pesummary.core.file.formats.bilby import ( 

55 prior_samples_from_file as _prior_samples_from_file 

56 ) 

57 from bilby.gw import prior 

58 

59 if isinstance(cls, str): 

60 cls = getattr(prior, cls) 

61 return _prior_samples_from_file(path, cls=cls, nsamples=nsamples, **kwargs) 

62 

63 

64class Bilby(GWSingleAnalysisRead): 

65 """PESummary wrapper of `bilby` (https://git.ligo.org/lscsoft/bilby). The 

66 path_to_results_file argument will be passed directly to 

67 `bilby.core.result.read_in_result`. All functions therefore use `bilby` 

68 methods and requires `bilby` to be installed. 

69 

70 Parameters 

71 ---------- 

72 path_to_results_file: str 

73 path to the results file that you wish to read in with `bilby`. 

74 disable_prior: Bool, optional 

75 if True, do not collect prior samples from the `bilby` result file. 

76 Default False 

77 disable_prior_conversion: Bool, optional 

78 if True, disable the conversion module from deriving alternative prior 

79 distributions. Default False 

80 pe_algorithm: str 

81 name of the algorithm used to generate the posterior samples 

82 remove_nan_likelihood_samples: Bool, optional 

83 if True, remove samples which have log_likelihood='nan'. Default True 

84 

85 Attributes 

86 ---------- 

87 parameters: list 

88 list of parameters stored in the result file 

89 converted_parameters: list 

90 list of parameters that have been derived from the sampled distributions 

91 samples: 2d list 

92 list of samples stored in the result file 

93 samples_dict: dict 

94 dictionary of samples stored in the result file keyed by parameters 

95 input_version: str 

96 version of the result file passed. 

97 extra_kwargs: dict 

98 dictionary of kwargs that were extracted from the result file 

99 prior: dict 

100 dictionary of prior samples extracted from the bilby result file. The 

101 analytic priors are evaluated for 5000 samples 

102 injection_parameters: dict 

103 dictionary of injection parameters stored in the result file 

104 converted_parameters: list 

105 list of parameters that have been added 

106 

107 Methods 

108 ------- 

109 to_dat: 

110 save the posterior samples to a .dat file 

111 to_latex_table: 

112 convert the posterior samples to a latex table 

113 generate_latex_macros: 

114 generate a set of latex macros for the stored posterior samples 

115 to_lalinference: 

116 convert the posterior samples to a lalinference result file 

117 generate_all_posterior_samples: 

118 generate all posterior distributions that may be derived from 

119 sampled distributions 

120 """ 

121 def __init__(self, path_to_results_file, **kwargs): 

122 super(Bilby, self).__init__(path_to_results_file, **kwargs) 

123 self.load(self._grab_data_from_bilby_file, **kwargs) 

124 

125 @staticmethod 

126 def grab_priors(bilby_object, nsamples=5000): 

127 """Draw samples from the prior functions stored in the bilby file 

128 """ 

129 from pesummary.utils.array import Array 

130 

131 f = bilby_object 

132 try: 

133 samples = f.priors.sample(size=nsamples) 

134 priors = {key: Array(samples[key]) for key in samples} 

135 except Exception as e: 

136 logger.info("Failed to draw prior samples because {}".format(e)) 

137 priors = {} 

138 return priors 

139 

140 @staticmethod 

141 def grab_extra_kwargs(bilby_object): 

142 """Grab any additional information stored in the bilby file 

143 """ 

144 from pesummary.core.file.formats.bilby import config_from_object 

145 import ast 

146 

147 f = bilby_object 

148 kwargs = CoreBilby.grab_extra_kwargs(bilby_object) 

149 try: 

150 kwargs["meta_data"]["f_ref"] = \ 

151 f.meta_data["likelihood"]["waveform_arguments"]["reference_frequency"] 

152 except Exception: 

153 pass 

154 for key, item in f.meta_data["likelihood"].items(): 

155 if not isinstance(item, dict): 

156 try: 

157 if isinstance(item, bool): 

158 kwargs["meta_data"][key] = str(item) 

159 else: 

160 kwargs["meta_data"][key] = item 

161 except Exception: 

162 pass 

163 try: 

164 kwargs["meta_data"]["approximant"] = \ 

165 f.meta_data["likelihood"]["waveform_arguments"]["waveform_approximant"] 

166 except Exception: 

167 pass 

168 try: 

169 kwargs["meta_data"]["IFOs"] = \ 

170 " ".join(f.meta_data["likelihood"]["interferometers"].keys()) 

171 except Exception: 

172 pass 

173 _config = config_from_object(f) 

174 if len(_config): 

175 if "config" in _config.keys(): 

176 _config = _config["config"] 

177 options = [ 

178 "minimum_frequency", "reference_frequency", 

179 "waveform_approximant", "maximum_frequency" 

180 ] 

181 pesummary_names = ["f_low", "f_ref", "approximant", "f_final"] 

182 for opt, name in zip(options, pesummary_names): 

183 if opt in _config.keys(): 

184 try: 

185 _option = ast.literal_eval(_config[opt]) 

186 except ValueError: 

187 try: 

188 import re 

189 _config[opt] = re.sub( 

190 r'([A-Za-z/\.0-9\-\+][^\[\],:"}]*)', r'"\g<1>"', _config[opt] 

191 ) 

192 _option = ast.literal_eval(_config[opt]) 

193 except Exception: 

194 _option = _config[opt] 

195 if isinstance(_option, dict): 

196 _value = np.min([ 

197 ast.literal_eval(value) for key, value in _option.items() if 

198 key != "waveform" 

199 ]) 

200 else: 

201 _value = _option 

202 kwargs["meta_data"][name] = _value 

203 return kwargs 

204 

205 @property 

206 def calibration_spline_posterior(self): 

207 if not any("recalib_" in i for i in self.parameters): 

208 return super(Bilby, self).calibration_spline_posterior 

209 ifos = np.unique( 

210 [ 

211 param.split('_')[1] for param in self.parameters if 'recalib_' 

212 in param and "non_reweighted" not in param 

213 ] 

214 ) 

215 amp_params, phase_params, log_freqs = {}, {}, {} 

216 for ifo in ifos: 

217 amp_params[ifo], phase_params[ifo] = [], [] 

218 freq_params = np.sort( 

219 [ 

220 param for param in self.parameters if 

221 'recalib_%s_frequency_' % (ifo) in param 

222 and "non_reweighted" not in param 

223 ] 

224 ) 

225 posterior = self.samples_dict 

226 log_freqs[ifo] = np.log( 

227 [posterior[param][0] for param in freq_params] 

228 ) 

229 amp_parameters = np.sort( 

230 [ 

231 param for param in self.parameters if 

232 'recalib_%s_amplitude_' % (ifo) in param 

233 and "non_reweighted" not in param 

234 ] 

235 ) 

236 amplitude = np.array([posterior[param] for param in amp_parameters]) 

237 phase_parameters = np.sort( 

238 [ 

239 param for param in self.parameters if 

240 'recalib_%s_phase_' % (ifo) in param 

241 and "non_reweighted" not in param 

242 ] 

243 ) 

244 phase = np.array([posterior[param] for param in phase_parameters]) 

245 for num, i in enumerate(amplitude): 

246 amp_params[ifo].append(i) 

247 phase_params[ifo].append(phase[num]) 

248 return log_freqs, amp_params, phase_params 

249 

250 @staticmethod 

251 def load_strain_data(path_to_strain_file): 

252 """Load the strain data 

253 

254 Parameters 

255 ---------- 

256 path_to_strain_file: str 

257 path to the strain file that you wish to load 

258 """ 

259 Bilby.load_from_function( 

260 Bilby._timeseries_from_bilby_pickle, path_to_strain_file) 

261 

262 @staticmethod 

263 def _timeseries_from_bilby_pickle(path_to_strain_file): 

264 """Load a bilby pickle file containing the strain data 

265 

266 Parameters 

267 ---------- 

268 path_to_strain_file: str 

269 path to the strain file that you wish to load 

270 """ 

271 import pickle 

272 import gwpy 

273 

274 with open(path_to_strain_file, "rb") as f: 

275 data = pickle.load(f) 

276 

277 strain_data = {} 

278 for i in data.interferometers: 

279 strain_data[i.name] = gwpy.timeseries.TimeSeries( 

280 data=i.strain_data.time_domain_strain, 

281 times=i.strain_data.time_array) 

282 return strain_data 

283 

284 @staticmethod 

285 def _grab_data_from_bilby_file(path, disable_prior=False, **kwargs): 

286 """ 

287 Load the results file using the `bilby` library 

288 

289 Complex matched filter SNRs are stored in the result file. 

290 The amplitude and angle are extracted here. 

291 """ 

292 return read_bilby(path, disable_prior=disable_prior, **kwargs) 

293 

294 def add_marginalized_parameters_from_config_file(self, config_file): 

295 """Search the configuration file and add the marginalized parameters 

296 to the list of parameters and samples 

297 

298 Parameters 

299 ---------- 

300 config_file: str 

301 path to the configuration file 

302 """ 

303 pass