Coverage for pesummary/gw/webpage/main.py: 76.7%

382 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-12-09 22:34 +0000

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

2 

3import os 

4import numpy as np 

5 

6from pesummary.core.webpage.main import _WebpageGeneration as _CoreWebpageGeneration 

7from pesummary.core.webpage.main import PlotCaption 

8from pesummary.gw.file.standard_names import descriptive_names 

9from pesummary.utils.utils import logger 

10from pesummary import conf 

11 

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

13 

14 

15class CommandLineCaption(object): 

16 """Class to handle generating the command line used to generate a plot and 

17 a caption to describe the plot 

18 

19 Parameters 

20 ---------- 

21 """ 

22 def __init__(self, command_line_arguments, samples=None): 

23 self.args = command_line_arguments 

24 self.samples = samples 

25 self.executable = self.get_executable("summaryplots") 

26 

27 @staticmethod 

28 def get_executable(executable): 

29 """Return the path to an executable 

30 

31 Parameters 

32 ---------- 

33 executable: str 

34 the name of the executable you wish to find 

35 """ 

36 from subprocess import check_output 

37 

38 path = check_output(["which", executable]).decode("utf-8").strip() 

39 return path 

40 

41 @property 

42 def command_line(self): 

43 """Generate the command line used to generate the plot 

44 """ 

45 return "{} {}".format(self.executable, " ".join(self.args)) 

46 

47 @property 

48 def caption(self): 

49 """Make a caption to describe the plot 

50 """ 

51 general_cli = "" 

52 if "1d_histogram" in self.args[0]: 

53 return self.histogram_caption() 

54 if "skymap" in self.args[0]: 

55 return self.skymap_caption() 

56 return general_cli 

57 

58 def histogram_caption(self): 

59 """Return a caption to describe the 1d histogram plot 

60 """ 

61 args = self.args[0].split(" ") 

62 parameter = args[args.index("--parameter") + 1] 

63 general_cli = ( 

64 "1d histogram showing the posterior distribution for " 

65 "{}.".format(parameter) 

66 ) 

67 if parameter == "chi_p": 

68 general_cli += ( 

69 " chi_p is the precession parameter and quantifies how much " 

70 "precession there is in the system. It ranges from 0 to 1 " 

71 "with 0 meaning there is no precession in the system and 1 " 

72 "meaning there is maximal precession in the system." 

73 ) 

74 if self.samples is not None: 

75 general_cli += ( 

76 " The median of the distribution is {} with 90% confidence " 

77 "interval {}".format( 

78 np.round(self.samples.average(type="median"), 3), 

79 [np.round(i, 3) for i in self.samples.credible_interval()] 

80 ) 

81 ) 

82 return general_cli 

83 

84 def skymap_caption(self): 

85 """Return a caption to describe the skymap plot 

86 """ 

87 general_cli = ( 

88 "Skymap showing the possible location of the source of the " 

89 "source of the gravitational waves" 

90 ) 

91 

92 

93class _WebpageGeneration(_CoreWebpageGeneration): 

94 """ 

95 """ 

96 def __init__( 

97 self, webdir=None, samples=None, labels=None, publication=None, 

98 user=None, config=None, same_parameters=None, base_url=None, 

99 file_versions=None, hdf5=None, colors=None, custom_plotting=None, 

100 pepredicates_probs=None, gracedb=None, approximant=None, key_data=None, 

101 file_kwargs=None, existing_labels=None, existing_config=None, 

102 existing_file_version=None, existing_injection_data=None, 

103 existing_samples=None, existing_metafile=None, add_to_existing=False, 

104 existing_file_kwargs=None, existing_weights=None, result_files=None, 

105 notes=None, disable_comparison=False, pastro_probs=None, gwdata=None, 

106 disable_interactive=False, publication_kwargs={}, no_ligo_skymap=False, 

107 psd=None, priors=None, package_information={"packages": []}, 

108 mcmc_samples=False, external_hdf5_links=False, preliminary_pages=False, 

109 existing_plot=None, disable_expert=False, analytic_priors=None 

110 ): 

111 self.pepredicates_probs = pepredicates_probs 

112 self.pastro_probs = pastro_probs 

113 self.gracedb = gracedb 

114 self.approximant = approximant 

115 self.file_kwargs = file_kwargs 

116 self.publication = publication 

117 self.publication_kwargs = publication_kwargs 

118 self.result_files = result_files 

119 self.gwdata = gwdata 

120 self.no_ligo_skymap = no_ligo_skymap 

121 self.psd = psd 

122 self.priors = priors 

123 

