Coverage for pesummary/gw/cli/parser.py: 97.1%

68 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 

3from ...core.cli.parser import ArgumentParser as _ArgumentParser 

4from ...core.cli.actions import DictionaryAction, DeprecatedStoreTrueAction 

5 

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

7 

8 

9class ArgumentParser(_ArgumentParser): 

10 """Extension of the pesummary.core.cli.parser.ArgumentParser object to handle 

11 gw specific command line arguments. 

12 

13 Properties 

14 ---------- 

15 fallback_options: dict 

16 dictionary of default kwargs 

17 pesummary_options: dict 

18 dictionary giving pesummary options 

19 command_line: str 

20 command line run 

21 command_line_arguments: list 

22 list giving command line arguments 

23 dynamic_argparse: list 

24 list of dynamic argparse functions that you wish to add to the 

25 argparse.Namespace object 

26 """ 

27 @property 

28 def dynamic_argparse(self): 

29 return [ 

30 add_dynamic_PSD_to_namespace, 

31 add_dynamic_calibration_to_namespace 

32 ] 

33 

34 @property 

35 def gw_options(self): 

36 parser = ArgumentParser() 

37 parser.add_known_options_to_parser_from_key(parser, "gw") 

38 parser.add_known_options_to_parser_from_key(parser, "remnant") 

39 opts = [i.dest for i in vars(parser)["_actions"]] 

40 defaults = [i.default for i in vars(parser)["_actions"]] 

41 return opts, defaults 

42 

43 def _pesummary_options(self): 

44 core_options = super(ArgumentParser, self)._pesummary_options() 

45 options = core_options.copy() 

