36from matplotlib
import figure
37from matplotlib.backends.backend_agg
import FigureCanvasAgg
as FigureCanvas
38from matplotlib
import ticker
40from ligo.lw
import lsctables
42from gstlal.plots
import util
as plotutil
43from gstlal.psd
import HorizonDistance
46def summarize_coinc_xmldoc(coinc_xmldoc):
47 coinc_event, = lsctables.CoincTable.get_table(coinc_xmldoc)
48 coinc_inspiral, = lsctables.CoincInspiralTable.get_table(coinc_xmldoc)
49 offset_vector = lsctables.TimeSlideTable.get_table(coinc_xmldoc).as_dict()[coinc_event.time_slide_id]
if coinc_event.time_slide_id
is not None else None
52 sngl_inspirals = dict((row.ifo, row)
for row
in lsctables.SnglInspiralTable.get_table(coinc_xmldoc))
54 mass1 = list(sngl_inspirals.values())[0].mass1
55 mass2 = list(sngl_inspirals.values())[0].mass2
57 mass1, mass2 = mass2, mass1
58 end_time = coinc_inspiral.end
59 on_instruments = coinc_inspiral.ifos
60 logging.info(
"%g Msun -- %g Msun event in %s at %.2f GPS" % (mass1, mass2,
", ".join(sorted(sngl_inspirals)), float(end_time)))
62 return sngl_inspirals, mass1, mass2, end_time, on_instruments
65def axes_plot_cummulative_snr(axes, psds, coinc_xmldoc):
66 sngl_inspirals, mass1, mass2, end_time, on_instruments = summarize_coinc_xmldoc(coinc_xmldoc)
68 axes.grid(which =
"both", linestyle =
"-", linewidth = 0.2)
71 for instrument, sngl_inspiral
in sngl_inspirals.items():
72 logging.info(
"found %s event with SNR %g" % (instrument, sngl_inspirals[instrument].snr))
74 if instrument
not in psds:
75 logging.info(
"no PSD for %s" % instrument)
77 psd = psds[instrument]
79 logging.info(
"no PSD for %s" % instrument)
81 psd_data = psd.data.data
82 f = psd.f0 + numpy.arange(len(psd_data)) * psd.deltaF
83 logging.info(
"found PSD for %s spanning [%g Hz, %g Hz]" % (instrument, f[0], f[-1]))
89 horizon_distance = HorizonDistance(10., 0.9 * f[-1], psd.deltaF, mass1, mass2)
93 inspiral_spectrum_x, inspiral_spectrum_y = horizon_distance(psd, sngl_inspiral.snr)[1]
94 lo = int(round((inspiral_spectrum_x[0] - psd.f0) / psd.deltaF))
95 hi = int(round((inspiral_spectrum_x[-1] - psd.f0) / psd.deltaF)) + 1
97 psd_data = psd_data[lo:hi]
100 snr2 = (inspiral_spectrum_y / psd_data).cumsum() * psd.deltaF
101 axes.semilogx(f, snr2**.5, color = plotutil.colour_from_instruments([instrument]), alpha = 0.8, linestyle =
"-", label =
"%s SNR = %.3g" % (instrument, sngl_inspiral.snr))
103 axes.set_ylim([0., axes.get_ylim()[1]])
105 axes.set_title(
r"Cumulative SNRs for $%.3g\,\mathrm{M}_{\odot}$--$%.3g\,\mathrm{M}_{\odot}$ Merger Candidate at %.2f GPS" % (mass1, mass2, float(end_time)))
106 axes.set_xlabel(
r"Frequency (Hz)")
107 axes.set_ylabel(
r"Cumulative SNR")
108 axes.legend(loc =
"upper left")
111def latex_horizon_distance(Mpc):
114 return "%s Gpc" % plotutil.latexnumber(
"%.4g" % (Mpc * 1e-3))
117 return "%s Mpc" % plotutil.latexnumber(
"%.4g" % Mpc)
120 return "%s kpc" % plotutil.latexnumber(
"%.4g" % (Mpc * 1e3))
123 return "%s pc" % plotutil.latexnumber(
"%.4g" % (Mpc * 1e6))
126def axes_plot_psds(axes, psds, coinc_xmldoc = None):
128 Places a PSD plot into a matplotlib Axes object.
130 @param axes An Axes object into which the plot will be placed.
132 @param psds A dictionary of PSDs as REAL8FrequencySeries keyed by
135 @param coinc_xmldoc An XML document containing a single event with all
136 of the metadata as would be uploaded to gracedb. This is optional.
139 if coinc_xmldoc
is not None:
140 sngl_inspirals, mass1, mass2, end_time, on_instruments = summarize_coinc_xmldoc(coinc_xmldoc)
145 mass1, mass2, end_time = 1.4, 1.4,
None
146 on_instruments = set(psds)
148 axes.grid(which =
"both", linestyle =
"-", linewidth = 0.2)
151 min_psds, max_psds = [], []
152 min_fs, max_fs = [], []
153 for instrument, psd
in sorted(psds.items()):
156 psd_data = psd.data.data
157 f = psd.f0 + numpy.arange(len(psd_data)) * psd.deltaF
158 logging.info(
"found PSD for %s spanning [%g Hz, %g Hz]" % (instrument, f[0], f[-1]))
165 horizon_distance = HorizonDistance(10., 0.9 * f[-1], psd.deltaF, mass1, mass2)
166 if instrument
in on_instruments:
169 label =
"%s (%s Horizon)" % (instrument, latex_horizon_distance(horizon_distance(psd, 8.)[0]))
173 label =
"%s (Off, Last Seen With %s Horizon)" % (instrument, latex_horizon_distance(horizon_distance(psd, 8.)[0]))
174 axes.loglog(f, psd_data, color = plotutil.colour_from_instruments([instrument]), alpha = alpha, linestyle = linestyle, label = label)
175 if instrument
in sngl_inspirals:
176 logging.info(
"found %s event with SNR %g" % (instrument, sngl_inspirals[instrument].snr))
177 inspiral_spectrum = horizon_distance(psd, sngl_inspirals[instrument].snr)[1]
178 axes.loglog(inspiral_spectrum[0], inspiral_spectrum[1], color = plotutil.colour_from_instruments([instrument]), dashes = (5, 2), alpha = 0.8, label =
"SNR = %.3g" % sngl_inspirals[instrument].snr)
180 min_psds.append(psd_data[int((10.0 - psd.f0) / psd.deltaF) : int((900 - psd.f0) / psd.deltaF)].min())
182 max_psds.append(psd_data[int((1.0 - psd.f0) / psd.deltaF) : int((900 - psd.f0) / psd.deltaF)].max())
185 axes.set_xlim((6.0, max(max_fs)))
187 axes.set_xlim((6.0, 3000.0))
189 axes.set_ylim((10.**math.floor(math.log10(min(min_psds) / 3.)), 10.**math.ceil(math.log10(max(max_psds)))))
192 axes.yaxis.set_major_locator(ticker.LogLocator(10., subs = (1.0,)))
193 axes.yaxis.set_minor_locator(ticker.LogLocator(10., subs = (0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9)))
195 title =
r"Strain Noise Spectral Density for $%.3g\,\mathrm{M}_{\odot}$--$%.3g\,\mathrm{M}_{\odot}$ Merger Candidate" % (mass1, mass2)
196 if end_time
is not None:
197 title +=
r" at %.2f GPS" % float(end_time)
198 axes.set_title(title)
199 axes.set_xlabel(
r"Frequency (Hz)")
200 axes.set_ylabel(
r"Spectral Density ($\mathrm{strain}^2 / \mathrm{Hz}$)")
201 axes.legend(loc =
"upper right")
204def plot_psds(psds, coinc_xmldoc = None, plot_width = 640):
206 Produces a matplotlib figure of PSDs.
208 @param psds A dictionary of PSDs as REAL8FrequencySeries keyed by
211 @param coinc_xmldoc An XML document containing a single event with all
212 of the metadata as would be uploaded to gracedb. This is optional.
214 @param plot_width How wide to make the figure object in pixels
215 (ignored if axes is provided).
217 fig = figure.Figure()
219 fig.set_size_inches(plot_width / float(fig.get_dpi()), int(round(plot_width / plotutil.golden_ratio)) / float(fig.get_dpi()))
220 axes_plot_psds(fig.gca(), psds, coinc_xmldoc = coinc_xmldoc)
221 fig.tight_layout(pad = .8)
225def plot_cumulative_snrs(psds, coinc_xmldoc, plot_width = 640):
227 Produces a matplotlib figure of cumulative SNRs.
229 @param psds A dictionary of PSDs as REAL8FrequencySeries keyed by
232 @param coinc_xmldoc An XML document containing a single event with all
233 of the metadata as would be uploaded to gracedb.
235 @param plot_width How wide to make the figure object in pixels
236 (ignored if axes is provided).
238 fig = figure.Figure()
240 fig.set_size_inches(plot_width / float(fig.get_dpi()), int(round(plot_width / plotutil.golden_ratio)) / float(fig.get_dpi()))
241 axes_plot_cummulative_snr(fig.gca(), psds, coinc_xmldoc)
242 fig.tight_layout(pad = .8)