124 super(_WebpageGeneration, self).__init__( 

125 webdir=webdir, samples=samples, labels=labels, 

126 publication=publication, user=user, config=config, 

127 same_parameters=same_parameters, base_url=base_url, 

128 file_versions=file_versions, hdf5=hdf5, colors=colors, 

129 custom_plotting=custom_plotting, 

130 existing_labels=existing_labels, existing_config=existing_config, 

131 existing_file_version=existing_file_version, 

132 existing_injection_data=existing_injection_data, 

133 existing_samples=existing_samples, 

134 existing_metafile=existing_metafile, 

135 existing_file_kwargs=existing_file_kwargs, 

136 existing_weights=existing_weights, 

137 add_to_existing=add_to_existing, notes=notes, 

138 disable_comparison=disable_comparison, 

139 disable_interactive=disable_interactive, 

140 package_information=package_information, mcmc_samples=mcmc_samples, 

141 external_hdf5_links=external_hdf5_links, key_data=key_data, 

142 existing_plot=existing_plot, disable_expert=disable_expert, 

143 analytic_priors=analytic_priors 

144 ) 

145 if self.file_kwargs is None: 

146 self.file_kwargs = { 

147 label: {"sampler": {}, "meta_data": {}} for label in self.labels 

148 } 

149 if self.approximant is None: 

150 self.approximant = {label: None for label in self.labels} 

151 if self.result_files is None: 

152 self.result_files = [None] * len(self.labels) 

153 self.psd_path = {"other": os.path.join("..", "psds")} 

154 self.calibration_path = {"other": os.path.join("..", "calibration")} 

155 self.preliminary_pages = preliminary_pages 

156 if not isinstance(self.preliminary_pages, dict): 

157 if self.preliminary_pages: 

158 self.preliminary_pages = { 

159 label: True for label in self.labels 

160 } 

161 else: 

162 self.preliminary_pages = { 

163 label: False for label in self.labels 

164 } 

165 if all(value for value in self.preliminary_pages.values()): 

166 self.all_pages_preliminary = True 

167 if len(self.labels) > 1: 

168 if any(value for value in self.preliminary_pages.values()): 

169 self.preliminary_pages["Comparison"] = True 

170 

171 def categorize_parameters(self, parameters): 

172 """Categorize the parameters into common headings 

173 

174 Parameters 

175 ---------- 

176 parameters: list 

177 list of parameters that you would like to sort 

178 """ 

179 return super(_WebpageGeneration, self).categorize_parameters( 

180 parameters, starting_letter=False 

181 ) 

182 

183 def categorize_2d_parameters(self, samples): 

184 """Categorize the default 2d parameters and decide the common 

185 headings 

186 

187 Parameters 

188 ---------- 

189 parameters: list 

190 list of parameters that you would like to sort 

191 """ 

192 sort = {} 

193 for params in conf.gw_2d_plots: 

194 if not all(p in samples.keys() for p in params): 

195 continue 

196 if not all(len(np.unique(samples[p])) > 1 for p in params): 

197 continue 

198 headings = self.categorize_parameters(params) 

199 for hh in headings[::-1]: 

200 if len(hh[1]) and hh[0] not in ["others"]: 

201 heading = hh[0] 

202 break 

203 elif len(hh[1]) and "geocent_time" in params: 

204 heading = "timings" 

205 break 

206 if heading not in sort.keys(): 

207 sort[heading] = [] 

208 sort[heading] += params 

209 return sort 

210 

211 def _kde_from_same_samples(self, param, samples): 

212 """Generate KDEs for a set of samples 

213 

214 Parameters 

215 ---------- 

216 param: str 

217 The parameter that the samples belong to 

218 samples: list 

219 list of samples for each result file 

220 """ 

221 from pesummary.utils.bounded_1d_kde import ReflectionBoundedKDE 

222 from pesummary.gw.plots.plot import _return_bounds 

223 

224 xlow, xhigh = _return_bounds(param, samples, comparison=True) 

225 return super(_WebpageGeneration, self)._kde_from_same_samples( 

226 param, samples, kde=ReflectionBoundedKDE, xlow=xlow, xhigh=xhigh 

227 ) 

228 

229 def make_navbar_for_homepage(self): 

230 """Make a navbar for the homepage 

231 """ 

232 links = super(_WebpageGeneration, self).make_navbar_for_homepage() 

233 if self.gwdata is not None: 

234 links.append(["Detchar", [i for i in self.gwdata.keys()]]) 

235 return links 

236 

237 def make_navbar_for_result_page(self): 

238 """Make a navbar for the result page homepage 

239 """ 

240 links = super(_WebpageGeneration, self).make_navbar_for_result_page() 

241 for num, label in enumerate(self.labels): 

