Coverage for pesummary/cli/summarypipe.py: 84.5%

426 statements  

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

1#! /usr/bin/env python 

2 

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

4 

5from pesummary.core.cli.parser import ArgumentParser as _ArgumentParser 

6from pesummary.utils.utils import logger, list_match 

7from pesummary.utils.decorators import open_config 

8import shutil 

9import os 

10import numpy as np 

11import sys 

12import re 

13import ast 

14import glob 

15from pathlib import Path 

16 

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

18__doc__ = """This executable is used to generate a summarypages executable 

19given a rundir""" 

20 

21 

22class ArgumentParser(_ArgumentParser): 

23 def _pesummary_options(self): 

24 options = super(ArgumentParser, self)._pesummary_options() 

25 options.update( 

26 { 

27 "--rundir": { 

28 "--short": "-r", 

29 "help": "run directory of the parameter estimation job", 

30 "required": True, 

31 }, 

32 "--config": { 

33 "help": ( 

34 "config file to extract information from. This can " 

35 "either be an absolute path to a config file, or a " 

36 "pattern to search for in run directory. If not " 

37 "provided, search run directory for a config file to " 

38 "use." 

39 ), 

40 }, 

41 "--samples": { 

42 "help": "path to posterior samples file you wish to use" 

43 }, 

44 "--pattern": { 

45 "help": "pattern to use when searching for files", 

46 }, 

47 "--return_string": { 

48 "action": "store_true", 

49 "default": False, 

50 "help": "Return command line as a string for testing" 

51 }, 

52 "additional options": { 

53 "nargs": "?", 

54 "type": str, 

55 "help": ( 

56 "all additional command line options are added to the " 

57 "printed summarypages executable" 

58 ) 

59 }, 

60 } 

61 ) 

62 return options 

63 

64 

65def _check_rundir(rundir, std_dirs): 

66 """Check that a list of directories are stored in a given rundir 

67 

68 Parameters 

69 ---------- 

70 rundir: str 

71 base directory to start searching 

72 std_dirs: list 

73 list of dirs which you expect to find in rundir or a subdir of rundir 

74 """ 

75 directories = [i[0] for i in os.walk(os.path.join(rundir, ""))] 

76 if all(any(std in dd for dd in directories) for std in std_dirs): 

77 return True 

78 return False 

79 

80 

81def is_lalinference_rundir(rundir): 

82 """Function to check if the rundir was made by LALInference 

83 

84 Parameters 

85 ---------- 

86 rundir: str 

87 the run directory of the parameter estimation job 

88 """ 

89 std_dirs = ["ROQdata", "engine", "posterior_samples", "caches", "log"] 

90 return _check_rundir(rundir, std_dirs) 

91 

92 

93def is_bilby_rundir(rundir): 

94 """Function to check if the rundir was made by Bilby 

95 

96 Parameters 

97 ---------- 

98 rundir: str 

99 the run directory of the parameter estimation job 

100 """ 

101 std_dirs = ["data", "result", "submit", "log_data_analysis"] 

102 return _check_rundir(rundir, std_dirs) 

103 

104 

105class Base(object): 

106 """ 

107 """ 

108 __arguments__ = { 

109 "samples": "samples", "config": "config", "webdir": "webdir", 

110 "approximant": "approximant", "labels": "label", "{}_psd": "psd", 

111 "{}_calibration": "calibration", "gwdata": "gwdata", 

112 "prior_file": "prior_file", "add_existing_plot": "add_existing_plot" 

113 } 

114 

115 def __init__( 

116 self, rundir, webdir=None, label=None, samples=None, config=None, other="", 

117 pattern=None 

118 ): 

119 if pattern is not None: 

120 self.pattern = { 

121 "posterior_file": "{}*{}".format(pattern, self.posterior_extension), 

122 "config_file": "{}*.ini".format(pattern), 

123 "prior_file": "{}.prior".format(pattern) 

124 } 

125 if "ignore_posterior_file" not in self.pattern.keys(): 

126 self.pattern["ignore_posterior_file"] = None 

127 self.rundir = os.path.abspath(rundir) 

128 self.parent_dir = Path(self.rundir).parent 

129 self.samples = samples 

130 self.config = config 

131 self.webdir = webdir 

132 self.label = label 

133 self.other = other 

134 self.gracedb = None 

135 self.approximant = None 

