Coverage for pesummary/gw/file/strain.py: 81.6%
103 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 pathlib
4from gwpy.timeseries import TimeSeries
5from pesummary.utils.utils import logger
6from pesummary.utils.dict import Dict
7from pesummary.utils.decorators import docstring_subfunction
9__author__ = ["Charlie Hoy <charlie.hoy@ligo.org>"]
12class StrainDataDict(Dict):
13 """Class to store multiple StrainData objects from different IFOs
15 Parameters
16 ----------
17 data: dict
18 dict keyed by IFO and values StrainData objects
20 Examples
21 --------
22 >>> from pesummary.gw.file.strain import StrainDataDict
23 >>> data = {
24 ... "H1": "./H-H1_LOSC_4_V2-1126257414-4096.gwf",
25 ... "L1": "./L-L1_LOSC_4_V2-1126257414-4096.gwf"
26 ... }
27 >>> channels = {"H1": "H1:LOSC-STRAIN", "L1": "L1:LOSC-STRAIN"}
28 >>> strain = StrainDataDict.read(data, channels=channels)
29 """
30 def __init__(self, *args):
31 super(StrainDataDict, self).__init__(
32 *args, value_class=StrainData, deconstruct_complex_columns=False
33 )
35 @classmethod
36 def read(cls, data, channels={}):
37 strain_data = {}
38 if not len(channels):
39 _data, _channels = {}, {}
40 for key in data.keys():
41 if ":" in key:
42 try:
43 IFO, _ = key.split(":")
44 _channels[IFO] = key
45 _data[IFO] = data[key]
46 logger.debug(
47 "Found ':' in '{}'. Assuming '{}' is the IFO and "
48 "'{}' is the channel".format(key, IFO, key)
49 )
50 except ValueError:
51 _data[key] = data[key]
52 _channels[key] = None
53 else:
54 _data = data
55 _channels = channels
56 if not len(_data):
57 raise ValueError("Please provide strain data to read")
58 if not all(IFO in _channels.keys() for IFO in _data.keys()):
59 raise ValueError("Please provide a channel for each IFO")
60 for IFO in _data.keys():
61 strain_data[IFO] = StrainData.read(_data[IFO], _channels[IFO], IFO=IFO)
62 return cls(strain_data)
64 @property
65 def detectors(self):
66 return list(self.keys())
69class StrainData(TimeSeries):
70 """Class to extend the gwpy.timeseries.TimeSeries plotting functions to
71 include the pesummary plots
73 Parameters
74 ----------
75 IFO: str, optional
76 IFO for which the strain data corresponds too. This is used to determine
77 the color on plots. Default 'H1'
79 Attributes
80 ----------
81 gwpy: gwpy.timeseries.TimeSeries
82 original gwpy TimeSeries object
83 IFO: str
84 IFO for which the strain data corresponds too
85 strain_dict: dict
86 dictionary of strain data
88 Methods
89 -------
90 plot:
91 Generate a plot based on the stored data
92 """
93 def __new__(cls, *args, IFO="H1", **kwargs):
94 new = super(StrainData, cls).__new__(cls, *args, **kwargs)
95 new.gwpy = TimeSeries(*args, **kwargs)
96 new.IFO = IFO
97 new.strain_dict = {new.IFO: new}
98 return new
100 def __array_finalize__(self, obj):
101 super(StrainData, self).__array_finalize__(obj)
102 try:
103 self.gwpy = getattr(obj, 'gwpy')
104 self.IFO = getattr(obj, 'IFO')
105 self.strain_dict = getattr(obj, 'strain_dict')
106 except (TypeError, AttributeError):
107 pass
109 @classmethod
110 def read(cls, *args, IFO="H1", **kwargs):
111 from pesummary.gw.file.formats.base_read import GWRead
112 if len(args) and isinstance(args[0], str):
113 if GWRead.extension_from_path(args[0]) == "pickle":
114 try:
115 from pesummary.gw.file.formats.bilby import Bilby
116 obj = Bilby._timeseries_from_bilby_pickle(args[0])
117 return StrainDataDict(obj)
118 except Exception as e:
119 pass
120 elif GWRead.extension_from_path(args[0]) == "lcf":
121 from glue.lal import Cache
122 with open(args[0], "r") as f:
123 data = Cache.fromfile(f)
124 args[0] = data
125 if len(args) and isinstance(args[0], pathlib.PosixPath):
126 args = list(args)
127 args[0] = str(args[0])
128 obj = super(StrainData, cls).read(*args, **kwargs)
129 return cls(obj, IFO=IFO)
131 @classmethod
132 def fetch_open_frame(cls, event, **kwargs):
133 """Fetch open frame files for a given event
135 Parameters
136 ----------
137 sampling_rate: int, optional
138 sampling rate of strain data you wish to download. Default 16384
139 format: str, optional
140 format of strain data you wish to download. Default "gwf"
141 duration: int, optional
142 duration of strain data you wish to download. Default 32
143 IFO: str, optional
144 detector strain data you wish to download. Default 'L1'
145 **kwargs: dict, optional
146 all additional kwargs passed to StrainData.read
147 """
148 from ..fetch import fetch_open_strain
149 return fetch_open_strain(event, read_file=True, **kwargs)
151 @classmethod
152 def fetch_open_data(cls, *args, **kwargs):
153 obj = super(StrainData, cls).fetch_open_data(*args, **kwargs)
154 return cls(obj, IFO=args[0])
156 @property
157 def plotting_map(self):
158 return {
159 "td": self._time_domain_plot,
160 "fd": self._frequency_domain_plot,
161 "omegascan": self._omega_scan_plot,
162 "spectrogram": self._spectrogram_plot
163 }
165 @property
166 def available_plots(self):
167 return list(self.plotting_map.keys())
169 @docstring_subfunction([
170 'pesummary.gw.plots.detchar.spectrogram',
171 'pesummary.gw.plots.detchar.omegascan',
172 'pesummary.gw.plots.detchar.time_domain_strain_data',
173 'pesummary.gw.plots.detchar.frequency_domain_strain_data'
174 ])
175 def plot(self, *args, type="td", **kwargs):
176 """Generate a plot displaying the gravitational wave strain data
178 Parameters
179 ----------
180 *args: tuple
181 all arguments are passed to the plotting function
182 type: str
183 name of the plot you wish to make
184 **kwargs: dict
185 all additional kwargs are passed to the plotting function
186 """
187 if type not in self.plotting_map.keys():
188 raise NotImplementedError(
189 "The {} method is not currently implemented. The allowed "
190 "plotting methods are {}".format(
191 type, ", ".join(self.available_plots)
192 )
193 )
194 return self.plotting_map[type](self.strain_dict, *args, **kwargs)
196 def _time_domain_plot(self, *args, **kwargs):
197 """Plot the strain data in the time domain
199 Parameters
200 ----------
201 *args: tuple
202 all args passed to the time_domain_strain_data function
203 **kwargs: dict
204 all kwargs passed to the time_domain_strain_data function
205 """
206 from pesummary.gw.plots.detchar import time_domain_strain_data
208 return time_domain_strain_data(*args, **kwargs)[self.IFO]
210 def _frequency_domain_plot(self, *args, **kwargs):
211 """Plot the strain data in the frequency domain
213 Parameters
214 ----------
215 *args: tuple
216 all args passed to the frequency_domain_strain_data function
217 **kwargs: dict
218 all kwargs passed to the frequency_domain_strain_data function
219 """
220 from pesummary.gw.plots.detchar import frequency_domain_strain_data
222 return frequency_domain_strain_data(*args, **kwargs)[self.IFO]
224 def _omega_scan_plot(self, *args, **kwargs):
225 """Plot an omegascan of the strain data
227 Parameters
228 ----------
229 *args: tuple
230 all args passed to the omegascan function
231 **kwargs: dict
232 all kwargs passed to the omegascan function
233 """
234 from pesummary.gw.plots.detchar import omegascan
236 return omegascan(*args, **kwargs)[self.IFO]
238 def _spectrogram_plot(self, *args, **kwargs):
239 """Plot the spectrogram of the strain data
241 Parameters
242 ----------
243 *args: tuple
244 all args passed to the spectrogram function
245 **kwargs: dict
246 all kwargs passed to the spectrogram function
247 """
248 from pesummary.gw.plots.detchar import spectrogram
250 return spectrogram(*args, **kwargs)[self.IFO]