242 links[label].append( 

243 ["2d Histograms", [ 

244 {"masses": label}, {"spins": label}, 

245 {"location": label}, {"timings": label} 

246 ]] 

247 ) 

248 if self.pepredicates_probs[label] is not None: 

249 links[label].append({"Classification": label}) 

250 return links 

251 

252 def generate_webpages(self): 

253 """Generate all webpages for all result files passed 

254 """ 

255 super(_WebpageGeneration, self).generate_webpages() 

256 self.make_2d_histogram_pages() 

257 if self.publication: 

258 self.make_publication_pages() 

259 if self.gwdata is not None: 

260 self.make_detector_pages() 

261 if all(val is not None for key, val in self.pepredicates_probs.items()): 

262 self.make_classification_pages() 

263 

264 def _make_home_pages(self, pages): 

265 """Make the home pages 

266 

267 Parameters 

268 ---------- 

269 pages: list 

270 list of pages that you wish to create 

271 """ 

272 title = None if self.gracedb is None else ( 

273 "Parameter Estimation Summary Pages for {}".format(self.gracedb) 

274 ) 

275 banner = "Summary" if self.gracedb is None else ( 

276 "Summary for {}".format(self.gracedb) 

277 ) 

278 html_file = self.setup_page("home", self.navbar["home"], title=title) 

279 html_file.make_banner(approximant=banner, key="Summary") 

280 path = self.image_path["home"] 

281 

282 if self.make_comparison: 

283 if not all(self.approximant[i] is not None for i in self.labels): 

284 image_contents = [] 

285 _plot = os.path.join(path, "compare_time_domain_waveforms.png") 

286 if os.path.isfile(_plot): 

287 image_contents.append(_plot) 

288 image_contents = [image_contents] 

289 html_file = self.make_modal_carousel( 

290 html_file, image_contents, unique_id=True 

291 ) 

292 

293 for i in self.labels: 

294 html_file.make_banner(approximant=i, key=i) 

295 image_contents, captions = [], [] 

296 basic_string = os.path.join(self.webdir, "plots", "{}.png") 

297 relative_path = os.path.join(path, "{}.png") 

298 if os.path.isfile(basic_string.format("%s_strain" % (i))): 

299 image_contents.append(relative_path.format("%s_strain" % (i))) 

300 captions.append(PlotCaption("strain")) 

301 if os.path.isfile(basic_string.format("%s_psd_plot" % (i))): 

302 image_contents.append(relative_path.format("%s_psd_plot" % (i))) 

303 captions.append(PlotCaption("psd")) 

304 if os.path.isfile( 

305 basic_string.format("%s_waveform_time_domain" % (i)) 

306 ): 

307 image_contents.append( 

308 relative_path.format("%s_waveform_time_domain" % (i)) 

309 ) 

310 captions.append(PlotCaption("time_waveform")) 

311 if os.path.isfile( 

312 basic_string.format("%s_calibration_plot" % (i)) 

313 ): 

314 image_contents.append( 

315 relative_path.format("%s_calibration_plot" % (i)) 

316 ) 

317 captions.append(PlotCaption("calibration")) 

318 image_contents = [image_contents] 

319 html_file = self.make_modal_carousel( 

320 html_file, image_contents, unique_id=True, extra_div=True, 

321 captions=[captions] 

322 ) 

323 

324 for _key in ["sampler", "meta_data"]: 

325 if _key == "sampler": 

326 html_file.make_banner( 

327 approximant="Sampler kwargs", key="sampler_kwargs", 

328 _style="font-size: 26px;" 

329 ) 

330 else: 

331 html_file.make_banner( 

332 approximant="Meta data", key="meta_data", 

333 _style="font-size: 26px;" 

334 ) 

335 _style = "margin-top:3em; margin-bottom:5em; max-width:1400px" 

336 _class = "row justify-content-center" 

337 html_file.make_container(style=_style) 

338 html_file.make_div(4, _class=_class, _style=None) 

339 

340 base_label = self.labels[0] 

341 total_keys = list(self.file_kwargs[base_label][_key].keys()) 

342 if len(self.labels) > 1: 

343 for _label in self.labels[1:]: 

344 total_keys += [ 

345 key for key in self.file_kwargs[_label][_key].keys() 

346 if key not in total_keys 

347 ] 

348 _headings = ["label"] + total_keys 

349 table_contents = [ 

350 [i] + [ 

351 self.file_kwargs[i][_key][key] if key in 

352 self.file_kwargs[i][_key].keys() else "-" for key in 

353 total_keys 

354 ] for i in self.labels 

355 ] 

356 html_file.make_table( 

357 headings=_headings, format="table-hover", heading_span=1, 

358 contents=table_contents, accordian=False 

359 ) 