136 self.prior_file = None 

137 self.add_existing_plot = None 

138 self.psd = None 

139 self.calibration = None 

140 self.gwdata = None 

141 self.command_line_arguments = { 

142 arg.format(self.label): getattr(self, attribute, None) 

143 for arg, attribute in self.__arguments__.items() 

144 } 

145 self.command = self.make_command( 

146 self.command_line_arguments, other=self.other 

147 ) 

148 self.print(self.command) 

149 

150 @property 

151 def rundir(self): 

152 return self._rundir 

153 

154 @rundir.setter 

155 def rundir(self, rundir): 

156 self._rundir = rundir 

157 if not os.path.isdir(rundir): 

158 raise ValueError( 

159 "'{}' is not a valid directory.".format(rundir) 

160 ) 

161 

162 @property 

163 def config(self): 

164 return self._config 

165 

166 @config.setter 

167 def config(self, config): 

168 if config is not None and os.path.isfile(config): 

169 logger.info("Using specified config file: '{}'".format(config)) 

170 self._config = config 

171 return 

172 else: 

173 if config is not None: 

174 CUSTOM = True 

175 pattern = config 

176 else: 

177 CUSTOM = False 

178 pattern = self.pattern["config_file"] 

179 files, _dir = self.find_config_file(pattern) 

180 if CUSTOM and not len(files): 

181 raise FileNotFoundError( 

182 "The supplied config file: '{}' does not exist".format(config) 

183 ) 

184 self._config = self.check_files( 

185 files, "configuration file", pattern, _dir, allow_multiple=False, 

186 multiple_warn=( 

187 "If you wish to use a specific config file, please add the " 

188 "'--config' flag and the path to the config file you wish to " 

189 "use." 

190 ) 

191 ) 

192 

193 @property 

194 def webdir(self): 

195 return self._webdir 

196 

197 @webdir.setter 

198 def webdir(self, webdir): 

199 if webdir is None: 

200 self._webdir = self.webdir_fallback() 

201 else: 

202 logger.info("Using the supplied webdir: {}".format(webdir)) 

203 self._webdir = webdir 

204 

205 @property 

206 def samples(self): 

207 return self._samples 

208 

209 @samples.setter 

210 def samples(self, samples): 

211 if samples is not None and os.path.isfile(samples): 

212 logger.info("Using specified posterior file: '{}'".format(samples)) 

213 self._samples = samples 

214 return 

215 elif samples is not None: 

216 raise ValueError("Unable to find posterior file: {}".format(samples)) 

217 files = self.glob( 

218 self.rundir, self.pattern["posterior_file"], 

219 ignore=self.pattern["ignore_posterior_file"] 

220 ) 

221 files = self.preferred_samples_from_options(files) 

222 self._samples = self.check_files( 

223 files, "result file", self.pattern["posterior_file"], self.rundir, 

224 allow_multiple=False, multiple_warn=( 

225 "If you wish to use a specific samples file, please add the " 

226 "'--samples' flag and the path to the samples file you wish to " 

227 "use." 

228 ) 

229 ) 

230 

231 @property 

232 def label(self): 

233 return self._label 

234 

235 @label.setter 

236 def label(self, label): 

237 if label is None: 

238 try: 

239 self._label = self.label_fallback() 

240 except AttributeError: 

241 logger.info("No label provided. Using default") 

242 self._label = self.default_label 

243 else: 

244 logger.info("Using the provided label: '{}'".format(label)) 

245 self._label = label 

246 

247 @property 

248 def gracedb(self): 

249 return self._gracedb 

250 

251 @gracedb.setter 

252 def gracedb(self, gracedb): 

253 self._gracedb = self.gracedb_fallback() 

254 

255 @property 

256 def approximant(self): 

257 return self._approximant 

258 

259 @approximant.setter 

260 def approximant(self, approximant): 

261 self._approximant = self.approximant_fallback() 

262 

263 @property 

264 def prior_file(self): 

265 return self._prior_file 

266 

267 @prior_file.setter 

268 def prior_file(self, prior_file): 

269 self._prior_file = self.prior_file_fallback() 

270 

271 @property 

272 def add_existing_plot(self): 

273 return self._add_existing_plot 

274 

275 @add_existing_plot.setter 

276 def add_existing_plot(self, add_existing_plot): 

277 self._add_existing_plot = self.add_existing_plot_fallback() 