46 gw_options = { 

47 "--disable_remnant": { 

48 "action": "store_true", 

49 "default": False, 

50 "help": ( 

51 "Prevent remnant quantities from being calculated when the " 

52 "conversions module is used" 

53 ), 

54 "key": "remnant", 

55 }, 

56 "--force_BBH_remnant_computation": { 

57 "default": False, 

58 "action": "store_true", 

59 "help": ( 

60 "Use BBH fits to calculate remnant quantities for systems " 

61 "that include tidal deformability parameters" 

62 ), 

63 "key": "remnant", 

64 }, 

65 "--force_BH_spin_evolution": { 

66 "default": False, 

67 "action": "store_true", 

68 "help": ( 

69 "Use BH spin evolution methods to evolve spins in systems " 

70 "that include tidal deformability parameters" 

71 ), 

72 "key": "remnant", 

73 }, 

74 "--evolve_spins": { 

75 "dest": "evolve_spins_forwards", 

76 "action": DeprecatedStoreTrueAction( 

77 new_option="--evolve_spins_forwards" 

78 ), 

79 "help": ( 

80 "Evolve the spins up to the Schwarzschild ISCO frequency " 

81 "for remnant fits evaluation" 

82 ), 

83 "default": False, 

84 "key": "remnant", 

85 }, 

86 "--evolve_spins_forwards": { 

87 "action": "store_true", 

88 "help": ( 

89 "Evolve the spins up to the Schwarzschild ISCO frequency " 

90 "for remnant fits evaluation" 

91 ), 

92 "default": False, 

93 "key": "remnant", 

94 }, 

95 "--evolve_spins_backwards": { 

96 "nargs": "?", 

97 "dest": "evolve_spins_backwards", 

98 "choices": ["precession_averaged", "hybrid_orbit_averaged"], 

99 "default": False, 

100 "help": ( 

101 "Method to use when evolving spins backwards to infinite " 

102 "separation. Default 'precession_averaged'." 

103 ), 

104 "key": "remnant", 

105 }, 

106 "--NRSur_fits": { 

107 "nargs": "?", 

108 "dest": "NRSur_fits", 

109 "default": False, 

110 "help": ( 

111 "The NRSurrogate you wish to use to calculate the remnant " 

112 "quantities from your posterior samples. If not passed, " 

113 "the average NR fits are used" 

114 ), 

115 "key": "remnant", 

116 }, 

117 "--waveform_fits": { 

118 "action": "store_true", 

119 "default": False, 

120 "help": ( 

121 "Use the provided approximant (either from command line or " 

122 "stored in the result file) to calculate the remnant " 

123 "quantities from your posterior samples. If not passed, " 

124 "the average NR fits are used" 

125 ), 

126 "key": "remnant", 

127 }, 

128 "--approximant": { 

129 "dest": "approximant", 

130 "help": "waveform approximant used to generate samples", 

131 "nargs": "+", 

132 "short": "-a", 

133 "key": "gw", 

134 }, 

135 "--sensitivity": { 

136 "action": "store_true", 

137 "default": False, 

138 "help": "generate sky sensitivities for HL, HLV", 

139 "key": "gw", 

140 }, 

141 "--gracedb": { 

142 "dest": "gracedb", 

143 "help": "gracedb of the event", 

144 "key": "gw", 

145 }, 

146 "--gracedb_server": { 

147 "dest": "gracedb_server", 

148 "help": "service url to use when accessing gracedb", 

149 "key": "gw", 

150 }, 

151 "--gracedb_data": { 

152 "dest": "gracedb_data", 

153 "nargs": "+", 

154 "default": ["t_0", "far", "created"], 

155 "help": ( 

156 "data you wish to download from gracedb and store in the " 

157 "metafile" 

158 ), 

159 "key": "gw", 

160 }, 

161 "--psd": { 

162 "dest": "psd", 

163 "action": DictionaryAction, 

164 "help": "psd files used", 

165 "nargs": "+", 

166 "default": {}, 

167 "key": "gw", 

168 }, 

169 "--{}_psd": { 

170 "dest": "example_psd", 

171 "metavar": "IFO:PATH_to_PSD.dat", 

172 "help": ( 

173 "psd files used for a specific label. '{}' should be " 

174 "replaced with the label of interest. For example " 

175 "--IMRPhenomPv3_psd H1:IF0_psd.dat" 

176 ), 

177 "key": "gw", 

178 }, 

179 "--calibration": { 

180 "dest": "calibration", 

181 "help": "files for the calibration envelope", 

182 "nargs": "+", 

183 "default": {}, 

184 "action": DictionaryAction, 

185 "key": "gw", 

186 }, 

187 "--{}_calibration": { 

188 "dest": "example_calibration", 

189 "metavar": "IFO:PATH_to_CAL.txt", 

190 "help": ( 

191 "calibration files used for a specific label. '{}' should " 

192 "be replaced with the label of interest. For example " 

193 "--IMRPhenomPv3_calibration H1:IF0_cal.dat" 

194 ), 

195 "key": "gw", 

196 }, 

197 "--trigfile": { 

198 "dest": "inj_file", 

199 "help": "xml file containing the trigger values", 

200 "nargs": "+", 

201 "key": "gw", 

202 }, 

203 "--gwdata": { 

204 "dest": "gwdata", 

205 "help": "channels and paths to strain cache files", 

206 "action": DictionaryAction, 

207 "metavar": "CHANNEL:CACHEFILE or PICKLEFILE", 

208 "nargs": "+", 

209 "key": "gw", 

210 }, 

211 "--multi_threading_for_skymap": { 

212 "action": "store_true", 

213 "default": False, 

214 "help": ( 

215 "use multi-threading to speed up generation of ligo.skymap" 

216 ), 

217 "key": "gw" 

218 }, 

219 "--nsamples_for_skymap": { 

220 "dest": "nsamples_for_skymap", 

221 "help": ( 

222 "The number of samples used to generate the ligo.skymap. " 

223 "These samples will be randomly drawn from the posterior " 

224 "distributions" 

225 ), 

226 "key": "gw", 

227 }, 

228 "--calculate_multipole_snr": { 

229 "action": "store_true", 

230 "default": False, 

231 "help": ( 

232 "Calculate the SNR in the (ell, m) = [(2, 1), (3, 3), " 

233 "(4, 4)] subdominant multipoles based on the posterior " 

234 "samples" 

235 ), 

236 "key": "gw", 

237 }, 

238 "--calculate_precessing_snr": { 

239 "action": "store_true", 

240 "default": False, 

241 "help": ( 

242 "Calculate the precessing SNR based on the posterior " 

243 "samples" 

244 ), 

245 "key": "gw", 

246 }, 

247 "--psd_default": { 

248 "dest": "psd_default", 

249 "default": "aLIGOZeroDetHighPower", 

250 "help": ( 

251 "The PSD to use for conversions when no psd file is " 

252 "provided. Default aLIGOZeroDetHighPower" 

253 ), 

254 "key": "gw", 

255 }, 

256 "--f_low": { 

257 "dest": "f_low", 

258 "nargs": "+", 

259 "help": "Low frequency cutoff used to generate the samples", 

260 "key": "gw", 

261 }, 

262 "--f_ref": { 

263 "dest": "f_ref", 

264 "help": "Reference frequency used to generate the samples", 

265 "nargs": "+", 

266 "key": "gw", 

267 }, 

268 "--f_final": { 

269 "dest": "f_final", 

270 "nargs": "+", 

271 "type": float, 

272 "help": ( 

273 "Final frequency to use when calculating the precessing snr" 

274 ), 

275 "key": "gw" 

276 }, 

277 "--delta_f": { 

278 "dest": "delta_f", 

279 "help": ( 

280 "Difference in frequency samples when calculating the " 

281 "precessing snr" 

282 ), 

283 "nargs": "+", 

284 "type": float, 

285 "key": "gw", 

286 }, 

287 "--no_ligo_skymap": { 

288 "action": "store_true", 

289 "default": False, 

290 "help": "do not generate a skymap with ligo.skymap", 

291 "key": "gw", 

292 }, 

293 "--gw": { 

294 "action": "store_true", 

295 "help": "run with the gravitational wave pipeline", 

296 "default": False, 

297 "key": "gw", 

298 }, 

299 "--public": { 

300 "action": "store_true", 

301 "help": "generate public facing summary pages", 

302 "default": False, 

303 "key": "gw" 

304 }, 

305 "--redshift_method": { 

306 "dest": "redshift_method", 

307 "help": "The method to use when estimating the redshift", 

308 "choices": ["approx", "exact"], 

309 "default": "approx", 

310 "key": "gw", 

311 }, 

312 "--cosmology": { 

313 "dest": "cosmology", 

314 "help": "The cosmology to use when calculating the redshift", 

315 "default": "Planck15", 

316 "key": "gw" 

317 }, 

318 "--no_conversion": { 

319 "action": "store_true", 

320 "default": False, 

321 "help": "Do not generate any conversions", 

322 "key": "gw" 

323 } 

324 } 