360 html_file.end_div(4) 

361 html_file.end_container() 

362 html_file.export("{}.csv".format(_key)) 

363 

364 html_file.make_footer(user=self.user, rundir=self.webdir) 

365 html_file.close() 

366 super(_WebpageGeneration, self)._make_home_pages(pages, make_home=False) 

367 

368 def make_2d_histogram_pages(self): 

369 """Wrapper function for _make_2d_histogram pages 

370 """ 

371 pages = [ 

372 "{}_{}_{}".format(i, i, j) for i in self.labels for j in 

373 self.categorize_2d_parameters(self.samples[i]).keys() 

374 ] 

375 self.create_blank_html_pages(pages) 

376 self._make_2d_histogram_pages(pages) 

377 

378 def _make_2d_histogram_pages(self, pages): 

379 """Make the 2d histogram pages 

380 """ 

381 for num, i in enumerate(self.labels): 

382 for j, k in self.categorize_2d_parameters(self.samples[i]).items(): 

383 html_file = self.setup_page( 

384 "{}_{}".format(i, j), self.navbar["result_page"][i], 

385 i, title="{} Posterior PDFs describing the {}".format(i, j), 

386 approximant=i, background_colour=self.colors[num], 

387 histogram_download=False, toggle=self.expert_plots 

388 ) 

389 html_file.make_banner(approximant=i, key=i) 

390 path = self.image_path["other"] 

391 contents = [ 

392 path + "{}_2d_posterior_{}_{}.png".format(i, k[l], k[l+1]) 

393 for l in range(0, len(k), 2) 

394 ] 

395 if len(contents) < 2: 

396 autoscale = False 

397 else: 

398 autoscale = True 

399 image_contents = [ 

400 contents[l:2 + l] for l in range(0, len(contents), 2) 

401 ] 

402 html_file.make_table_of_images( 

403 contents=image_contents, code="changeimage", 

404 mcmc_samples=False, autoscale=autoscale 

405 ) 

406 key_data = self.key_data 

407 contents = [] 

408 headings = [" "] + self.key_data_headings.copy() 

409 _injection = False 

410 rows = [] 

411 for param in np.unique(k): 

412 _row = [param] 

413 _row += self.key_data_table[i][param] 

414 rows.append(_row) 

415 _style = "margin-top:3em; margin-bottom:5em; max-width:1400px" 

416 _class = "row justify-content-center" 

417 html_file.make_container(style=_style) 

418 html_file.make_div(4, _class=_class, _style=None) 

419 html_file.make_table( 

420 headings=headings, contents=rows, heading_span=1, 

421 accordian=False, format="table-hover" 

422 ) 

423 html_file.end_div(4) 

424 html_file.end_container() 

425 html_file.export("summary_information_{}.csv".format(i)) 

426 html_file.make_footer(user=self.user, rundir=self.webdir) 

427 html_file.close() 

428 

429 def make_publication_pages(self): 

430 """Wrapper function for _make_publication_pages() 

431 """ 

432 pages = ["Publication"] 

433 self.create_blank_html_pages(pages) 

434 self._make_publication_pages(pages) 

435 

436 def _make_publication_pages(self, pages): 

437 """Make the publication pages 

438 

439 Parameters 

440 ---------- 

441 pages: list 

442 list of pages that you wish to create 

443 """ 

444 from glob import glob 

445 

446 executable = self.get_executable("summarypublication") 

447 general_cli = "%s --webdir %s --samples %s --labels %s --plot {}" % ( 

448 executable, os.path.join(self.webdir, "plots", "publication"), 

449 " ".join(self.result_files), " ".join(self.labels) 

450 ) 

451 if self.publication_kwargs != {}: 

452 general_cli += "--publication_kwargs %s" % ( 

453 " ".join( 

454 [ 

455 "{}:{}".format(key, value) for key, value in 

456 self.publication_kwargs.items() 

457 ] 

458 ) 

459 ) 

460 html_file = self.setup_page( 

461 "Publication", self.navbar["home"], title="Publication Plots" 

462 ) 

463 html_file.make_banner(approximant="Publication", key="Publication") 

464 path = self.image_path["other"] 

465 pub_plots = glob( 

466 os.path.join(self.webdir, "plots", "publication", "*.png") 

467 ) 

468 for num, i in enumerate(pub_plots): 

469 shortened_path = i.split("/plots/")[-1] 

470 pub_plots[num] = path + shortened_path 

471 cli = [] 

472 cap = [] 

473 posterior_name = \ 

474 lambda i: "{} ({})".format(i, descriptive_names[i]) if i in \ 

475 descriptive_names.keys() and descriptive_names[i] != "" else i 