278 

279 @property 

280 def psd(self): 

281 return self._psd 

282 

283 @psd.setter 

284 def psd(self, psd): 

285 self._psd = self.psd_fallback() 

286 

287 @property 

288 def calibration(self): 

289 return self._calibration 

290 

291 @calibration.setter 

292 def calibration(self, calibration): 

293 self._calibration = self.calibration_fallback() 

294 

295 @property 

296 def gwdata(self): 

297 return self._gwdata 

298 

299 @gwdata.setter 

300 def gwdata(self, gwdata): 

301 self._gwdata = self.gwdata_fallback() 

302 

303 @staticmethod 

304 def glob(directory, name, ignore=None): 

305 """Recursively search a directory for a given file 

306 

307 Parameters 

308 ---------- 

309 directory: str 

310 directory to search in 

311 name: str 

312 file you wish to search for. This can include wildcards 

313 ignore: list, optional 

314 list of patterns to ignore 

315 """ 

316 files = glob.glob(os.path.join(directory, "**", name), recursive=True) 

317 if ignore is not None: 

318 files = list_match(files, ignore, return_false=True, return_true=False) 

319 return files 

320 

321 @staticmethod 

322 def check_files( 

323 files, description, filename, rundir, allow_failure=False, 

324 allow_multiple=True, multiple_warn=None, 

325 ): 

326 """Check a list of files found with `glob` 

327 

328 Parameters 

329 ---------- 

330 files: list 

331 list of files to check 

332 description: str 

333 description for the type of file you are searching for. This could 

334 be 'configuration file' for example 

335 filename: str 

336 pattern that was used to find the list of files with glob 

337 rundir: str 

338 run directory which was searched 

339 allow_failure: Bool, optional 

340 if True, bypass FileNotFoundError if the file does not exist 

341 allow_multiple: Bool, optional 

342 if True, do not raise a ValueError when multiple files are found 

343 which match a given pattern 

344 multiple_warn: str, optional 

345 warning message to print if multiple files are found 

346 """ 

347 if len(files) == 0: 

348 if not allow_failure: 

349 raise FileNotFoundError( 

350 "No file called '{}' found in {}.".format(filename, rundir) 

351 ) 

352 return None 

353 elif len(files) == 1: 

354 logger.info( 

355 "Found the following {}: {}".format(description, files[0]) 

356 ) 

357 return files[0] 

358 else: 

359 if allow_multiple: 

360 logger.info( 

361 "Multiple {}s found: {}. Using {}".format( 

362 description, ", ".join(files), files[0] 

363 ) 

364 ) 

365 if multiple_warn is not None: 

366 logger.warning(multiple_warn) 

367 return files[0] 

368 raise ValueError( 

369 "Multiple {}s found in {}: {}. Please either specify one from the command " 

370 "line or add a pattern unique to that file name via the '--pattern' " 

371 "command line argument".format( 

372 description, ", ".join(files), rundir 

373 ) 

374 ) 

375 

376 @staticmethod 

377 def find_executable(executable): 

378 """Return the path to a given executable 

379 

380 Parameters 

381 ---------- 

382 executable: str 

383 name of executable you wish to get the path for 

384 """ 

385 return shutil.which(executable) 

386 

387 @staticmethod 

388 def make_command(dictionary, other=""): 

389 """Make the command line 

390 

391 Parameters 

392 ---------- 

393 dictionary: dict 

394 dictionary of commands and arguments 

395 """ 

396 executable = Base.find_executable("summarypages") 

397 command = "summarypages " 

398 for key, val in dictionary.items(): 

399 cla = "--{}".format(key) 

400 if cla in other: 

401 ind = other.index(cla) 

402 logger.warning( 

403 "Ignoring {}={} extracted from config file and using input " 

404 "from command line {}={}".format( 

405 key, val, other[ind].replace("-", ""), other[ind + 1] 

406 ) 

407 ) 

408 elif val is True: 

409 command += "{} ".format(cla) 

410 elif val is None: 

411 continue 

412 elif not len(val): 

413 continue 

414 else: 

415 command += "{} {} ".format(cla, val) 

416 if len(other): 

417 command += " ".join(other) 

418 command += " " 

419 if "--gw" not in command: 

420 command += "--gw " 

421 return command 

422 

423 @staticmethod 

424 def print(command): 