325 options.update(gw_options) 

326 return options 

327 

328 def add_gw_group(self): 

329 gw_group = self.add_argument_group( 

330 "\n\n=====================================================\n" 

331 "Options specific for gravitational wave results files\n" 

332 "=====================================================" 

333 ) 

334 return self.add_known_options_to_parser_from_key(gw_group, "gw") 

335 

336 def add_remnant_group(self): 

337 remnant_group = self.add_argument_group( 

338 "Options specific for calculating the remnant properties\n" 

339 "-------------------------------------------------------" 

340 ) 

341 return self.add_known_options_to_parser_from_key( 

342 remnant_group, "remnant" 

343 ) 

344 

345 def add_all_groups_to_parser(self): 

346 super(ArgumentParser, self).add_all_groups_to_parser() 

347 self.add_gw_group() 

348 self.add_remnant_group() 

349 

350 

351class TGRArgumentParser(ArgumentParser): 

352 """Extension of the pesummary.gw.cli.parser.ArgumentParser object to handle 

353 TGR specific command line arguments. 

354 

355 Properties 

356 ---------- 

357 fallback_options: dict 

358 dictionary of default kwargs 

359 pesummary_options: dict 

360 dictionary giving pesummary options 

361 command_line: str 

362 command line run 

363 command_line_arguments: list 

364 list giving command line arguments 

365 dynamic_argparse: list 

366 list of dynamic argparse functions that you wish to add to the 

367 argparse.Namespace object 

368 """ 