476 for i in pub_plots: 

477 filename = i.split("/")[-1] 

478 if "violin_plot" in filename: 

479 parameter = filename.split("violin_plot_")[-1].split(".png")[0] 

480 cli.append( 

481 general_cli.format("violin") + " --parameters %s" % ( 

482 parameter 

483 ) 

484 ) 

485 cap.append( 

486 PlotCaption("violin").format(posterior_name(parameter)) 

487 ) 

488 elif "spin_disk" in filename: 

489 cli.append(general_cli.format("spin_disk")) 

490 cap.append(PlotCaption("spin_disk")) 

491 elif "2d_contour" in filename: 

492 parameters = filename.split("2d_contour_plot_")[-1].split(".png")[0] 

493 cli.append( 

494 general_cli.format("2d_contour") + " --parameters %s" % ( 

495 parameters.replace("_and_", " ") 

496 ) 

497 ) 

498 pp = parameters.split("_and_") 

499 cap.append( 

500 PlotCaption("2d_contour").format( 

501 posterior_name(pp[0]), posterior_name(pp[1]) 

502 ) 

503 ) 

504 image_contents = [ 

505 pub_plots[i:3 + i] for i in range(0, len(pub_plots), 3) 

506 ] 

507 command_lines = [ 

508 cli[i:3 + i] for i in range(0, len(cli), 3) 

509 ] 

510 captions = [cap[i:3 + i] for i in range(0, len(cap), 3)] 

511 html_file = self.make_modal_carousel( 

512 html_file, image_contents, cli=command_lines, captions=captions 

513 ) 

514 html_file.make_footer(user=self.user, rundir=self.webdir) 

515 html_file.close() 

516 

517 def make_detector_pages(self): 

518 """Wrapper function for _make_publication_pages() 

519 """ 

520 pages = [i for i in self.gwdata.keys()] 

521 self.create_blank_html_pages(pages) 

522 self._make_detector_pages(pages) 

523 

524 def _make_detector_pages(self, pages): 

525 """Make the detector characterisation pages 

526 

527 Parameters 

528 ---------- 

529 pages: list 

530 list of pages that you wish to create 

531 """ 

532 from pesummary.utils.utils import ( 

533 determine_gps_time_and_window, command_line_dict 

534 ) 

535 from astropy.time import Time 

536 

537 executable = self.get_executable("summarydetchar") 

538 try: 

539 command_line = command_line_dict() 

540 except SystemExit: 

541 command_line = {"gwdata": {}} 

542 if isinstance(command_line["gwdata"], dict): 

543 gwdata_command_line = [ 

544 "{}:{}".format(key, val) for key, val in 

545 command_line["gwdata"].items() 

546 ] 

547 else: 

548 gwdata_command_line = command_line["gwdata"] 

549 if gwdata_command_line is None: 

550 gwdata_command_line = [] 

551 general_cli = "%s --webdir %s --gwdata %s --plot {}{}" % ( 

552 executable, os.path.join(self.webdir, "plots"), 

553 " ".join(gwdata_command_line) 

554 ) 

555 path = self.image_path["other"] 

556 base = os.path.join(path, "{}_{}.png") 

557 ADD_DETCHAR_LINK = True 

558 try: 

559 maxL_samples = { 

560 i: { 

561 "geocent_time": self.key_data[i]["geocent_time"]["maxL"] 

562 } for i in self.labels 

563 } 

564 except KeyError: 

565 # trying a different name for time 

566 try: 

567 maxL_samples = { 

568 i: { 

569 "geocent_time": self.key_data[i][ 

570 "marginalized_geocent_time" 

571 ]["maxL"] 

572 } for i in self.labels 

573 } 

574 except KeyError: 

575 logger.warning( 

576 "Failed to find a time parameter to link to detchar/" 

577 "summary pages. Not adding link to webpages." 

578 ) 

579 ADD_DETCHAR_LINK = False 

580 if ADD_DETCHAR_LINK: 

581 gps_time, window = determine_gps_time_and_window(maxL_samples, self.labels) 

582 t = Time(gps_time, format='gps') 

583 t = Time(t, format='datetime') 

584 link = ( 

585 "https://ldas-jobs.ligo-wa.caltech.edu/~detchar/summary/day" 

586 "/{}{}{}/".format( 

587 t.value.year, 

588 "0{}".format(t.value.month) if t.value.month < 10 else t.value.month, 

589 "0{}".format(t.value.day) if t.value.day < 10 else t.value.day 

590 ) 

591 ) 

592 else: 

593 link = None 

594 gps_time, window = None, None 

595 for det in self.gwdata.keys(): 

