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

68 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2025-11-05 13:38 +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 "--approximant_flags": { 

136 "dest": "approximant_flags", 

137 "help": ( 

138 "flags used to control variant of waveform approximant used. Must " 

139 "be in the form LABEL:FLAG:VALUE where LABEL is the analysis label " 

140 "that you wish to assign FLAG:VALUE to" 

141 ), 

142 "nargs": "+", 

143 "action": DictionaryAction, 

144 "default": {}, 

145 "key": "gw", 

146 }, 

147 "--sensitivity": { 

148 "action": "store_true", 

149 "default": False, 

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

151 "key": "gw", 

152 }, 

153 "--terrestrial_probability": { 

154 "dest": "terrestrial_probability", 

155 "default": None, 

156 "nargs": "+", 

157 "help": ( 

158 "Terrestrial probability for the candidate you are " 

159 "analysing. This is used when computing PAstro" 

160 ), 

161 "key": "gw", 

162 }, 

163 "--catch_terrestrial_probability_error": { 

164 "dest": "catch_terrestrial_probability_error", 

165 "default": False, 

166 "action": "store_true", 

167 "help": ( 

168 "Catch the ValueError raised when no terrestrial probability " 

169 "is provided when computing PAstro" 

170 ), 

171 "key": "gw", 

172 }, 

173 "--pastro_category_file": { 

174 "dest": "pastro_category_file", 

175 "default": None, 

176 "help": ( 

177 "path to yml file containing summary data for each " 

178 "category (BBH, BNS, NSBH). This includes e.g. rates, " 

179 "mass bounds etc. This is used when computing PAstro" 

180 ), 

181 "key": "gw", 

182 }, 

183 "--gracedb": { 

184 "dest": "gracedb", 

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

186 "key": "gw", 

187 }, 

188 "--gracedb_server": { 

189 "dest": "gracedb_server", 

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

191 "key": "gw", 

192 }, 

193 "--gracedb_data": { 

194 "dest": "gracedb_data", 

195 "nargs": "+", 

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

197 "help": ( 

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

199 "metafile" 

200 ), 

201 "key": "gw", 

202 }, 

203 "--psd": { 

204 "dest": "psd", 

205 "action": DictionaryAction, 

206 "help": "psd files used", 

207 "nargs": "+", 

208 "default": {}, 

209 "key": "gw", 

210 }, 

211 "--{}_psd": { 

212 "dest": "example_psd", 

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

214 "help": ( 

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

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

217 "--IMRPhenomPv3_psd H1:IF0_psd.dat" 

218 ), 

219 "key": "gw", 

220 }, 

221 "--calibration_definition": { 

222 "dest": "calibration_definition", 

223 "help": "Definition for each calibration envelope", 

224 "nargs": "+", 

225 "default": ["data"], 

226 "key": "gw", 

227 }, 

228 "--calibration": { 

229 "dest": "calibration", 

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

231 "nargs": "+", 

232 "default": {}, 

233 "action": DictionaryAction, 

234 "key": "gw", 

235 }, 

236 "--{}_calibration": { 

237 "dest": "example_calibration", 

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

239 "help": ( 

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

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

242 "--IMRPhenomPv3_calibration H1:IF0_cal.dat" 

243 ), 

244 "key": "gw", 

245 }, 

246 "--trigfile": { 

247 "dest": "inj_file", 

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

249 "nargs": "+", 

250 "key": "gw", 

251 }, 

252 "--gwdata": { 

253 "dest": "gwdata", 

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

255 "action": DictionaryAction, 

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

257 "nargs": "+", 

258 "key": "gw", 

259 }, 

260 "--multi_threading_for_skymap": { 

261 "action": "store_true", 

262 "default": False, 

263 "help": ( 

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

265 ), 

266 "key": "gw" 

267 }, 

268 "--nsamples_for_skymap": { 

269 "dest": "nsamples_for_skymap", 

270 "help": ( 

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

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

273 "distributions" 

274 ), 

275 "key": "gw", 

276 }, 

277 "--calculate_multipole_snr": { 

278 "action": "store_true", 

279 "default": False, 

280 "help": ( 

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

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

283 "samples" 

284 ), 

285 "key": "gw", 

286 }, 