425 """Print the command line to std.out 

426 

427 Parameters 

428 ---------- 

429 command: str 

430 command you wish to print 

431 """ 

432 logger.info("To run PESummary, run the following command line:") 

433 print("\n\t$ {}\n".format(command)) 

434 

435 @staticmethod 

436 @open_config(index=0) 

437 def load_config(path_to_config): 

438 """Load a configuration file with configparser 

439 

440 Parameters 

441 ---------- 

442 path_to_config: str 

443 path to the configuration file you wish to open 

444 """ 

445 return path_to_config 

446 

447 @property 

448 def preferred_posterior_string(self): 

449 return "" 

450 

451 def find_config_file(self, pattern): 

452 """Find a configuration file given a pattern 

453 

454 Parameters 

455 ---------- 

456 pattern: str 

457 pattern to find a specific config file 

458 """ 

459 return self.glob(self.rundir, pattern), self.rundir 

460 

461 def preferred_samples_from_options(self, files): 

462 """Return the preferred posterior samples file from a list of 

463 options 

464 

465 Parameters 

466 ---------- 

467 files: list 

468 list of available options 

469 """ 

470 _files = list_match(files, self.preferred_posterior_string) 

471 if len(_files): 

472 return _files 

473 return files 

474 

475 def webdir_fallback(self): 

476 return "webpage" 

477 

478 def gracedb_fallback(self): 

479 return 

480 

481 def approximant_fallback(self): 

482 return 

483 

484 def prior_file_fallback(self): 

485 return 

486 

487 def add_existing_plot_fallback(self): 

488 return 

489 

490 def psd_fallback(self): 

491 return 

492 

493 def calibration_fallback(self): 

494 return 

495 

496 def gwdata_fallback(self): 

497 return 

498 

499 def _webdir_fallback(self, webdir): 

500 """Print the webdir extracted from the config file 

501 

502 Parameters 

503 ---------- 

504 webdir: str 

505 webdir extracted from the config file 

506 """ 

507 logger.info( 

508 "Using the webdir '{}' extracted from the config file: {}".format( 

509 webdir, self.config 

510 ) 

511 ) 

512 return webdir 

513 

514 def _label_fallback(self, label): 

515 """Print the label extracted from the config file 

516 

517 Parameters 

518 ---------- 

519 label: str 

520 label extracted from the config file 

521 """ 

522 logger.info( 

523 "Found the following label in '{}': '{}'".format(self.config, label) 

524 ) 

525 return label 

526 

527 def _prior_file_fallback(self, prior_file): 

528 """Print the prior file found in the run directory 

529 

530 Parameters 

531 ---------- 

532 prior_file: str 

533 path to prior file found in the run directory 

534 """ 

535 logger.info( 

536 "Found the following prior file': '{}'".format(prior_file) 

537 ) 

538 return prior_file 

539 

540 def _gracedb_fallback(self, gid): 

541 """Print the gracedb ID extracted from the config file 

542 

543 Parameters 

544 ---------- 

545 gid: str 

546 gracedb ID extracted from the config file 

547 """ 

548 if gid is not None: 

549 logger.info( 

550 "Found the following gracedb ID in '{}': '{}'".format( 

551 self.config, gid 

552 ) 

553 ) 

554 return gid 

555 else: 

556 logger.info( 

557 "Unable to find a gracedb entry in '{}'".format(self.config) 

558 ) 

559 return None 

560 

561 def _approximant_fallback(self, approx): 

562 """Print the approximant extracted from the config file 

563 

564 Parameters 

565 ---------- 

566 approx: str 

567 approximant extracted from the config file 

568 """ 

569 approx = approx.split("pseudo")[0].split("_ROQ")[0] 

570 logger.info( 

571 "Found the following approximant in '{}': '{}'".format( 

572 self.config, approx 

573 ) 

574 ) 

575 return approx 

576 

577 

578class Bilby(Base): 

579 """Generate a `summarypages` executable for a bilby_pipe run directory 

580 """ 

581 def __init__(self, *args, **kwargs): 

582 self.default_label = "Bilby" 

583 self.posterior_extension = "_result.json" 

584 self.pattern = { 

585 "posterior_file": "*_result.json", 

586 "ignore_posterior_file": ["*checkpoint*"], 

587 "config_file": "*.ini", 

588 "prior_file": "*.prior" 

589 } 