596 html_file = self.setup_page( 

597 det, self.navbar["home"], title="{} Detchar".format(det) 

598 ) 

599 html_file.make_banner(approximant=det, key="detchar", link=link) 

600 image_contents = [ 

601 [base.format("spectrogram", det), base.format("omegascan", det)] 

602 ] 

603 command_lines = [ 

604 [ 

605 general_cli.format("spectrogram", ""), 

606 general_cli.format( 

607 "omegascan", "--gps %s --vmin 0 --vmax 25 --window %s" % ( 

608 gps_time, window 

609 ) 

610 ) 

611 ] 

612 ] 

613 html_file = self.make_modal_carousel( 

614 html_file, image_contents, cli=command_lines, autoscale=True, 

615 ) 

616 html_file.make_footer(user=self.user, rundir=self.webdir) 

617 html_file.close() 

618 

619 def make_classification_pages(self): 

620 """Wrapper function for _make_publication_pages() 

621 """ 

622 pages = ["{}_{}_Classification".format(i, i) for i in self.labels] 

623 self.create_blank_html_pages(pages) 

624 self._make_classification_pages(pages) 

625 

626 def _make_classification_pages(self, pages): 

627 """Make the classification pages 

628 

629 Parameters 

630 ---------- 

631 pages: list 

632 list of pages that you wish to create 

633 """ 

634 executable = self.get_executable("summaryclassification") 

635 general_cli = "%s --samples {}" % (executable) 

636 for num, label in enumerate(self.labels): 

637 html_file = self.setup_page( 

638 "{}_Classification".format(label), 

639 self.navbar["result_page"][label], label, 

640 title="{} Classification".format(label), 

641 background_colour=self.colors[num], approximant=label 

642 ) 

643 html_file.make_banner(approximant=label, key="classification") 

644 

645 if self.pepredicates_probs[label] is not None: 

646 html_file.make_container() 

647 _class = "row justify-content-center" 

648 html_file.make_div(4, _class=_class, _style=None) 

649 keys = list(self.pepredicates_probs[label]["default"].keys()) 

650 table_contents = [ 

651 ["{} prior".format(i)] + [ 

652 self.pepredicates_probs[label][i][j] for j in keys 

653 ] for i in ["default", "population"] 

654 ] 

655 if self.pastro_probs[label] is not None: 

656 keys += ["HasNS"] 

657 keys += ["HasRemnant"] 

658 table_contents[0].append(self.pastro_probs[label]["default"]["HasNS"]) 

659 table_contents[0].append( 

660 self.pastro_probs[label]["default"]["HasRemnant"] 

661 ) 

662 try: 

663 table_contents[1].append( 

664 self.pastro_probs[label]["population"]["HasNS"] 

665 ) 

666 table_contents[1].append( 

667 self.pastro_probs[label]["population"]["HasRemnant"] 

668 ) 

669 except KeyError: 

670 table_contents[1].append("-") 

671 table_contents[1].append("-") 

672 logger.warning( 

673 "Failed to add 'em_bright' probabilities for population " 

674 "reweighted prior" 

675 ) 

676 html_file.make_table( 

677 headings=[" "] + keys, contents=table_contents, 

678 heading_span=1, accordian=False 

679 ) 

680 html_file.make_cli_button( 

681 general_cli.format(self.result_files[num]) 

682 ) 

683 html_file.export( 

684 "classification_{}.csv".format(label), 

685 margin_top="-1.5em", margin_bottom="0.5em", json=True 

686 ) 

687 html_file.end_div(4) 

688 html_file.end_container() 

689 path = self.image_path["other"] 

690 base = os.path.join(path, "%s_{}_pepredicates{}.png" % (label)) 

691 image_contents = [ 

692 [ 

693 base.format("default", ""), base.format("default", "_bar"), 

694 base.format("population", ""), 

695 base.format("population", "_bar") 

696 ] 

697 ] 

698 base = ( 

699 "%s --webdir %s --labels %s --plot {} --prior {}" % ( 

700 general_cli.format(self.result_files[num]), 

701 os.path.join(self.webdir, "plots"), label 

702 ) 

703 ) 

704 command_lines = [ 

705 [ 

706 base.format("mass_1_mass_2", "default"), 

707 base.format("bar", "default"), 

708 base.format("mass_1_mass_2", "population"), 

709 base.format("bar", "population") 

710 ] 

711 ] 

712 captions = [ 

713 [ 

714 PlotCaption("default_classification_mass_1_mass_2"), 

715 PlotCaption("default_classification_bar"), 

716 PlotCaption("population_classification_mass_1_mass_2"), 

717 PlotCaption("population_classification_bar") 

718 ] 

719 ] 