369 @property 

370 def dynamic_argparse(self): 

371 return [add_dynamic_tgr_kwargs_to_namespace] 

372 

373 def _pesummary_options(self): 

374 TESTS = ["imrct"] 

375 _options = super(TGRArgumentParser, self)._pesummary_options() 

376 options = { 

377 "--test": { 

378 "short": "-t", 

379 "help": ( 

380 "What test do you want to run? Currently only supports " 

381 "{}".format(", ".join(TESTS)) 

382 ), 

383 "required": True, 

384 "choices": TESTS, 

385 }, 

386 "--{test}_kwargs": { 

387 "dest": "example_test_kwargs", 

388 "help": ( 

389 "Kwargs you wish to use when postprocessing the results. " 

390 "Kwargs should be provided as a dictionary 'kwarg:value'. " 

391 "For example `--imrct_kwargs N_bins:201 multi_process:4` " 

392 "would pass the kwargs N_bins=201, multi_process=4 to the " 

393 "IMRCT function. The test name '{test}' should match the " 

394 "test provided with the --test flag" 

395 ), 

396 }, 

397 "--labels": { 

398 "help": ( 

399 "Labels used to distinguish runs. The label format is " 

400 "dependent on the TGR test you wish to use. For the IMRCT " 

401 "test, labels need to be inspiral and postinspiral if " 

402 "analysing a single event or {label1}:inspiral," 

403 "{label1}:postinspiral,{label2}:inspiral," 

404 "{label2}:postinspiral,... if analysing two or more events " 

405 "(where label1/label2 is a unique string to distinguish " 

406 "files from a single event). If a metafile is provided, " 

407 "labels need to be {inspiral_label}:inspiral " 

408 "{postinspiral_label}:postinspiral where inspiral_label " 

409 "and postinspiral_label are the pesummary labels for the " 

410 "inspiral and postinspiral analyses respectively." 

411 ), 

412 "nargs": "+", 

413 }, 

414 "--cutoff_frequency": { 

415 "type": float, 

416 "nargs": "+", 

417 "help": ( 

418 "Cutoff Frequency for IMRCT. Overrides any cutoff " 

419 "frequency present in the supplied files. The supplied " 

420 "cutoff frequency will only be used as metadata and " 

421 "does not affect the cutoff frequency used in the " 

422 "analysis. If only one number is supplied, the inspiral " 

423 "maximum frequency and the postinspiral maximum frequency " 

424 "are set to the same number. If a list of length 2 is " 

425 "supplied, this assumes that the one corresponding to the " 

426 "inspiral label is the maximum frequency for the inspiral " 

427 "and that corresponding to the postinspiral label is " 

428 "the minimum frequency for the postinspiral" 

429 ) 

430 }, 

431 "--links_to_pe_pages": { 

432 "help": "URLs for PE results pages separated by spaces.", 

433 "nargs": "+", 

434 "default": [], 

435 }, 

436 "--disable_pe_page_generation": { 

437 "action": "store_true", 

438 "help": ( 

439 "Disable PE page generation for the input samples. This " 

440 "option is only relevant if no URLs for PE results pages " 

441 "are provided using --links_to_pe_pages." 

442 ) 

443 }, 

444 "--pe_page_options": { 

445 "type": str, 

446 "default": "", 

447 "help": ( 

448 "Additional options to pass to 'summarypages' when " 

449 "generating PE webpages. All options should be wrapped in " 

450 "quotation marks like, --pe_page_options='--no_ligo_skymap " 

451 "--nsamples 1000 --psd...'. See 'summarypages --help' for " 

452 "details. These options are added to base executable: " 

453 "'summarypages --webdir {} --samples {} --labels {} --gw'" 

454 ), 

455 }, 

456 "--make_diagnostic_plots": { 

457 "action": "store_true", 

458 "help": "Make extra diagnostic plots" 

459 } 

460 } 