590 super(Bilby, self).__init__(*args, **kwargs) 

591 

592 @property 

593 def preferred_posterior_string(self): 

594 return "*merged_result.json" 

595 

596 def find_config_file(self, pattern): 

597 """Find a configuration file given a pattern 

598 

599 Parameters 

600 ---------- 

601 pattern: str 

602 pattern to find a specific config file 

603 """ 

604 files = self.glob(self.rundir, pattern, ignore="*_complete.ini") 

605 if not len(files): 

606 return self.glob( 

607 self.parent_dir, pattern, ignore="*_complete.ini" 

608 ), self.parent_dir 

609 return files, self.rundir 

610 

611 def try_underscore_and_hyphen(self, config, option): 

612 """Try to find a key in a dictionary. If KeyError is raised, replace 

613 underscore with a hyphen and try again 

614 

615 Parameters 

616 ---------- 

617 config: dict 

618 dictionary you wish to search 

619 option: str 

620 key you wish to search for 

621 """ 

622 original = "_" if "_" in option else "-" 

623 alternative = "-" if original == "_" else "_" 

624 try: 

625 return config[option] 

626 except KeyError: 

627 return config[option.replace(original, alternative)] 

628 

629 def webdir_fallback(self): 

630 """Grab the web directory from a Bilby configuration file 

631 """ 

632 config = self.load_config(self.config) 

633 outdir = config["config"]["outdir"] 

634 if self.rundir in outdir: 

635 path = os.path.join(outdir, "webpage") 

636 else: 

637 path = os.path.join(self.rundir, outdir, "webpage") 

638 return self._webdir_fallback(path) 

639 

640 def label_fallback(self): 

641 """Grab the label from a Bilby configuration file 

642 """ 

643 config = self.load_config(self.config) 

644 label = config["config"]["label"] 

645 return self._label_fallback(label) 

646 

647 def gracedb_fallback(self): 

648 """Grab the gracedb entry from a Bilby configuration file 

649 """ 

650 config = self.load_config(self.config) 

651 try: 

652 gid = config["config"]["gracedb"] 

653 return self._gracedb_fallback(gid) 

654 except KeyError: 

655 return self._gracedb_fallback(None) 

656 

657 def approximant_fallback(self): 

658 """Grab the approximant used from a Bilby configuration file 

659 """ 

660 config = self.load_config(self.config) 

661 option = "waveform_approximant" 

662 approx = self.try_underscore_and_hyphen(config["config"], option) 

663 return self._approximant_fallback(approx) 

664 

665 def prior_file_fallback(self): 

666 """Grab the prior file used from a Bilby run directory 

667 """ 

668 prior_files = self.glob(self.rundir, self.pattern["prior_file"]) 

669 if not len(prior_files): 

670 prior_files = self.glob(self.parent_dir, self.pattern["prior_file"]) 

671 return self.check_files( 

672 prior_files, "prior_file", self.pattern["prior_file"], self.rundir, 

673 allow_failure=True 

674 ) 

675 

676 def add_existing_plot_fallback(self): 

677 """Grab the trace checkpoint plots generated from a Bilby run directory 

678 """ 

679 existing_plots = self.glob(self.rundir, "*checkpoint*.png") 

680 return " ".join( 

681 ["{}:{}".format(self.label, _plot) for _plot in existing_plots] 

682 ) 

683 

684 def psd_fallback(self): 

685 """Try and grab PSD data from a Bilby configuration file else 

686 look in the rundir for any PSD files 

687 """ 

688 config = self.load_config(self.config) 

689 try: 

690 return self.grab_psd_calibration_data_from_config(config, "psd_dict") 

691 except KeyError: 

692 logger.info( 

693 "Unable to find any PSD information in '{}'. Looking in " 

694 "run directory".format(self.config) 

695 ) 

696 return self.grab_psd_calibration_data_from_directory("PSDs") 

697 

698 def calibration_fallback(self): 

699 """Try and grab calibration data from a Bilby configuration file else 

700 look in the rundir for any calibration files 

701 """ 

702 config = self.load_config(self.config) 

703 try: 

704 return self.grab_psd_calibration_data_from_config( 

705 config, "spline_calibration_envelope_dict" 

706 ) 

707 except KeyError: 

708 logger.info( 

709 "Unable to find any calibration information in '{}'. Looking " 

710 "in run directory".format(self.config) 

711 ) 