287 "--calculate_precessing_snr": { 

288 "action": "store_true", 

289 "default": False, 

290 "help": ( 

291 "Calculate the precessing SNR based on the posterior " 

292 "samples" 

293 ), 

294 "key": "gw", 

295 }, 

296 "--psd_default": { 

297 "dest": "psd_default", 

298 "default": "aLIGOZeroDetHighPower", 

299 "help": ( 

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

301 "provided. Default aLIGOZeroDetHighPower" 

302 ), 

303 "key": "gw", 

304 }, 

305 "--f_start": { 

306 "dest": "f_start", 

307 "nargs": "+", 

308 "help": "Starting frequency of the supplied waveform", 

309 "key": "gw", 

310 }, 

311 "--f_low": { 

312 "dest": "f_low", 

313 "nargs": "+", 

314 "help": ( 

315 "Low frequency cutoff for likelihood integration used to generate " 

316 "the samples" 

317 ), 

318 "key": "gw", 

319 }, 

320 "--f_ref": { 

321 "dest": "f_ref", 

322 "help": ( 

323 "Reference frequency of the waveform used to generate the samples" 

324 ), 

325 "nargs": "+", 

326 "key": "gw", 

327 }, 

328 "--f_final": { 

329 "dest": "f_final", 

330 "nargs": "+", 

331 "type": float, 

332 "help": ( 

333 "Final frequency of the waveform. Used when calculating the precessing snr" 

334 ), 

335 "key": "gw" 

336 }, 

337 "--delta_f": { 

338 "dest": "delta_f", 

339 "help": ( 

340 "Difference in frequency samples when calculating the " 

341 "precessing snr" 

342 ), 

343 "nargs": "+", 

344 "type": float, 

345 "key": "gw", 

346 }, 

347 "--no_ligo_skymap": { 

348 "action": "store_true", 

349 "default": False, 

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

351 "key": "gw", 

352 }, 

353 "--gw": { 

354 "action": "store_true", 

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

356 "default": False, 

357 "key": "gw", 

358 }, 

359 "--public": { 

360 "action": "store_true", 

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

362 "default": False, 

363 "key": "gw" 

364 }, 

365 "--redshift_method": { 

366 "dest": "redshift_method", 

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

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

369 "default": "approx", 

370 "key": "gw", 

371 }, 

372 "--cosmology": { 

373 "dest": "cosmology", 

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

375 "default": "Planck15", 

376 "key": "gw" 

377 }, 

378 "--no_conversion": { 

379 "action": "store_true", 

380 "default": False, 

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

382 "key": "gw" 

383 } 

384 } 

385 options.update(gw_options) 

386 return options 

387 

388 def add_gw_group(self): 

389 gw_group = self.add_argument_group( 

390 "\n\n=====================================================\n" 

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

392 "=====================================================" 

393 ) 

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

395 

396 def add_remnant_group(self): 

397 remnant_group = self.add_argument_group( 

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

399 "-------------------------------------------------------" 

400 ) 

401 return self.add_known_options_to_parser_from_key( 

402 remnant_group, "remnant" 

403 ) 

404 

405 def add_all_groups_to_parser(self): 

406 super(ArgumentParser, self).add_all_groups_to_parser() 

407 self.add_gw_group() 

408 self.add_remnant_group() 

409 

410 

411class TGRArgumentParser(ArgumentParser): 

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

413 TGR specific command line arguments. 

414 

415 Properties 

416 ---------- 

417 fallback_options: dict 

418 dictionary of default kwargs 

419 pesummary_options: dict 

420 dictionary giving pesummary options 

421 command_line: str 

422 command line run 

423 command_line_arguments: list 

424 list giving command line arguments 

425 dynamic_argparse: list 

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

427 argparse.Namespace object 