461 extra_options = [ 

462 "--webdir", "--approximant", "--evolve_spins_forwards", "--f_low", 

463 "--samples" 

464 ] 

465 for key in extra_options: 

466 options[key] = _options[key] 

467 return options 

468 

469 

470def add_dynamic_argparse( 

471 existing_namespace, pattern, example="--{}_psd", default={}, 

472 nargs='+', action=DictionaryAction, command_line=None 

473): 

474 """Add a dynamic argparse argument and add it to an existing 

475 argparse.Namespace object 

476 

477 Parameters 

478 ---------- 

479 existing_namespace: argparse.Namespace 

480 existing namespace you wish to add the dynamic arguments too 

481 pattern: str 

482 generic pattern for customg argparse. For example '--*_psd' 

483 example: str, optional 

484 example string to demonstrate usage 

485 default: obj, optional 

486 the default argument for the dynamic argparse object 

487 nargs: str 

488 action: argparse.Action 

489 argparse action to use for the dynamic argparse 

490 command_line: str, optional 

491 command line you wish to pass. If None, command line taken from 

492 sys.argv 

493 """ 

494 import fnmatch 

495 import collections 

496 import argparse 

497 if command_line is None: 

498 from pesummary.utils.utils import command_line_arguments 

499 command_line = command_line_arguments() 

500 commands = fnmatch.filter(command_line, pattern) 

501 duplicates = [ 

502 item for item, count in collections.Counter(commands).items() if 

503 count > 1 

504 ] 

505 if example in commands: 

506 commands.remove(example) 

507 if len(duplicates) > 0: 

508 raise Exception( 

509 "'{}' has been repeated. Please give a unique argument".format( 

510 duplicates[0] 

511 ) 

512 ) 

513 parser = argparse.ArgumentParser() 

514 for i in commands: 

515 parser.add_argument( 

516 i, help=argparse.SUPPRESS, action=action, nargs=nargs, 

517 default=default 

518 ) 

519 args, unknown = parser.parse_known_args(args=command_line) 

520 existing_namespace.__dict__.update(vars(args)) 

521 return args, unknown 

522 

523 

524def add_dynamic_PSD_to_namespace(existing_namespace, command_line=None): 

525 """Add a dynamic PSD argument to the argparse namespace 

526 

527 Parameters 

528 ---------- 

529 existing_namespace: argparse.Namespace 

530 existing namespace you wish to add the dynamic arguments too 

531 command_line: str, optional 

532 The command line which you are passing. Default None 

533 """ 

534 return add_dynamic_argparse( 

535 existing_namespace, "--*_psd", command_line=command_line 

536 ) 

537 

538 

539def add_dynamic_calibration_to_namespace(existing_namespace, command_line=None): 

540 """Add a dynamic calibration argument to the argparse namespace 

541 

542 Parameters 

543 ---------- 

544 existing_namespace: argparse.Namespace 

545 existing namespace you wish to add the dynamic arguments too 

546 command_line: str, optional 

547 The command line which you are passing. Default None 

548 """ 

549 return add_dynamic_argparse( 

550 existing_namespace, "--*_calibration", example="--{}_calibration", 

551 command_line=command_line 

552 ) 

553 

554 

555def add_dynamic_tgr_kwargs_to_namespace(existing_namespace, command_line=None): 

556 """Add a dynamic TGR kwargs argument to the argparse namespace 

557 

558 Parameters 

559 ---------- 

560 existing_namespace: argparse.Namespace 

561 existing namespace you wish to add the dynamic arguments to 

562 command_line: str, optional 

563 The command line which you are passing. Default None 

564 """ 

565 return add_dynamic_argparse( 

566 existing_namespace, "--*_kwargs", example="--{}_kwargs", 

567 command_line=command_line 

568 )