712 return self.grab_psd_calibration_data_from_directory( 

713 "cal_env", _type="calibration" 

714 ) 

715 

716 def grab_psd_calibration_data_from_config(self, config, pattern): 

717 """Grab psd/calibration data from a Bilby configuration file 

718 

719 Parameters 

720 ---------- 

721 config: configparser.ConfigParser 

722 open configuration file 

723 pattern: str 

724 string to identify the data stored in the configuration file 

725 """ 

726 from pesummary.core.cli.actions import ConfigAction 

727 

728 data_dict = self.try_underscore_and_hyphen(config["config"], pattern) 

729 if data_dict is None or data_dict == "None": 

730 return 

731 try: 

732 data_dict = ConfigAction.dict_from_str(data_dict, delimiter=":") 

733 except IndexError: 

734 data_dict = ConfigAction.dict_from_str(data_dict, delimiter="=") 

735 if not len(data_dict): 

736 raise KeyError 

737 for key, value in data_dict.items(): 

738 if not os.path.isfile(value[0]): 

739 config_dir = Path(self.config).parent 

740 if os.path.isfile(os.path.join(config_dir, value[0])): 

741 data_dict[key] = os.path.join(config_dir, value[0]) 

742 else: 

743 logger.warning( 

744 "Found file: '{}' in the config file, but it " 

745 "does not exist. This is likely because the config " 

746 "was run on a different cluster. Ignoring from final " 

747 "command.".format(value[0]) 

748 ) 

749 raise KeyError 

750 else: 

751 data_dict[key] = value[0] 

752 return " ".join( 

753 ["{}:{}".format(key, val) for key, val in data_dict.items()] 

754 ) 

755 

756 def grab_psd_calibration_data_from_directory(self, pattern, _type="PSD"): 

757 """Grab psd/calibration data from a given run directory 

758 

759 Parameters 

760 ---------- 

761 pattern: str 

762 pattern to find a specific set of files 

763 """ 

764 from pesummary.gw.cli.inputs import _GWInput 

765 

766 files = self.glob(os.path.join(self.rundir, pattern), "*") 

767 if not len(files): 

768 files = self.glob(os.path.join(self.parent_dir, pattern), "*") 

769 if not len(files): 

770 logger.info( 

771 "No {} files found in '{}'".format(_type, self.rundir) 

772 ) 

773 return None 

774 data_dict = { 

775 _GWInput.get_ifo_from_file_name(ff): ff for ff in files 

776 } 

777 return " ".join( 

778 ["{}:{}".format(key, val) for key, val in data_dict.items()] 

779 ) 

780 

781 

782class LALInference(Base): 

783 """Generate a `summarypages` executable for a lalinference run directory 

784 """ 

785 def __init__(self, *args, **kwargs): 

786 self.default_label = "LALInference" 

787 self.posterior_extension = "hdf5" 

788 self.pattern = { 

789 "posterior_file": "posterior*.hdf5", 

790 "config_file": "config.ini" 

791 } 

792 super(LALInference, self).__init__(*args, **kwargs) 

793 

794 def webdir_fallback(self): 

795 """Grab the web directory from a LALInference configuration file 

796 """ 

797 config = self.load_config(self.config) 

798 path = config["paths"]["webdir"] 

799 return self._webdir_fallback(path) 

800 

801 def gracedb_fallback(self): 

802 """Grab the gracedb entry from a LALInference configuration file 

803 """ 

804 config = self.load_config(self.config) 

805 try: 

806 gid = config["input"]["gid"] 

807 return self._gracedb_fallback(gid) 

808 except KeyError: 

809 return self._gracedb_fallback(None) 

810 

811 def approximant_fallback(self): 

812 """Grab the approximant used from a LALInference configuration file 

813 """ 

814 config = self.load_config(self.config) 

815 approx = config["engine"]["approx"] 

816 return self._approximant_fallback(approx) 

817 

818 def psd_fallback(self): 

819 """Try and grab data from a LALInference configuration file else 

820 look in the rundir for any PSD files 

821 """ 

822 config = self.load_config(self.config) 

823 try: 

824 return self.grab_psd_calibration_data_from_config(config, "-psd") 

825 except KeyError: 

826 logger.info( 

827 "Unable to find any PSD information in '{}'. Looking in " 

828 "run directory".format(self.config) 

829 ) 