428 """ 

429 @property 

430 def dynamic_argparse(self): 

431 return [add_dynamic_tgr_kwargs_to_namespace] 

432 

433 def _pesummary_options(self): 

434 TESTS = ["imrct"] 

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

436 options = { 

437 "--test": { 

438 "short": "-t", 

439 "help": ( 

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

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

442 ), 

443 "required": True, 

444 "choices": TESTS, 

445 }, 

446 "--{test}_kwargs": { 

447 "dest": "example_test_kwargs", 

448 "help": ( 

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

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

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

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

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

454 "test provided with the --test flag" 

455 ), 

456 }, 

457 "--labels": { 

458 "help": ( 

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

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

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

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

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

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

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

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

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

468 "{postinspiral_label}:postinspiral where inspiral_label " 

469 "and postinspiral_label are the pesummary labels for the " 

470 "inspiral and postinspiral analyses respectively." 

471 ), 

472 "nargs": "+", 

473 }, 

474 "--cutoff_frequency": { 

475 "type": float, 

476 "nargs": "+", 

477 "help": ( 

478 "Cutoff Frequency for IMRCT. Overrides any cutoff " 

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

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

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

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

483 "maximum frequency and the postinspiral maximum frequency " 

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

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

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

487 "and that corresponding to the postinspiral label is " 

488 "the minimum frequency for the postinspiral" 

489 ) 

490 }, 

491 "--links_to_pe_pages": { 

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

493 "nargs": "+", 

494 "default": [], 

495 }, 

496 "--disable_pe_page_generation": { 

497 "action": "store_true", 

498 "help": ( 

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

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

501 "are provided using --links_to_pe_pages." 

502 ) 

503 }, 

504 "--pe_page_options": { 

505 "type": str, 

506 "default": "", 

507 "help": ( 

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

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

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

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

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

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

514 ), 

515 }, 

516 "--make_diagnostic_plots": { 

517 "action": "store_true", 

518 "help": "Make extra diagnostic plots" 

519 } 

520 } 

521 extra_options = [ 

522 "--webdir", "--approximant", "--evolve_spins_forwards", "--f_low", 

523 "--samples" 

524 ] 

525 for key in extra_options: 

526 options[key] = _options[key] 

527 return options 

528 

529 

530def add_dynamic_argparse( 

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

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

533): 

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

535 argparse.Namespace object 

536 

537 Parameters 

538 ---------- 

539 existing_namespace: argparse.Namespace 

540 existing namespace you wish to add the dynamic arguments too 

541 pattern: str 

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

543 example: str, optional 

544 example string to demonstrate usage 

545 default: obj, optional 

546 the default argument for the dynamic argparse object 

547 nargs: str 

548 action: argparse.Action 

549 argparse action to use for the dynamic argparse 

550 command_line: str, optional 

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

552 sys.argv 

553 """ 

554 import fnmatch 

555 import collections 

556 import argparse 

557 if command_line is None: 

558 from pesummary.utils.utils import command_line_arguments 

559 command_line = command_line_arguments() 

560 commands = fnmatch.filter(command_line, pattern) 

561 duplicates = [ 

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

563 count > 1 

564 ] 

565 if example in commands: 

566 commands.remove(example) 

567 if len(duplicates) > 0: 

568 raise Exception( 

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

570 duplicates[0] 

571 ) 

572 ) 

573 parser = argparse.ArgumentParser() 

574 for i in commands: 

575 parser.add_argument( 

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

577 default=default 

578 ) 

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

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

581 return args, unknown 

582 

583 

584def add_dynamic_PSD_to_namespace(existing_namespace, command_line=None): 

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

586 

587 Parameters 

588 ---------- 

589 existing_namespace: argparse.Namespace 

590 existing namespace you wish to add the dynamic arguments too 

591 command_line: str, optional 

592 The command line which you are passing. Default None 

593 """ 

594 return add_dynamic_argparse( 

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

596 ) 

597 

598 

599def add_dynamic_calibration_to_namespace(existing_namespace, command_line=None): 

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

601 

602 Parameters 

603 ---------- 

604 existing_namespace: argparse.Namespace 

605 existing namespace you wish to add the dynamic arguments too 

606 command_line: str, optional 

607 The command line which you are passing. Default None 

608 """ 

609 return add_dynamic_argparse( 

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

611 command_line=command_line 

612 ) 

613 

614 

615def add_dynamic_tgr_kwargs_to_namespace(existing_namespace, command_line=None): 

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

617 

618 Parameters 

619 ---------- 

620 existing_namespace: argparse.Namespace 

621 existing namespace you wish to add the dynamic arguments to 

622 command_line: str, optional 

623 The command line which you are passing. Default None 

624 """ 

625 return add_dynamic_argparse( 

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

627 command_line=command_line 

628 )