720 html_file = self.make_modal_carousel( 

721 html_file, image_contents, cli=command_lines, autoscale=True, 

722 captions=captions 

723 ) 

724 html_file.make_footer(user=self.user, rundir=self.webdir) 

725 html_file.close() 

726 

727 def _make_entry_in_downloads_table(self, html_file, label, num, base_string): 

728 """Make a label specific entry into the downloads table 

729 

730 Parameters 

731 ---------- 

732 label: str 

733 the label you wish to add to the downloads table 

734 base_string: str 

735 the download string 

736 """ 

737 table = super(_WebpageGeneration, self)._make_entry_in_downloads_table( 

738 html_file, label, num, base_string 

739 ) 

740 if not self.no_ligo_skymap: 

741 table.append( 

742 [ 

743 base_string.format( 

744 "Fits file containing skymap for this analysis", 

745 self.results_path["other"] + "%s_skymap.fits" % (label) 

746 ) 

747 ] 

748 ) 

749 if self.psd is not None and self.psd != {} and label in self.psd.keys(): 

750 for ifo in self.psd[label].keys(): 

751 if len(self.psd[label][ifo]): 

752 table.append( 

753 [ 

754 base_string.format( 

755 "%s psd file used for this analysis" % (ifo), 

756 os.path.join( 

757 self.psd_path["other"], 

758 "%s_%s_psd.dat" % (label, ifo) 

759 ) 

760 ) 

761 ] 

762 ) 

763 if self.priors is not None and "calibration" in self.priors.keys(): 

764 if label in self.priors["calibration"].keys(): 

765 for ifo in self.priors["calibration"][label].keys(): 

766 if len(self.priors["calibration"][label][ifo]): 

767 table.append( 

768 [ 

769 base_string.format( 

770 "%s calibration envelope file used for " 

771 "this analysis" % (ifo), os.path.join( 

772 self.calibration_path["other"], 

773 "%s_%s_cal.txt" % (label, ifo) 

774 ) 

775 ) 

776 ] 

777 ) 

778 return table 

779 

780 def default_images_for_result_page(self, label): 

781 """Return the default images that will be displayed on the result page 

782 """ 

783 path = self.image_path["other"] 

784 base_string = path + "%s_{}.png" % (label) 

785 image_contents = [ 

786 [ 

787 base_string.format("1d_posterior_mass_1"), 

788 base_string.format("1d_posterior_mass_2"), 

789 ], [ 

790 base_string.format("1d_posterior_a_1"), 

791 base_string.format("1d_posterior_a_2"), 

792 base_string.format("1d_posterior_chi_eff") 

793 ], [ 

794 base_string.format("1d_posterior_iota"), 

795 base_string.format("skymap"), 

796 base_string.format("waveform"), 

797 base_string.format("1d_posterior_luminosity_distance"), 

798 ] 

799 ] 

800 executable = self.get_executable("summaryplots") 

801 general_cli = ( 

802 "%s --webdir %s --samples %s --burnin %s --plot {} {} " 

803 "--labels %s" % ( 

804 executable, os.path.join(self.webdir, "plots"), 

805 self.result_files[self.labels.index(label)], conf.burnin, label 

806 ) 

807 ) 

808 cli = [ 

809 [ 

810 general_cli.format("1d_histogram", "--parameter mass_1"), 

811 general_cli.format("1d_histgram", "--parameter mass_2"), 

812 ], [ 

813 general_cli.format("1d_histogram", "--parameter a_1"), 

814 general_cli.format("1d_histogram", "--parameter a_2"), 

815 general_cli.format("1d_histogram", "--parameter chi_eff") 

816 ], [ 

817 general_cli.format("1d_histogram", "--parameter iota"), 

818 general_cli.format("skymap", ""), 

819 general_cli.format("waveform", ""), 

820 general_cli.format( 

821 "1d_histogram", "--parameter luminosity_distance" 

822 ), 

823 ] 

824 ] 

825 

826 caption_1d_histogram = PlotCaption("1d_histogram") 

827 posterior_name = \ 

828 lambda i: "{} ({})".format(i, descriptive_names[i]) if i in \ 

829 descriptive_names.keys() and descriptive_names[i] != "" else i 

830 captions = [ 

831 [ 

832 caption_1d_histogram.format(posterior_name("mass_1")), 

833 caption_1d_histogram.format(posterior_name("mass_2")), 

834 ], [ 

835 caption_1d_histogram.format(posterior_name("a_1")), 

836 caption_1d_histogram.format(posterior_name("a_2")), 

837 caption_1d_histogram.format(posterior_name("chi_eff")) 

838 ], [ 

839 caption_1d_histogram.format(posterior_name("iota")), 

840 PlotCaption("skymap"), PlotCaption("frequency_waveform"), 

841 caption_1d_histogram.format(posterior_name("luminosity_distance")) 

842 ] 

843 ] 