830 files = self.glob(self.rundir, "*-PSD.dat") 

831 if len(files) == 0: 

832 logger.info( 

833 "No PSD files found in '{}'".format(self.rundir) 

834 ) 

835 return None 

836 ifos = {re.split("([A-Z][0-9]+)-PSD.dat", i)[-2] for i in files} 

837 return " ".join( 

838 [ 

839 [ 

840 "{}:{}".format(ifo, i) for i in files if 

841 "{}-PSD.dat".format(ifo) in i 

842 ][0] for ifo in ifos 

843 ] 

844 ) 

845 

846 def calibration_fallback(self): 

847 """Grab calibration data from a LALInference configuration file 

848 """ 

849 config = self.load_config(self.config) 

850 try: 

851 return self.grab_psd_calibration_data_from_config( 

852 config, "-spcal-envelope" 

853 ) 

854 except KeyError: 

855 logger.info( 

856 "Unable to find any calibration information in '{}'".format( 

857 self.config 

858 ) 

859 ) 

860 return None 

861 

862 def gwdata_fallback(self): 

863 """Grab the GW cache files from a LALInference run directory 

864 """ 

865 try: 

866 cache_files = self.glob(self.rundir, "*.lcf") 

867 logger.info( 

868 "Found the following cache files in {}: {}".format( 

869 self.rundir, ", ".join(cache_files) 

870 ) 

871 ) 

872 ifos = {re.split("([A-Z]-[A-Z][0-9]+)", i)[-2] for i in cache_files} 

873 ifos = [ifo.split("-")[-1] for ifo in ifos] 

874 config = self.load_config(self.config) 

875 channels = ast.literal_eval(config["data"]["channels"]) 

876 return " ".join( 

877 [ 

878 "{}:{}".format(channels[ifo], path) for ifo, path 

879 in zip(ifos, cache_files) 

880 ] 

881 ) 

882 except ValueError: 

883 return None 

884 

885 def grab_psd_calibration_data_from_config(self, config, pattern): 

886 """Grab psd/calibration data from a LALInference configuration file 

887 

888 Parameters 

889 ---------- 

890 config: configparser.ConfigParser 

891 open configuration file 

892 pattern: str 

893 string to identify the data stored in the configuration file 

894 """ 

895 ifos = [i for i in list(config["engine"].keys()) if pattern in i] 

896 data = { 

897 ifo.split(pattern)[0].upper(): config["engine"][ifo] for ifo in 

898 ifos if len(ifos) 

899 } 

900 if not len(ifos): 

901 raise KeyError 

902 return " ".join( 

903 ["{}:{}".format(key, val) for key, val in data.items()] 

904 ) 

905 

906 

907def main(args=None): 

908 """Top level interface for `summarypipe` 

909 """ 

910 parser = ArgumentParser(description=__doc__) 

911 parser.add_known_options_to_parser( 

912 [ 

913 "--rundir", "--webdir", "--labels", "--config", "--samples", 

914 "--pattern", "--return_string", "additional options" 

915 ] 

916 ) 

917 opts, unknown = parser.parse_known_args(args=args) 

918 if args is None: 

919 all_options = sys.argv[1:] 

920 else: 

921 all_options = args 

922 idxs = [all_options.index(_) for _ in unknown if "--" in _] 

923 unknown = np.array([ 

924 [all_options[i], all_options[i + 1]] if i + 1 < len(all_options) 

925 and "--" not in all_options[i + 1] else [all_options[i], ""] for i in 

926 idxs 

927 ]).flatten() 

928 label = opts.labels[0] if opts.labels is not None else None 

929 if is_lalinference_rundir(opts.rundir): 

930 cl = LALInference( 

931 opts.rundir, webdir=opts.webdir, label=label, samples=opts.samples, 

932 config=opts.config, other=unknown, pattern=opts.pattern 

933 ) 

934 elif is_bilby_rundir(opts.rundir): 

935 cl = Bilby( 

936 opts.rundir, webdir=opts.webdir, label=label, samples=opts.samples, 

937 config=opts.config, other=unknown, pattern=opts.pattern 

938 ) 

939 else: 

940 raise NotImplementedError( 

941 "'{}' not understood. Currently 'summarypipe' only works with a " 

942 "LALInference or Bilby rundir.".format(opts.rundir) 

943 ) 

944 if opts.return_string: 

945 return cl.command