Coverage for pesummary/gw/webpage/main.py: 77.7%
391 statements
« prev ^ index » next coverage.py v7.4.4, created at 2025-11-05 13:38 +0000
« 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
3import os
4import numpy as np
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
12__author__ = ["Charlie Hoy <charlie.hoy@ligo.org>"]
15class CommandLineCaption(object):
16 """Class to handle generating the command line used to generate a plot and
17 a caption to describe the plot
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")
27 @staticmethod
28 def get_executable(executable):
29 """Return the path to an executable
31 Parameters
32 ----------
33 executable: str
34 the name of the executable you wish to find
35 """
36 from subprocess import check_output
38 path = check_output(["which", executable]).decode("utf-8").strip()
39 return path
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))
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
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
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 )
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 pastro_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, embright_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.pastro_probs = pastro_probs
112 self.embright_probs = embright_probs
113 if self.embright_probs is None:
114 self.embright_probs = {
115 label: None for label in self.pastro_probs.keys()
116 }
117 self.gracedb = gracedb
118 self.approximant = approximant
119 self.file_kwargs = file_kwargs
120 self.publication = publication
121 self.publication_kwargs = publication_kwargs
122 self.result_files = result_files
123 self.gwdata = gwdata
124 self.no_ligo_skymap = no_ligo_skymap
125 self.psd = psd
126 self.priors = priors
128 super(_WebpageGeneration, self).__init__(
129 webdir=webdir, samples=samples, labels=labels,
130 publication=publication, user=user, config=config,
131 same_parameters=same_parameters, base_url=base_url,
132 file_versions=file_versions, hdf5=hdf5, colors=colors,
133 custom_plotting=custom_plotting,
134 existing_labels=existing_labels, existing_config=existing_config,
135 existing_file_version=existing_file_version,
136 existing_injection_data=existing_injection_data,
137 existing_samples=existing_samples,
138 existing_metafile=existing_metafile,
139 existing_file_kwargs=existing_file_kwargs,
140 existing_weights=existing_weights,
141 add_to_existing=add_to_existing, notes=notes,
142 disable_comparison=disable_comparison,
143 disable_interactive=disable_interactive,
144 package_information=package_information, mcmc_samples=mcmc_samples,
145 external_hdf5_links=external_hdf5_links, key_data=key_data,
146 existing_plot=existing_plot, disable_expert=disable_expert,
147 analytic_priors=analytic_priors
148 )
149 if self.file_kwargs is None:
150 self.file_kwargs = {
151 label: {"sampler": {}, "meta_data": {}} for label in self.labels
152 }
153 if self.approximant is None:
154 self.approximant = {label: None for label in self.labels}
155 if self.result_files is None:
156 self.result_files = [None] * len(self.labels)
157 self.psd_path = {"other": os.path.join("..", "psds")}
158 self.calibration_path = {"other": os.path.join("..", "calibration")}
159 self.preliminary_pages = preliminary_pages
160 if not isinstance(self.preliminary_pages, dict):
161 if self.preliminary_pages:
162 self.preliminary_pages = {
163 label: True for label in self.labels
164 }
165 else:
166 self.preliminary_pages = {
167 label: False for label in self.labels
168 }
169 if all(value for value in self.preliminary_pages.values()):
170 self.all_pages_preliminary = True
171 if len(self.labels) > 1:
172 if any(value for value in self.preliminary_pages.values()):
173 self.preliminary_pages["Comparison"] = True
175 def categorize_parameters(self, parameters):
176 """Categorize the parameters into common headings
178 Parameters
179 ----------
180 parameters: list
181 list of parameters that you would like to sort
182 """
183 return super(_WebpageGeneration, self).categorize_parameters(
184 parameters, starting_letter=False
185 )
187 def categorize_2d_parameters(self, samples):
188 """Categorize the default 2d parameters and decide the common
189 headings
191 Parameters
192 ----------
193 parameters: list
194 list of parameters that you would like to sort
195 """
196 sort = {}
197 for params in conf.gw_2d_plots:
198 if not all(p in samples.keys() for p in params):
199 continue
200 if not all(len(np.unique(samples[p])) > 1 for p in params):
201 continue
202 headings = self.categorize_parameters(params)
203 for hh in headings[::-1]:
204 if len(hh[1]) and hh[0] not in ["others"]:
205 heading = hh[0]
206 break
207 elif len(hh[1]) and "geocent_time" in params:
208 heading = "timings"
209 break
210 if heading not in sort.keys():
211 sort[heading] = []
212 sort[heading] += params
213 return sort
215 def _kde_from_same_samples(self, param, samples):
216 """Generate KDEs for a set of samples
218 Parameters
219 ----------
220 param: str
221 The parameter that the samples belong to
222 samples: list
223 list of samples for each result file
224 """
225 from pesummary.utils.bounded_1d_kde import ReflectionBoundedKDE
226 from pesummary.gw.plots.plot import _return_bounds
228 xlow, xhigh = _return_bounds(param, samples, comparison=True)
229 return super(_WebpageGeneration, self)._kde_from_same_samples(
230 param, samples, kde=ReflectionBoundedKDE, xlow=xlow, xhigh=xhigh
231 )
233 def make_navbar_for_homepage(self):
234 """Make a navbar for the homepage
235 """
236 if not hasattr(self, "make_pastro"):
237 if self.pastro_probs is not None:
238 self.make_pastro = {
239 label: self.pastro_probs[label] is not None and not any(
240 _ is None for _ in self.pastro_probs[label]["default"].values()
241 ) for label in self.pastro_probs.keys()
242 }
243 else:
244 self.make_pastro = {
245 label: False for label in self.pastro_probs.keys()
246 }
247 links = super(_WebpageGeneration, self).make_navbar_for_homepage()
248 if self.gwdata is not None:
249 links.append(["Detchar", [i for i in self.gwdata.keys()]])
250 return links
252 def make_navbar_for_result_page(self):
253 """Make a navbar for the result page homepage
254 """
255 links = super(_WebpageGeneration, self).make_navbar_for_result_page()
256 for num, label in enumerate(self.labels):
257 links[label].append(
258 ["2d Histograms", [
259 {"masses": label}, {"spins": label},
260 {"location": label}, {"timings": label}
261 ]]
262 )
263 if self.make_pastro[label]:
264 links[label].append({"Classification": label})
265 return links
267 def generate_webpages(self):
268 """Generate all webpages for all result files passed
269 """
270 super(_WebpageGeneration, self).generate_webpages()
271 self.make_2d_histogram_pages()
272 if self.publication:
273 self.make_publication_pages()
274 if self.gwdata is not None:
275 self.make_detector_pages()
276 self.make_classification_pages()
278 def _make_home_pages(self, pages):
279 """Make the home pages
281 Parameters
282 ----------
283 pages: list
284 list of pages that you wish to create
285 """
286 title = None if self.gracedb is None else (
287 "Parameter Estimation Summary Pages for {}".format(self.gracedb)
288 )
289 banner = "Summary" if self.gracedb is None else (
290 "Summary for {}".format(self.gracedb)
291 )
292 html_file = self.setup_page("home", self.navbar["home"], title=title)
293 html_file.make_banner(approximant=banner, key="Summary")
294 path = self.image_path["home"]
296 if self.make_comparison:
297 if not all(self.approximant[i] is not None for i in self.labels):
298 image_contents = []
299 _plot = os.path.join(path, "compare_time_domain_waveforms.png")
300 if os.path.isfile(_plot):
301 image_contents.append(_plot)
302 image_contents = [image_contents]
303 html_file = self.make_modal_carousel(
304 html_file, image_contents, unique_id=True
305 )
307 for i in self.labels:
308 html_file.make_banner(approximant=i, key=i)
309 image_contents, captions = [], []
310 basic_string = os.path.join(self.webdir, "plots", "{}.png")
311 relative_path = os.path.join(path, "{}.png")
312 if os.path.isfile(basic_string.format("%s_strain" % (i))):
313 image_contents.append(relative_path.format("%s_strain" % (i)))
314 captions.append(PlotCaption("strain"))
315 if os.path.isfile(basic_string.format("%s_psd_plot" % (i))):
316 image_contents.append(relative_path.format("%s_psd_plot" % (i)))
317 captions.append(PlotCaption("psd"))
318 if os.path.isfile(
319 basic_string.format("%s_waveform_time_domain" % (i))
320 ):
321 image_contents.append(
322 relative_path.format("%s_waveform_time_domain" % (i))
323 )
324 captions.append(PlotCaption("time_waveform"))
325 if os.path.isfile(
326 basic_string.format("%s_calibration_plot" % (i))
327 ):
328 image_contents.append(
329 relative_path.format("%s_calibration_plot" % (i))
330 )
331 captions.append(PlotCaption("calibration"))
332 image_contents = [image_contents]
333 html_file = self.make_modal_carousel(
334 html_file, image_contents, unique_id=True, extra_div=True,
335 captions=[captions]
336 )
338 for _key in ["sampler", "meta_data"]:
339 if _key == "sampler":
340 html_file.make_banner(
341 approximant="Sampler kwargs", key="sampler_kwargs",
342 _style="font-size: 26px;"
343 )
344 else:
345 html_file.make_banner(
346 approximant="Meta data", key="meta_data",
347 _style="font-size: 26px;"
348 )
349 _style = "margin-top:3em; margin-bottom:5em; max-width:1400px"
350 _class = "row justify-content-center"
351 html_file.make_container(style=_style)
352 html_file.make_div(4, _class=_class, _style=None)
354 base_label = self.labels[0]
355 total_keys = list(self.file_kwargs[base_label][_key].keys())
356 if len(self.labels) > 1:
357 for _label in self.labels[1:]:
358 total_keys += [
359 key for key in self.file_kwargs[_label][_key].keys()
360 if key not in total_keys
361 ]
362 _headings = ["label"] + total_keys
363 table_contents = [
364 [i] + [
365 self.file_kwargs[i][_key][key] if key in
366 self.file_kwargs[i][_key].keys() else "-" for key in
367 total_keys
368 ] for i in self.labels
369 ]
370 html_file.make_table(
371 headings=_headings, format="table-hover", heading_span=1,
372 contents=table_contents, accordian=False
373 )
374 html_file.end_div(4)
375 html_file.end_container()
376 html_file.export("{}.csv".format(_key))
378 html_file.make_footer(user=self.user, rundir=self.webdir)
379 html_file.close()
380 super(_WebpageGeneration, self)._make_home_pages(pages, make_home=False)
382 def make_2d_histogram_pages(self):
383 """Wrapper function for _make_2d_histogram pages
384 """
385 pages = [
386 "{}_{}_{}".format(i, i, j) for i in self.labels for j in
387 self.categorize_2d_parameters(self.samples[i]).keys()
388 ]
389 self.create_blank_html_pages(pages)
390 self._make_2d_histogram_pages(pages)
392 def _make_2d_histogram_pages(self, pages):
393 """Make the 2d histogram pages
394 """
395 for num, i in enumerate(self.labels):
396 for j, k in self.categorize_2d_parameters(self.samples[i]).items():
397 html_file = self.setup_page(
398 "{}_{}".format(i, j), self.navbar["result_page"][i],
399 i, title="{} Posterior PDFs describing the {}".format(i, j),
400 approximant=i, background_colour=self.colors[num],
401 histogram_download=False, toggle=self.expert_plots
402 )
403 html_file.make_banner(approximant=i, key=i)
404 path = self.image_path["other"]
405 contents = [
406 path + "{}_2d_posterior_{}_{}.png".format(i, k[l], k[l+1])
407 for l in range(0, len(k), 2)
408 ]
409 if len(contents) < 2:
410 autoscale = False
411 else:
412 autoscale = True
413 image_contents = [
414 contents[l:2 + l] for l in range(0, len(contents), 2)
415 ]
416 html_file.make_table_of_images(
417 contents=image_contents, code="changeimage",
418 mcmc_samples=False, autoscale=autoscale
419 )
420 key_data = self.key_data
421 contents = []
422 headings = [" "] + self.key_data_headings.copy()
423 _injection = False
424 rows = []
425 for param in np.unique(k):
426 _row = [param]
427 _row += self.key_data_table[i][param]
428 rows.append(_row)
429 _style = "margin-top:3em; margin-bottom:5em; max-width:1400px"
430 _class = "row justify-content-center"
431 html_file.make_container(style=_style)
432 html_file.make_div(4, _class=_class, _style=None)
433 html_file.make_table(
434 headings=headings, contents=rows, heading_span=1,
435 accordian=False, format="table-hover"
436 )
437 html_file.end_div(4)
438 html_file.end_container()
439 html_file.export("summary_information_{}.csv".format(i))
440 html_file.make_footer(user=self.user, rundir=self.webdir)
441 html_file.close()
443 def make_publication_pages(self):
444 """Wrapper function for _make_publication_pages()
445 """
446 pages = ["Publication"]
447 self.create_blank_html_pages(pages)
448 self._make_publication_pages(pages)
450 def _make_publication_pages(self, pages):
451 """Make the publication pages
453 Parameters
454 ----------
455 pages: list
456 list of pages that you wish to create
457 """
458 from glob import glob
460 executable = self.get_executable("summarypublication")
461 general_cli = "%s --webdir %s --samples %s --labels %s --plot {}" % (
462 executable, os.path.join(self.webdir, "plots", "publication"),
463 " ".join(self.result_files), " ".join(self.labels)
464 )
465 if self.publication_kwargs != {}:
466 general_cli += "--publication_kwargs %s" % (
467 " ".join(
468 [
469 "{}:{}".format(key, value) for key, value in
470 self.publication_kwargs.items()
471 ]
472 )
473 )
474 html_file = self.setup_page(
475 "Publication", self.navbar["home"], title="Publication Plots"
476 )
477 html_file.make_banner(approximant="Publication", key="Publication")
478 path = self.image_path["other"]
479 pub_plots = glob(
480 os.path.join(self.webdir, "plots", "publication", "*.png")
481 )
482 for num, i in enumerate(pub_plots):
483 shortened_path = i.split("/plots/")[-1]
484 pub_plots[num] = path + shortened_path
485 cli = []
486 cap = []
487 posterior_name = \
488 lambda i: "{} ({})".format(i, descriptive_names[i]) if i in \
489 descriptive_names.keys() and descriptive_names[i] != "" else i
490 for i in pub_plots:
491 filename = i.split("/")[-1]
492 if "violin_plot" in filename:
493 parameter = filename.split("violin_plot_")[-1].split(".png")[0]
494 cli.append(
495 general_cli.format("violin") + " --parameters %s" % (
496 parameter
497 )
498 )
499 cap.append(
500 PlotCaption("violin").format(posterior_name(parameter))
501 )
502 elif "spin_disk" in filename:
503 cli.append(general_cli.format("spin_disk"))
504 cap.append(PlotCaption("spin_disk"))
505 elif "2d_contour" in filename:
506 parameters = filename.split("2d_contour_plot_")[-1].split(".png")[0]
507 cli.append(
508 general_cli.format("2d_contour") + " --parameters %s" % (
509 parameters.replace("_and_", " ")
510 )
511 )
512 pp = parameters.split("_and_")
513 cap.append(
514 PlotCaption("2d_contour").format(
515 posterior_name(pp[0]), posterior_name(pp[1])
516 )
517 )
518 image_contents = [
519 pub_plots[i:3 + i] for i in range(0, len(pub_plots), 3)
520 ]
521 command_lines = [
522 cli[i:3 + i] for i in range(0, len(cli), 3)
523 ]
524 captions = [cap[i:3 + i] for i in range(0, len(cap), 3)]
525 html_file = self.make_modal_carousel(
526 html_file, image_contents, cli=command_lines, captions=captions
527 )
528 html_file.make_footer(user=self.user, rundir=self.webdir)
529 html_file.close()
531 def make_detector_pages(self):
532 """Wrapper function for _make_publication_pages()
533 """
534 pages = [i for i in self.gwdata.keys()]
535 self.create_blank_html_pages(pages)
536 self._make_detector_pages(pages)
538 def _make_detector_pages(self, pages):
539 """Make the detector characterisation pages
541 Parameters
542 ----------
543 pages: list
544 list of pages that you wish to create
545 """
546 from pesummary.utils.utils import (
547 determine_gps_time_and_window, command_line_dict
548 )
549 from astropy.time import Time
551 executable = self.get_executable("summarydetchar")
552 try:
553 command_line = command_line_dict()
554 except SystemExit:
555 command_line = {"gwdata": {}}
556 if isinstance(command_line["gwdata"], dict):
557 gwdata_command_line = [
558 "{}:{}".format(key, val) for key, val in
559 command_line["gwdata"].items()
560 ]
561 else:
562 gwdata_command_line = command_line["gwdata"]
563 if gwdata_command_line is None:
564 gwdata_command_line = []
565 general_cli = "%s --webdir %s --gwdata %s --plot {}{}" % (
566 executable, os.path.join(self.webdir, "plots"),
567 " ".join(gwdata_command_line)
568 )
569 path = self.image_path["other"]
570 base = os.path.join(path, "{}_{}.png")
571 ADD_DETCHAR_LINK = True
572 try:
573 maxL_samples = {
574 i: {
575 "geocent_time": self.key_data[i]["geocent_time"]["maxL"]
576 } for i in self.labels
577 }
578 except KeyError:
579 # trying a different name for time
580 try:
581 maxL_samples = {
582 i: {
583 "geocent_time": self.key_data[i][
584 "marginalized_geocent_time"
585 ]["maxL"]
586 } for i in self.labels
587 }
588 except KeyError:
589 logger.warning(
590 "Failed to find a time parameter to link to detchar/"
591 "summary pages. Not adding link to webpages."
592 )
593 ADD_DETCHAR_LINK = False
594 if ADD_DETCHAR_LINK:
595 gps_time, window = determine_gps_time_and_window(maxL_samples, self.labels)
596 t = Time(gps_time, format='gps')
597 t = Time(t, format='datetime')
598 link = (
599 "https://ldas-jobs.ligo-wa.caltech.edu/~detchar/summary/day"
600 "/{}{}{}/".format(
601 t.value.year,
602 "0{}".format(t.value.month) if t.value.month < 10 else t.value.month,
603 "0{}".format(t.value.day) if t.value.day < 10 else t.value.day
604 )
605 )
606 else:
607 link = None
608 gps_time, window = None, None
609 for det in self.gwdata.keys():
610 html_file = self.setup_page(
611 det, self.navbar["home"], title="{} Detchar".format(det)
612 )
613 html_file.make_banner(approximant=det, key="detchar", link=link)
614 image_contents = [
615 [base.format("spectrogram", det), base.format("omegascan", det)]
616 ]
617 command_lines = [
618 [
619 general_cli.format("spectrogram", ""),
620 general_cli.format(
621 "omegascan", "--gps %s --vmin 0 --vmax 25 --window %s" % (
622 gps_time, window
623 )
624 )
625 ]
626 ]
627 html_file = self.make_modal_carousel(
628 html_file, image_contents, cli=command_lines, autoscale=True,
629 )
630 html_file.make_footer(user=self.user, rundir=self.webdir)
631 html_file.close()
633 def make_classification_pages(self):
634 """Wrapper function for _make_publication_pages()
635 """
636 self._make_classification_pages([])
638 def _make_classification_pages(self, pages):
639 """Make the classification pages
641 Parameters
642 ----------
643 pages: list
644 list of pages that you wish to create
645 """
646 executable = self.get_executable("summaryclassification")
647 general_cli = "%s --samples {}" % (executable)
648 for num, label in enumerate(self.labels):
649 make_embright = self.embright_probs[label] is None or not any(
650 _ is None for _ in self.embright_probs[label]["default"].values()
651 )
652 if not self.make_pastro[label]:
653 continue
654 page = "{}_{}_Classification".format(label, label)
655 self.create_blank_html_pages([page])
656 html_file = self.setup_page(
657 "{}_Classification".format(label),
658 self.navbar["result_page"][label], label,
659 title="{} Classification".format(label),
660 background_colour=self.colors[num], approximant=label
661 )
662 html_file.make_banner(approximant=label, key="classification")
663 if self.make_pastro[label]:
664 html_file.make_container()
665 _class = "row justify-content-center"
666 html_file.make_div(4, _class=_class, _style=None)
667 keys = list(self.pastro_probs[label]["default"].keys())
668 table_contents = [
669 ["{} prior".format(i)] + [
670 self.pastro_probs[label][i][j] for j in keys
671 ] for i in ["default"]
672 ]
673 if self.embright_probs[label] is not None and make_embright:
674 keys += ["HasNS"]
675 keys += ["HasRemnant"]
676 keys += ["HasMassGap"]
677 table_contents[0].append(
678 self.embright_probs[label]["default"]["HasNS"]
679 )
680 table_contents[0].append(
681 self.embright_probs[label]["default"]["HasRemnant"]
682 )
683 table_contents[0].append(
684 self.embright_probs[label]["default"]["HasMassGap"]
685 )
686 html_file.make_table(
687 headings=[" "] + keys, contents=table_contents,
688 heading_span=1, accordian=False
689 )
690 html_file.make_cli_button(
691 general_cli.format(self.result_files[num])
692 )
693 html_file.export(
694 "classification_{}.csv".format(label),
695 margin_top="-1.5em", margin_bottom="0.5em", json=True
696 )
697 html_file.end_div(4)
698 html_file.end_container()
699 path = self.image_path["other"]
700 image_contents = [
701 [
702 os.path.join(path, "%s.pesummary.p_astro.png" % (label)),
703 os.path.join(path, "%s.pesummary.em_bright.png" % (label))
704 ]
705 ]
706 mass_plot = os.path.join(
707 self.webdir, "plots",
708 f"{label}_2d_posterior_mass_1_source_mass_2_source.png"
709 )
710 if os.path.isfile(mass_plot):
711 mass_plot = os.path.join(
712 path,
713 f"{label}_2d_posterior_mass_1_source_mass_2_source.png"
714 )
715 image_contents[0].append(mass_plot)
716 base = (
717 "%s --webdir %s --labels %s --plot {} --prior {}" % (
718 general_cli.format(self.result_files[num]),
719 os.path.join(self.webdir, "plots"), label
720 )
721 )
722 command_lines = [
723 [
724 base.format("bar", "default"),
725 base.format("bar", "population"),
726 None
727 ]
728 ]
729 captions = [
730 [
731 PlotCaption("pastro_classification_bar"),
732 PlotCaption("embright_classification_bar"),
733 None
734 ]
735 ]
736 html_file = self.make_modal_carousel(
737 html_file, image_contents, cli=command_lines, autoscale=False,
738 captions=captions
739 )
740 html_file.make_footer(user=self.user, rundir=self.webdir)
741 html_file.close()
743 def _make_entry_in_downloads_table(self, html_file, label, num, base_string):
744 """Make a label specific entry into the downloads table
746 Parameters
747 ----------
748 label: str
749 the label you wish to add to the downloads table
750 base_string: str
751 the download string
752 """
753 table = super(_WebpageGeneration, self)._make_entry_in_downloads_table(
754 html_file, label, num, base_string
755 )
756 if not self.no_ligo_skymap:
757 table.append(
758 [
759 base_string.format(
760 "Fits file containing skymap for this analysis",
761 self.results_path["other"] + "%s_skymap.fits" % (label)
762 )
763 ]
764 )
765 if self.make_pastro[label]:
766 table.append(
767 [
768 base_string.format(
769 "PAstro json file containing source classification "
770 "probabilities",
771 self.results_path["other"] + label + ".pesummary.p_astro.json"
772 )
773 ]
774 )
775 table.append(
776 [
777 base_string.format(
778 "PAstro bar plot showing source classification "
779 "probabilities",
780 self.image_path["other"] + label + ".pesummary.p_astro.png"
781 )
782 ]
783 )
784 if self.psd is not None and self.psd != {} and label in self.psd.keys():
785 for ifo in self.psd[label].keys():
786 if len(self.psd[label][ifo]):
787 table.append(
788 [
789 base_string.format(
790 "%s psd file used for this analysis" % (ifo),
791 os.path.join(
792 self.psd_path["other"],
793 "%s_%s_psd.dat" % (label, ifo)
794 )
795 )
796 ]
797 )
798 if self.priors is not None and "calibration" in self.priors.keys():
799 if label in self.priors["calibration"].keys():
800 for ifo in self.priors["calibration"][label].keys():
801 if len(self.priors["calibration"][label][ifo]):
802 table.append(
803 [
804 base_string.format(
805 "%s calibration envelope file used for "
806 "this analysis" % (ifo), os.path.join(
807 self.calibration_path["other"],
808 "%s_%s_cal.txt" % (label, ifo)
809 )
810 )
811 ]
812 )
813 return table
815 def default_images_for_result_page(self, label):
816 """Return the default images that will be displayed on the result page
817 """
818 path = self.image_path["other"]
819 base_string = path + "%s_{}.png" % (label)
820 image_contents = [
821 [
822 base_string.format("1d_posterior_mass_1"),
823 base_string.format("1d_posterior_mass_2"),
824 ], [
825 base_string.format("1d_posterior_a_1"),
826 base_string.format("1d_posterior_a_2"),
827 base_string.format("1d_posterior_chi_eff")
828 ], [
829 base_string.format("1d_posterior_iota"),
830 base_string.format("skymap"),
831 base_string.format("waveform"),
832 base_string.format("1d_posterior_luminosity_distance"),
833 ]
834 ]
835 executable = self.get_executable("summaryplots")
836 general_cli = (
837 "%s --webdir %s --samples %s --burnin %s --plot {} {} "
838 "--labels %s" % (
839 executable, os.path.join(self.webdir, "plots"),
840 self.result_files[self.labels.index(label)], conf.burnin, label
841 )
842 )
843 cli = [
844 [
845 general_cli.format("1d_histogram", "--parameter mass_1"),
846 general_cli.format("1d_histgram", "--parameter mass_2"),
847 ], [
848 general_cli.format("1d_histogram", "--parameter a_1"),
849 general_cli.format("1d_histogram", "--parameter a_2"),
850 general_cli.format("1d_histogram", "--parameter chi_eff")
851 ], [
852 general_cli.format("1d_histogram", "--parameter iota"),
853 general_cli.format("skymap", ""),
854 general_cli.format("waveform", ""),
855 general_cli.format(
856 "1d_histogram", "--parameter luminosity_distance"
857 ),
858 ]
859 ]
861 caption_1d_histogram = PlotCaption("1d_histogram")
862 posterior_name = \
863 lambda i: "{} ({})".format(i, descriptive_names[i]) if i in \
864 descriptive_names.keys() and descriptive_names[i] != "" else i
865 captions = [
866 [
867 caption_1d_histogram.format(posterior_name("mass_1")),
868 caption_1d_histogram.format(posterior_name("mass_2")),
869 ], [
870 caption_1d_histogram.format(posterior_name("a_1")),
871 caption_1d_histogram.format(posterior_name("a_2")),
872 caption_1d_histogram.format(posterior_name("chi_eff"))
873 ], [
874 caption_1d_histogram.format(posterior_name("iota")),
875 PlotCaption("skymap"), PlotCaption("frequency_waveform"),
876 caption_1d_histogram.format(posterior_name("luminosity_distance"))
877 ]
878 ]
879 return image_contents, cli, captions
881 def default_categories(self):
882 """Return the default categories
883 """
884 categories = self.categories = {
885 "masses": {
886 "accept": ["mass"],
887 "reject": ["source", "final", "torus"]
888 },
889 "source": {
890 "accept": ["source"], "reject": ["final", "torus"]
891 },
892 "remnant": {
893 "accept": ["final", "torus"], "reject": []
894 },
895 "inclination": {
896 "accept": ["theta", "iota", "viewing"], "reject": []
897 },
898 "spins": {
899 "accept": ["spin", "chi_p", "chi_eff", "a_1", "a_2", "precession"],
900 "reject": ["lambda", "final", "gamma", "order"]
901 },
902 "spin_angles": {
903 "accept": ["phi", "tilt", "beta"], "reject": []
904 },
905 "tidal": {
906 "accept": [
907 "lambda", "gamma_", "log_pressure",
908 "spectral_decomposition_gamma_", "compactness_",
909 "tidal_disruption"
910 ],
911 "reject": []
912 },
913 "location": {
914 "accept": [
915 "ra", "dec", "psi", "luminosity_distance", "redshift",
916 "comoving_distance"
917 ],
918 "reject": ["mass_ratio", "radiated", "ram", "ran", "rat", "time", "delay"]
919 },
920 "timings": {
921 "accept": ["time", "delay"], "reject": []
922 },
923 "SNR": {
924 "accept": ["snr", "subdominant_multipoles"], "reject": []
925 },
926 "calibration": {
927 "accept": ["spcal", "recalib", "frequency"],
928 "reject": ["minimum", "tidal_disruption", "quasinormal"]
929 },
930 "energy": {
931 "accept": ["peak_luminosity", "radiated"],
932 "reject": []
933 },
934 "others": {
935 "accept": ["phase", "likelihood", "prior", "quasinormal"],
936 "reject": ["spcal", "recalib"]
937 }
938 }
939 return categories
941 def default_popular_options(self):
942 """Return a list of popular options
943 """
944 popular_options = [
945 "mass_1, mass_2", "luminosity_distance, theta_jn, ra, dec",
946 "theta_jn, phi_12, phi_jl, tilt_1, tilt_2"
947 ]
948 return popular_options
950 def default_comparison_homepage_plots(self):
951 """Return a list of default plots for the comparison homepage
952 """
953 path = self.image_path["other"]
954 base = os.path.join(path, "{}.png")
955 contents = [
956 [
957 base.format("combined_skymap"),
958 base.format("compare_time_domain_waveforms"),
959 base.format("compare_waveforms")
960 ]
961 ]
962 return contents
964 def default_corner_params(self):
965 """Return a list of default corner parameters used by the corner
966 plotting function
967 """
968 return conf.gw_corner_parameters
970 def add_to_expert_pages(self, path, label):
971 """Additional expert plots to add beyond the default. This returns a
972 dictionary keyed by the parameter, with values providing the path
973 to the additional plots you wish to add. The plots are a 2d list
974 where each sublist represents a row in the table of images.
976 Parameters
977 ----------
978 path: str
979 path to the image directory
980 label: str
981 label of the plot you wish to add
982 """
983 mydict = super(_WebpageGeneration, self).add_to_expert_pages(
984 path, label
985 )
986 contour_base = path + "{}_2d_contour_{}_log_likelihood.png"
987 mydict.update({
988 "network_precessing_snr": [
989 [
990 contour_base.format(label, "_b_bar"),
991 contour_base.format(label, "_precessing_harmonics_overlap"),
992 ]
993 ]
994 })
995 return mydict
997 @property
998 def additional_1d_pages(self):
999 """Additional 1d histogram pages beyond one for each parameter. You may,
1000 for instance, want a 1d histogram page which combines multiple
1001 parameters. This returns a dictionary, keyed by the new 1d histogram
1002 page, with values indicating the parameters you wish to include on this
1003 page. Only the 1d marginalized histograms are shown.
1004 """
1005 return conf.additional_1d_pages