844 return image_contents, cli, captions 

845 

846 def default_categories(self): 

847 """Return the default categories 

848 """ 

849 categories = self.categories = { 

850 "masses": { 

851 "accept": ["mass"], 

852 "reject": ["source", "final", "torus"] 

853 }, 

854 "source": { 

855 "accept": ["source"], "reject": ["final", "torus"] 

856 }, 

857 "remnant": { 

858 "accept": ["final", "torus"], "reject": [] 

859 }, 

860 "inclination": { 

861 "accept": ["theta", "iota", "viewing"], "reject": [] 

862 }, 

863 "spins": { 

864 "accept": ["spin", "chi_p", "chi_eff", "a_1", "a_2", "precession"], 

865 "reject": ["lambda", "final", "gamma", "order"] 

866 }, 

867 "spin_angles": { 

868 "accept": ["phi", "tilt", "beta"], "reject": [] 

869 }, 

870 "tidal": { 

871 "accept": [ 

872 "lambda", "gamma_", "log_pressure", 

873 "spectral_decomposition_gamma_", "compactness_", 

874 "tidal_disruption" 

875 ], 

876 "reject": [] 

877 }, 

878 "location": { 

879 "accept": [ 

880 "ra", "dec", "psi", "luminosity_distance", "redshift", 

881 "comoving_distance" 

882 ], 

883 "reject": ["mass_ratio", "radiated", "ram", "ran", "rat", "time", "delay"] 

884 }, 

885 "timings": { 

886 "accept": ["time", "delay"], "reject": [] 

887 }, 

888 "SNR": { 

889 "accept": ["snr", "subdominant_multipoles"], "reject": [] 

890 }, 

891 "calibration": { 

892 "accept": ["spcal", "recalib", "frequency"], 

893 "reject": ["minimum", "tidal_disruption", "quasinormal"] 

894 }, 

895 "energy": { 

896 "accept": ["peak_luminosity", "radiated"], 

897 "reject": [] 

898 }, 

899 "others": { 

900 "accept": ["phase", "likelihood", "prior", "quasinormal"], 

901 "reject": ["spcal", "recalib"] 

902 } 

903 } 

904 return categories 

905 

906 def default_popular_options(self): 

907 """Return a list of popular options 

908 """ 

909 popular_options = [ 

910 "mass_1, mass_2", "luminosity_distance, theta_jn, ra, dec", 

911 "theta_jn, phi_12, phi_jl, tilt_1, tilt_2" 

912 ] 

913 return popular_options 

914 

915 def default_comparison_homepage_plots(self): 

916 """Return a list of default plots for the comparison homepage 

917 """ 

918 path = self.image_path["other"] 

919 base = os.path.join(path, "{}.png") 

920 contents = [ 

921 [ 

922 base.format("combined_skymap"), 

923 base.format("compare_time_domain_waveforms"), 

924 base.format("compare_waveforms") 

925 ] 

926 ] 

927 return contents 

928 

929 def default_corner_params(self): 

930 """Return a list of default corner parameters used by the corner 

931 plotting function 

932 """ 

933 return conf.gw_corner_parameters 

934 

935 def add_to_expert_pages(self, path, label): 

936 """Additional expert plots to add beyond the default. This returns a 

937 dictionary keyed by the parameter, with values providing the path 

938 to the additional plots you wish to add. The plots are a 2d list 

939 where each sublist represents a row in the table of images. 

940 

941 Parameters 

942 ---------- 

943 path: str 

944 path to the image directory 

945 label: str 

946 label of the plot you wish to add 

947 """ 

948 mydict = super(_WebpageGeneration, self).add_to_expert_pages( 

949 path, label 

950 ) 

951 contour_base = path + "{}_2d_contour_{}_log_likelihood.png" 

952 mydict.update({ 

953 "network_precessing_snr": [ 

954 [ 

955 contour_base.format(label, "_b_bar"), 

956 contour_base.format(label, "_precessing_harmonics_overlap"), 

957 ] 

958 ] 

959 }) 

960 return mydict 

961 

962 @property 

963 def additional_1d_pages(self): 

964 """Additional 1d histogram pages beyond one for each parameter. You may, 

965 for instance, want a 1d histogram page which combines multiple 

966 parameters. This returns a dictionary, keyed by the new 1d histogram 

967 page, with values indicating the parameters you wish to include on this 

968 page. Only the 1d marginalized histograms are shown. 

969 """ 

970 return conf.additional_1d_pages