gwpy 3.0.7__py3-none-any.whl → 3.0.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of gwpy might be problematic. Click here for more details.
- gwpy/_version.py +2 -2
- gwpy/astro/range.py +3 -3
- gwpy/astro/tests/test_range.py +4 -5
- gwpy/cli/qtransform.py +1 -1
- gwpy/detector/tests/test_units.py +3 -0
- gwpy/detector/units.py +12 -3
- gwpy/frequencyseries/frequencyseries.py +13 -4
- gwpy/io/cache.py +24 -1
- gwpy/io/datafind.py +1 -0
- gwpy/io/ffldatafind.py +27 -16
- gwpy/io/tests/test_ffldatafind.py +10 -3
- gwpy/plot/gps.py +7 -2
- gwpy/plot/tests/test_gps.py +1 -0
- gwpy/plot/tests/test_tex.py +3 -0
- gwpy/plot/tex.py +8 -6
- gwpy/segments/flag.py +14 -3
- gwpy/segments/tests/test_flag.py +53 -0
- gwpy/signal/filter_design.py +4 -3
- gwpy/signal/spectral/_lal.py +1 -1
- gwpy/signal/tests/test_coherence.py +9 -9
- gwpy/signal/tests/test_filter_design.py +3 -3
- gwpy/spectrogram/tests/test_spectrogram.py +2 -2
- gwpy/testing/fixtures.py +2 -19
- gwpy/testing/utils.py +43 -19
- gwpy/time/_tconvert.py +19 -33
- gwpy/time/tests/test_time.py +4 -45
- gwpy/timeseries/core.py +4 -2
- gwpy/timeseries/io/cache.py +47 -29
- gwpy/timeseries/io/gwf/framecpp.py +11 -2
- gwpy/timeseries/io/gwf/lalframe.py +21 -0
- gwpy/timeseries/io/losc.py +8 -2
- gwpy/timeseries/tests/test_io_cache.py +74 -0
- gwpy/timeseries/tests/test_io_gwf_lalframe.py +17 -0
- gwpy/timeseries/tests/test_timeseries.py +78 -36
- gwpy/types/array.py +16 -3
- gwpy/types/index.py +7 -5
- gwpy/types/sliceutils.py +5 -1
- gwpy/types/tests/test_array2d.py +4 -0
- gwpy/types/tests/test_series.py +26 -0
- gwpy/utils/shell.py +2 -2
- gwpy/utils/sphinx/zenodo.py +118 -61
- gwpy/utils/tests/test_shell.py +2 -2
- gwpy/utils/tests/test_sphinx_zenodo.py +175 -0
- {gwpy-3.0.7.dist-info → gwpy-3.0.9.dist-info}/METADATA +8 -7
- {gwpy-3.0.7.dist-info → gwpy-3.0.9.dist-info}/RECORD +49 -48
- {gwpy-3.0.7.dist-info → gwpy-3.0.9.dist-info}/WHEEL +1 -1
- gwpy/utils/sphinx/epydoc.py +0 -104
- {gwpy-3.0.7.dist-info → gwpy-3.0.9.dist-info}/LICENSE +0 -0
- {gwpy-3.0.7.dist-info → gwpy-3.0.9.dist-info}/entry_points.txt +0 -0
- {gwpy-3.0.7.dist-info → gwpy-3.0.9.dist-info}/top_level.txt +0 -0
gwpy/_version.py
CHANGED
gwpy/astro/range.py
CHANGED
|
@@ -25,7 +25,7 @@ import warnings
|
|
|
25
25
|
from functools import wraps
|
|
26
26
|
from math import pi
|
|
27
27
|
|
|
28
|
-
from scipy.integrate import
|
|
28
|
+
from scipy.integrate import trapezoid
|
|
29
29
|
from scipy.interpolate import interp1d
|
|
30
30
|
|
|
31
31
|
from astropy import (
|
|
@@ -238,7 +238,7 @@ def sensemon_range(psd, snr=8, mass1=1.4, mass2=1.4, fmin=None, fmax=None,
|
|
|
238
238
|
integrand = sensemon_range_psd(psd[frange], snr=snr, mass1=mass1,
|
|
239
239
|
mass2=mass2, horizon=horizon)
|
|
240
240
|
return (units.Quantity(
|
|
241
|
-
|
|
241
|
+
trapezoid(integrand.value, f.value[frange]),
|
|
242
242
|
unit=integrand.unit * units.Hertz,
|
|
243
243
|
) ** (1/2.)).to('Mpc')
|
|
244
244
|
|
|
@@ -528,7 +528,7 @@ def burst_range(psd, snr=8, energy=1e-2, fmin=100, fmax=500):
|
|
|
528
528
|
# calculate integrand and integrate
|
|
529
529
|
integrand = burst_range_spectrum(
|
|
530
530
|
psd[frange], snr=snr, energy=energy) ** 3
|
|
531
|
-
out =
|
|
531
|
+
out = trapezoid(integrand.value, f[frange])
|
|
532
532
|
# normalize and return
|
|
533
533
|
return (units.Quantity(
|
|
534
534
|
out / (fmax - fmin),
|
gwpy/astro/tests/test_range.py
CHANGED
|
@@ -24,7 +24,7 @@ from unittest import mock
|
|
|
24
24
|
import pytest
|
|
25
25
|
|
|
26
26
|
from astropy import units
|
|
27
|
-
from scipy.integrate import
|
|
27
|
+
from scipy.integrate import trapezoid
|
|
28
28
|
|
|
29
29
|
from ... import astro
|
|
30
30
|
from ...testing import utils
|
|
@@ -71,7 +71,7 @@ def test_sensemon_range_psd(psd):
|
|
|
71
71
|
r = astro.sensemon_range_psd(psd[frange])
|
|
72
72
|
assert isinstance(r, FrequencySeries)
|
|
73
73
|
utils.assert_quantity_almost_equal(
|
|
74
|
-
|
|
74
|
+
trapezoid(r, r.frequencies) ** (1/2.),
|
|
75
75
|
TEST_RESULTS['sensemon_range'],
|
|
76
76
|
)
|
|
77
77
|
assert r.f0.value > 0
|
|
@@ -103,9 +103,8 @@ def test_inspiral_range_psd(psd):
|
|
|
103
103
|
frange = (psd.frequencies.value < 4096)
|
|
104
104
|
r = astro.inspiral_range_psd(psd[frange])
|
|
105
105
|
assert isinstance(r, FrequencySeries)
|
|
106
|
-
print(trapz(r, r.frequencies) ** (1/2.))
|
|
107
106
|
utils.assert_quantity_almost_equal(
|
|
108
|
-
|
|
107
|
+
trapezoid(r, r.frequencies) ** (1/2.),
|
|
109
108
|
TEST_RESULTS['inspiral_range'],
|
|
110
109
|
)
|
|
111
110
|
assert r.f0.value > 0
|
|
@@ -129,7 +128,7 @@ def test_burst_range_spectrum(psd):
|
|
|
129
128
|
r = astro.burst_range_spectrum(psd[frange])
|
|
130
129
|
assert isinstance(r, FrequencySeries)
|
|
131
130
|
utils.assert_quantity_almost_equal(
|
|
132
|
-
(
|
|
131
|
+
(trapezoid(r**3, f[frange]) / (400 * units.Hz)) ** (1/3.),
|
|
133
132
|
TEST_RESULTS['burst_range'],
|
|
134
133
|
)
|
|
135
134
|
assert r.f0.value > 0
|
gwpy/cli/qtransform.py
CHANGED
|
@@ -215,7 +215,7 @@ class Qtransform(Spectrogram):
|
|
|
215
215
|
outseg = Segment(gps, gps).protract(args.plot[self.plot_num])
|
|
216
216
|
|
|
217
217
|
# use the precomputed ASD as the whitener if needed
|
|
218
|
-
if self.qxfrm_args.get("whiten"):
|
|
218
|
+
if self.qxfrm_args.get("whiten") is True:
|
|
219
219
|
self.qxfrm_args["whiten"] = asd
|
|
220
220
|
|
|
221
221
|
# This section tries to optimize the amount of data that is
|
|
@@ -39,6 +39,9 @@ __author__ = 'Duncan Macleod <duncan.macleod@ligo.org>'
|
|
|
39
39
|
('degrees_C', units.Unit('Celsius')),
|
|
40
40
|
('DegC', units.Unit('Celsius')),
|
|
41
41
|
('degrees_F', units.Unit('Fahrenheit')),
|
|
42
|
+
('time', units.second), # LIGO default time 'unit'
|
|
43
|
+
('Time (sec)', units.second), # Virgo default time 'unit'
|
|
44
|
+
('Seconds', units.second), # GWOSC default time 'unit'
|
|
42
45
|
])
|
|
43
46
|
def test_parse_unit(arg, unit):
|
|
44
47
|
assert parse_unit(arg, parse_strict='silent') == unit
|
gwpy/detector/units.py
CHANGED
|
@@ -161,9 +161,18 @@ units.add_enabled_units(units_imperial)
|
|
|
161
161
|
# 1) alternative names
|
|
162
162
|
registry = units.get_current_unit_registry().registry
|
|
163
163
|
for unit, aliases in [
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
164
|
+
(units.Unit('ct'), ('counts',)),
|
|
165
|
+
(units.Unit('Celsius'), ('Degrees_C', 'DegC')),
|
|
166
|
+
(units.Unit('Fahrenheit'), ('Degrees_F', 'DegF')),
|
|
167
|
+
# GW observatories like to record 'time' as the unit
|
|
168
|
+
(units.Unit('second'), (
|
|
169
|
+
'time',
|
|
170
|
+
'time (s)',
|
|
171
|
+
'time [s]',
|
|
172
|
+
'Time [sec]',
|
|
173
|
+
'Time (sec)',
|
|
174
|
+
'Seconds', # GWOSC
|
|
175
|
+
)),
|
|
167
176
|
]:
|
|
168
177
|
unit.names.extend(aliases)
|
|
169
178
|
for alias in aliases:
|
|
@@ -227,10 +227,19 @@ class FrequencySeries(Series):
|
|
|
227
227
|
# Undo normalization from TimeSeries.fft
|
|
228
228
|
# The DC component does not have the factor of two applied
|
|
229
229
|
# so we account for it here
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
230
|
+
array = self.value.copy()
|
|
231
|
+
array[1:] /= 2.0
|
|
232
|
+
dift = npfft.irfft(
|
|
233
|
+
array * nout,
|
|
234
|
+
n=nout,
|
|
235
|
+
)
|
|
236
|
+
return TimeSeries(
|
|
237
|
+
dift,
|
|
238
|
+
epoch=self.epoch,
|
|
239
|
+
channel=self.channel,
|
|
240
|
+
unit=self.unit,
|
|
241
|
+
dx=(1/self.dx/nout).to(TimeSeries._default_xunit),
|
|
242
|
+
)
|
|
234
243
|
|
|
235
244
|
def zpk(self, zeros, poles, gain, analog=True):
|
|
236
245
|
"""Filter this `FrequencySeries` by applying a zero-pole-gain filter
|
gwpy/io/cache.py
CHANGED
|
@@ -23,6 +23,7 @@ and associated metadata for those files, designed to make identifying
|
|
|
23
23
|
relevant data, and sieving large file lists, easier for the user.
|
|
24
24
|
"""
|
|
25
25
|
|
|
26
|
+
import contextlib
|
|
26
27
|
import os
|
|
27
28
|
import warnings
|
|
28
29
|
from collections import (namedtuple, OrderedDict)
|
|
@@ -51,6 +52,27 @@ else:
|
|
|
51
52
|
__author__ = 'Duncan Macleod <duncan.macleod@ligo.org>'
|
|
52
53
|
|
|
53
54
|
|
|
55
|
+
@contextlib.contextmanager
|
|
56
|
+
def _silence_lal_debug_warnings():
|
|
57
|
+
"""Temporarily silence debug warnings from LAL.
|
|
58
|
+
"""
|
|
59
|
+
try:
|
|
60
|
+
import lal
|
|
61
|
+
except ImportError:
|
|
62
|
+
yield
|
|
63
|
+
return
|
|
64
|
+
|
|
65
|
+
debuglevel = lal.GetDebugLevel()
|
|
66
|
+
# silence LAL debug warnings
|
|
67
|
+
lal.ClobberDebugLevel(0)
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
yield
|
|
71
|
+
finally:
|
|
72
|
+
# reset
|
|
73
|
+
lal.ClobberDebugLevel(debuglevel)
|
|
74
|
+
|
|
75
|
+
|
|
54
76
|
def _preformat_entry(entry):
|
|
55
77
|
path = file_path(entry)
|
|
56
78
|
obs, tag, seg = filename_metadata(path)
|
|
@@ -83,7 +105,8 @@ def _format_entry_ffl(entry):
|
|
|
83
105
|
def _parse_entry_ffl(line, gpstype=LIGOTimeGPS):
|
|
84
106
|
from ..segments import Segment
|
|
85
107
|
path, start, dur, _, _ = line
|
|
86
|
-
|
|
108
|
+
with _silence_lal_debug_warnings():
|
|
109
|
+
start = gpstype(start)
|
|
87
110
|
end = start + float(dur)
|
|
88
111
|
try:
|
|
89
112
|
observatory, description = os.path.basename(path).split('-', 2)[:2]
|
gwpy/io/datafind.py
CHANGED
|
@@ -65,6 +65,7 @@ GRB_TYPE = re.compile(r'^(?!.*_GRB\d{6}([A-Z])?$)') # *_GRBYYMMDD{A}
|
|
|
65
65
|
HIGH_PRIORITY_TYPE = re.compile("({})".format("|".join((
|
|
66
66
|
r'\A[A-Z]\d_HOFT_C\d\d(_T\d{7}_v\d)?\Z', # X1_HOFT_CXY
|
|
67
67
|
r'\AV1Online\Z',
|
|
68
|
+
r'\AHoftOnline\Z',
|
|
68
69
|
r'\AV1O[0-9]+([A-Z]+)?Repro[0-9]+[A-Z]+\Z', # V1OXReproXY
|
|
69
70
|
))))
|
|
70
71
|
LOW_PRIORITY_TYPE = re.compile("({})".format("|".join((
|
gwpy/io/ffldatafind.py
CHANGED
|
@@ -28,8 +28,9 @@ __author__ = "Duncan Macleod <duncan.macleod@ligo.org>"
|
|
|
28
28
|
|
|
29
29
|
import os
|
|
30
30
|
import re
|
|
31
|
-
from
|
|
31
|
+
from collections import defaultdict
|
|
32
32
|
from functools import lru_cache
|
|
33
|
+
from warnings import warn
|
|
33
34
|
|
|
34
35
|
from ligo.segments import (
|
|
35
36
|
segment,
|
|
@@ -37,8 +38,10 @@ from ligo.segments import (
|
|
|
37
38
|
)
|
|
38
39
|
|
|
39
40
|
from .cache import (
|
|
41
|
+
_CacheEntry,
|
|
40
42
|
_iter_cache,
|
|
41
43
|
cache_segments,
|
|
44
|
+
file_segment,
|
|
42
45
|
read_cache_entry,
|
|
43
46
|
)
|
|
44
47
|
|
|
@@ -123,10 +126,10 @@ def _find_ffl_files(basedir=None):
|
|
|
123
126
|
def _find_ffls(basedir=None):
|
|
124
127
|
"""Find all readable FFL files.
|
|
125
128
|
"""
|
|
126
|
-
ffls =
|
|
129
|
+
ffls = defaultdict(list)
|
|
127
130
|
for path in _find_ffl_files(basedir=basedir):
|
|
128
131
|
try:
|
|
129
|
-
ffls[_get_site_tag(path)]
|
|
132
|
+
ffls[_get_site_tag(path)].append(path)
|
|
130
133
|
except (
|
|
131
134
|
OSError, # file is empty (or cannot be read at all)
|
|
132
135
|
AttributeError, # last entry didn't match _SITE_REGEX
|
|
@@ -135,8 +138,8 @@ def _find_ffls(basedir=None):
|
|
|
135
138
|
return ffls
|
|
136
139
|
|
|
137
140
|
|
|
138
|
-
def
|
|
139
|
-
"""Return the
|
|
141
|
+
def _ffl_paths(site, tag, basedir=None):
|
|
142
|
+
"""Return the paths of all FFL files for a given site and tag.
|
|
140
143
|
"""
|
|
141
144
|
try:
|
|
142
145
|
return _find_ffls(basedir=basedir)[(site, tag)]
|
|
@@ -147,15 +150,18 @@ def _ffl_path(site, tag, basedir=None):
|
|
|
147
150
|
|
|
148
151
|
|
|
149
152
|
@lru_cache()
|
|
150
|
-
def
|
|
151
|
-
"""Read
|
|
153
|
+
def _read_ffls(site, tag, basedir=None):
|
|
154
|
+
"""Read all FFL files for a given site and tag
|
|
155
|
+
as a list of `CacheEntry` objects.
|
|
152
156
|
"""
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
157
|
+
entries = []
|
|
158
|
+
for ffl in _ffl_paths(site, tag, basedir=basedir):
|
|
159
|
+
with open(ffl, "r") as fobj:
|
|
160
|
+
entries.extend(
|
|
161
|
+
_CacheEntry(site, tag, entry.segment, entry.path)
|
|
162
|
+
for entry in _iter_cache(fobj, gpstype=float)
|
|
163
|
+
)
|
|
164
|
+
return entries
|
|
159
165
|
|
|
160
166
|
|
|
161
167
|
def _handle_error(action, message):
|
|
@@ -261,7 +267,7 @@ def find_urls(
|
|
|
261
267
|
span = segment(gpsstart, gpsend)
|
|
262
268
|
|
|
263
269
|
cache = [
|
|
264
|
-
e for e in
|
|
270
|
+
e for e in _read_ffls(site, tag) if (
|
|
265
271
|
e.observatory == site
|
|
266
272
|
and e.description == tag
|
|
267
273
|
and e.segment.intersects(span)
|
|
@@ -305,11 +311,16 @@ def find_latest(site, tag, on_missing="warn"):
|
|
|
305
311
|
for a specific site and tag.
|
|
306
312
|
"""
|
|
307
313
|
try:
|
|
308
|
-
|
|
314
|
+
fflfiles = _ffl_paths(site, tag)
|
|
309
315
|
except ValueError: # no readable FFL file
|
|
310
316
|
urls = []
|
|
311
317
|
else:
|
|
312
|
-
urls = [
|
|
318
|
+
urls = [
|
|
319
|
+
read_cache_entry(_read_last_line(fflfile), gpstype=float)
|
|
320
|
+
for fflfile in fflfiles
|
|
321
|
+
]
|
|
322
|
+
if urls: # if multiple, find the latest one
|
|
323
|
+
urls = sorted(urls, key=file_segment)[-1:]
|
|
313
324
|
|
|
314
325
|
if not urls:
|
|
315
326
|
_handle_error(on_missing, "No files found")
|
|
@@ -96,9 +96,11 @@ def test_is_ffl_file(path, result):
|
|
|
96
96
|
# -- test ffl UI ------------
|
|
97
97
|
|
|
98
98
|
FFLS = {
|
|
99
|
-
"test.ffl": [
|
|
99
|
+
"a/test.ffl": [
|
|
100
100
|
"/tmp/X-test-0-1.gwf 0 1 0 0",
|
|
101
101
|
"/tmp/X-test-1-1.gwf 1 1 0 0",
|
|
102
|
+
],
|
|
103
|
+
"b/test.ffl": [
|
|
102
104
|
"/tmp/X-test-2-1.gwf 2 1 0 0",
|
|
103
105
|
],
|
|
104
106
|
"test2.ffl": [
|
|
@@ -114,7 +116,11 @@ FFLS = {
|
|
|
114
116
|
"test-empty.ffl": [],
|
|
115
117
|
"test-bad.ffl": ["badness"],
|
|
116
118
|
}
|
|
117
|
-
TEST_URLS = [
|
|
119
|
+
TEST_URLS = [
|
|
120
|
+
x.split()[0]
|
|
121
|
+
for key in ("a/test.ffl", "b/test.ffl")
|
|
122
|
+
for x in FFLS[key]
|
|
123
|
+
]
|
|
118
124
|
|
|
119
125
|
|
|
120
126
|
@pytest.fixture(autouse=True)
|
|
@@ -124,6 +130,7 @@ def mock_ffl(tmp_path):
|
|
|
124
130
|
"""
|
|
125
131
|
for path, lines in FFLS.items():
|
|
126
132
|
ffl = tmp_path / path
|
|
133
|
+
ffl.parent.mkdir(parents=True, exist_ok=True)
|
|
127
134
|
ffl.write_text("\n".join(lines))
|
|
128
135
|
with mock.patch.dict(
|
|
129
136
|
"os.environ",
|
|
@@ -190,7 +197,7 @@ def test_find_latest():
|
|
|
190
197
|
assert ffldatafind.find_latest(
|
|
191
198
|
"X",
|
|
192
199
|
"test",
|
|
193
|
-
) == sorted(x.split()[0] for x in FFLS["test.ffl"])[-1:]
|
|
200
|
+
) == sorted(x.split()[0] for x in FFLS["b/test.ffl"])[-1:]
|
|
194
201
|
|
|
195
202
|
|
|
196
203
|
@pytest.mark.parametrize(("on_missing", "ctx"), (
|
gwpy/plot/gps.py
CHANGED
|
@@ -164,8 +164,13 @@ class GPSMixin(object):
|
|
|
164
164
|
"""
|
|
165
165
|
if not self.unit:
|
|
166
166
|
return None
|
|
167
|
-
|
|
168
|
-
|
|
167
|
+
try:
|
|
168
|
+
name = self.unit.long_names[0]
|
|
169
|
+
except IndexError:
|
|
170
|
+
name = self.unit.name
|
|
171
|
+
if len(name) > 1:
|
|
172
|
+
return name + "s" # pluralise for humans
|
|
173
|
+
return name
|
|
169
174
|
|
|
170
175
|
def get_scale(self):
|
|
171
176
|
"""The scale (in seconds) of the current GPS unit.
|
gwpy/plot/tests/test_gps.py
CHANGED
gwpy/plot/tests/test_tex.py
CHANGED
|
@@ -39,6 +39,7 @@ def test_has_tex_missing_exe():
|
|
|
39
39
|
"""Test that `gwpy.plot.tex.has_tex` returns `False` when
|
|
40
40
|
any one of the necessary executables is missing.
|
|
41
41
|
"""
|
|
42
|
+
plot_tex.has_tex.cache_clear()
|
|
42
43
|
assert not plot_tex.has_tex()
|
|
43
44
|
|
|
44
45
|
|
|
@@ -47,6 +48,7 @@ def test_has_tex_bad_latex(_):
|
|
|
47
48
|
"""Test that `gwpy.plot.tex.has_tex` returns `False` when
|
|
48
49
|
the LaTeX figure fails to render.
|
|
49
50
|
"""
|
|
51
|
+
plot_tex.has_tex.cache_clear()
|
|
50
52
|
assert not plot_tex.has_tex()
|
|
51
53
|
|
|
52
54
|
|
|
@@ -57,6 +59,7 @@ def test_has_tex_true(_which_, _test_usetex):
|
|
|
57
59
|
all of the necessary executables are found, and the LaTeX figure
|
|
58
60
|
doesn't raise an exception.
|
|
59
61
|
"""
|
|
62
|
+
plot_tex.has_tex.cache_clear()
|
|
60
63
|
assert plot_tex.has_tex()
|
|
61
64
|
|
|
62
65
|
|
gwpy/plot/tex.py
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
23
|
import re
|
|
24
|
+
from functools import lru_cache
|
|
24
25
|
from shutil import which
|
|
25
26
|
|
|
26
27
|
__author__ = "Duncan Macleod <duncan.macleod@ligo.org>"
|
|
@@ -38,12 +39,15 @@ def _test_usetex():
|
|
|
38
39
|
from matplotlib import (pyplot, rc_context)
|
|
39
40
|
with rc_context({"text.usetex": True}):
|
|
40
41
|
fig = pyplot.figure()
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
try:
|
|
43
|
+
ax = fig.gca()
|
|
44
|
+
ax.set_xlabel(r"\LaTeX")
|
|
45
|
+
fig.canvas.draw()
|
|
46
|
+
finally:
|
|
47
|
+
pyplot.close(fig)
|
|
45
48
|
|
|
46
49
|
|
|
50
|
+
@lru_cache(maxsize=None)
|
|
47
51
|
def has_tex():
|
|
48
52
|
"""Returns whether tex is installed on this system
|
|
49
53
|
|
|
@@ -69,8 +73,6 @@ def has_tex():
|
|
|
69
73
|
return True
|
|
70
74
|
|
|
71
75
|
|
|
72
|
-
HAS_TEX = has_tex()
|
|
73
|
-
|
|
74
76
|
# -- tex formatting -----------------------------------------------------------
|
|
75
77
|
|
|
76
78
|
LATEX_CONTROL_CHARS = ["%", "\\", "_", "~", "&", "#"]
|
gwpy/segments/flag.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
# Copyright (C) Louisiana State University (2014-2017)
|
|
3
|
-
# Cardiff University (2017-
|
|
3
|
+
# Cardiff University (2017-2023)
|
|
4
4
|
#
|
|
5
5
|
# This file is part of GWpy.
|
|
6
6
|
#
|
|
@@ -1263,12 +1263,19 @@ class DataQualityDict(OrderedDict):
|
|
|
1263
1263
|
|
|
1264
1264
|
# parse a table into the target DataQualityDict
|
|
1265
1265
|
def _parse_segments(table, listattr):
|
|
1266
|
+
# handle missing *_ns columns in LIGO_LW XML
|
|
1267
|
+
# (LIGO DMT doesn't/didn't write them)
|
|
1268
|
+
if 'start_time_ns' in table.columnnames:
|
|
1269
|
+
row_segment = operator.attrgetter("segment")
|
|
1270
|
+
else:
|
|
1271
|
+
row_segment = operator.attrgetter("start_time", "end_time")
|
|
1272
|
+
|
|
1266
1273
|
for row in table:
|
|
1267
1274
|
for flag in out:
|
|
1268
1275
|
# match row ID to list of IDs found for this flag
|
|
1269
1276
|
if int(row.segment_def_id) in id_[flag]:
|
|
1270
1277
|
getattr(out[flag], listattr).append(
|
|
1271
|
-
Segment(*map(gpstype, row
|
|
1278
|
+
Segment(*map(gpstype, row_segment(row))),
|
|
1272
1279
|
)
|
|
1273
1280
|
break
|
|
1274
1281
|
|
|
@@ -1442,7 +1449,11 @@ class DataQualityDict(OrderedDict):
|
|
|
1442
1449
|
if segments is None and source.netloc:
|
|
1443
1450
|
try:
|
|
1444
1451
|
tmp = {key: self[key].query(
|
|
1445
|
-
self[key].name,
|
|
1452
|
+
self[key].name,
|
|
1453
|
+
self[key].known,
|
|
1454
|
+
url=source.geturl(),
|
|
1455
|
+
**kwargs,
|
|
1456
|
+
)}
|
|
1446
1457
|
except URLError as exc:
|
|
1447
1458
|
if on_error == 'ignore':
|
|
1448
1459
|
pass
|
gwpy/segments/tests/test_flag.py
CHANGED
|
@@ -76,6 +76,52 @@ def veto_definer(tmp_path):
|
|
|
76
76
|
return tmp
|
|
77
77
|
|
|
78
78
|
|
|
79
|
+
# -- XML without _ns columns --------------------------------------------------
|
|
80
|
+
|
|
81
|
+
LIGOLW_NO_NS = """
|
|
82
|
+
<?xml version="1.0"?>
|
|
83
|
+
<!DOCTYPE LIGO_LW SYSTEM "http://ldas-sw.ligo.caltech.edu/doc/ligolwAPI/html/ligolw_dtd.txt">
|
|
84
|
+
<LIGO_LW>
|
|
85
|
+
<Table Name="segment_definergroup:segment_definer:table">
|
|
86
|
+
<Column Name="segment_def_id" Type="int_8s"/>
|
|
87
|
+
<Column Name="ifos" Type="lstring"/>
|
|
88
|
+
<Column Name="name" Type="lstring"/>
|
|
89
|
+
<Column Name="version" Type="int_4s"/>
|
|
90
|
+
<Column Name="comment" Type="lstring"/>
|
|
91
|
+
<Stream Name="segment_definergroup:segment_definer:table" Type="Local" Delimiter=",">
|
|
92
|
+
0,"X1","TEST_FLAG",1,"Test flag",
|
|
93
|
+
</Stream>
|
|
94
|
+
</Table>
|
|
95
|
+
<Table Name="segment_summarygroup:segment_summary:table">
|
|
96
|
+
<Column Name="segment_sum_id" Type="int_8s"/>
|
|
97
|
+
<Column Name="start_time" Type="int_4s"/>
|
|
98
|
+
<Column Name="end_time" Type="int_4s"/>
|
|
99
|
+
<Column Name="comment" Type="lstring"/>
|
|
100
|
+
<Column Name="segment_definer:segment_def_id" Type="int_8s"/>
|
|
101
|
+
<Stream Name="segment_summarygroup:segment_summary:table" Type="Local" Delimiter=",">
|
|
102
|
+
0,1366644592,1366644608,"",0,
|
|
103
|
+
</Stream>
|
|
104
|
+
</Table>
|
|
105
|
+
<Table Name="segmentgroup:segment:table">
|
|
106
|
+
<Column Name="segment_id" Type="int_8s"/>
|
|
107
|
+
<Column Name="start_time" Type="int_4s"/>
|
|
108
|
+
<Column Name="end_time" Type="int_4s"/>
|
|
109
|
+
<Column Name="segment_definer:segment_def_id" Type="int_8s"/>
|
|
110
|
+
<Stream Name="segmentgroup:segment:table" Type="Local" Delimiter=",">
|
|
111
|
+
0,1366644592,1366644593,0,
|
|
112
|
+
</Stream>
|
|
113
|
+
</Table>
|
|
114
|
+
</LIGO_LW>
|
|
115
|
+
""".strip() # noqa: E501
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@pytest.fixture
|
|
119
|
+
def ligolw_no_ns(tmp_path):
|
|
120
|
+
tmp = tmp_path / "test.xml"
|
|
121
|
+
tmp.write_text(LIGOLW_NO_NS)
|
|
122
|
+
return tmp
|
|
123
|
+
|
|
124
|
+
|
|
79
125
|
# -- test data ----------------------------------------------------------------
|
|
80
126
|
|
|
81
127
|
def _as_segmentlist(*segments):
|
|
@@ -489,6 +535,13 @@ class TestDataQualityFlag(object):
|
|
|
489
535
|
segdeftab = read_table(tmp, 'segment_definer')
|
|
490
536
|
assert int(segdeftab[0].process_id) == 100
|
|
491
537
|
|
|
538
|
+
@pytest.mark.requires("ligo.lw.lsctables")
|
|
539
|
+
def test_read_ligolw_no_ns(self, ligolw_no_ns):
|
|
540
|
+
flag = self.TEST_CLASS.read(ligolw_no_ns, format="ligolw")
|
|
541
|
+
assert flag.name == "X1:TEST_FLAG:1"
|
|
542
|
+
assert flag.known == [(1366644592, 1366644608)]
|
|
543
|
+
assert flag.active == [(1366644592, 1366644593)]
|
|
544
|
+
|
|
492
545
|
# -- test queries ---------------------------
|
|
493
546
|
|
|
494
547
|
@mock.patch("gwpy.segments.flag.query_segments", mock_query_segments)
|
gwpy/signal/filter_design.py
CHANGED
|
@@ -30,6 +30,7 @@ from scipy import signal
|
|
|
30
30
|
|
|
31
31
|
from astropy.units import (Unit, Quantity)
|
|
32
32
|
|
|
33
|
+
from ..types.array import COPY_IF_NEEDED
|
|
33
34
|
from .window import (get_window, planck)
|
|
34
35
|
|
|
35
36
|
__author__ = "Duncan Macleod <duncan.macleod@ligo.org>"
|
|
@@ -89,7 +90,7 @@ def _design_fir(wp, ws, sample_rate, gpass, gstop, window='hamming', **kwargs):
|
|
|
89
90
|
kwargs.setdefault('pass_zero', False)
|
|
90
91
|
if ws.shape == (1,):
|
|
91
92
|
kwargs.setdefault('width', ws - wp)
|
|
92
|
-
kwargs.setdefault('
|
|
93
|
+
kwargs.setdefault('fs', sample_rate)
|
|
93
94
|
return signal.firwin(nt, wp, window=window, **kwargs)
|
|
94
95
|
|
|
95
96
|
|
|
@@ -281,9 +282,9 @@ def bilinear_zpk(zeros, poles, gain, fs=1.0, unit='Hz'):
|
|
|
281
282
|
zpk : `tuple`
|
|
282
283
|
digital version of input zpk
|
|
283
284
|
"""
|
|
284
|
-
zeros = numpy.array(zeros, dtype=float, copy=
|
|
285
|
+
zeros = numpy.array(zeros, dtype=float, copy=COPY_IF_NEEDED)
|
|
285
286
|
zeros = zeros[numpy.isfinite(zeros)]
|
|
286
|
-
poles = numpy.array(poles, dtype=float, copy=
|
|
287
|
+
poles = numpy.array(poles, dtype=float, copy=COPY_IF_NEEDED)
|
|
287
288
|
gain = gain
|
|
288
289
|
|
|
289
290
|
# convert from Hz to rad/s if needed
|
gwpy/signal/spectral/_lal.py
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"""GWpy API to the LAL FFT routines
|
|
20
20
|
|
|
21
21
|
See the `LAL TimeFreqFFT.h documentation
|
|
22
|
-
<
|
|
22
|
+
<https://lscsoft.docs.ligo.org/lalsuite/lal/group___time_freq_f_f_t__h.html>`_
|
|
23
23
|
for more details
|
|
24
24
|
|
|
25
25
|
This module is deprecated and will be removed in a future release.
|
|
@@ -33,7 +33,8 @@ from ...timeseries import TimeSeries
|
|
|
33
33
|
def series_data():
|
|
34
34
|
"""Create some fake data with equal sampling frequencies.
|
|
35
35
|
|
|
36
|
-
Returns
|
|
36
|
+
Returns
|
|
37
|
+
-------
|
|
37
38
|
firstarr: an array of data, simple mixture of waves
|
|
38
39
|
secondarr: a second array of data from different mixture
|
|
39
40
|
seglen: segment length param to reuse for ffts
|
|
@@ -49,7 +50,7 @@ def series_data():
|
|
|
49
50
|
firstarr += np.random.normal(5.8, 2, n_t)
|
|
50
51
|
|
|
51
52
|
secondarr = 0.5 * np.cos(ts + 0.1) + 0.1 * np.sin(5 * ts + 10)
|
|
52
|
-
|
|
53
|
+
secondarr += np.random.normal(5.8, 2, n_t)
|
|
53
54
|
|
|
54
55
|
return firstarr, secondarr, seglen
|
|
55
56
|
|
|
@@ -58,7 +59,8 @@ def series_data():
|
|
|
58
59
|
def unequal_fs_series_data():
|
|
59
60
|
"""Create some fake data with unequal sampling frequencies.
|
|
60
61
|
|
|
61
|
-
Returns
|
|
62
|
+
Returns
|
|
63
|
+
-------
|
|
62
64
|
ts1: array of time points for first data array
|
|
63
65
|
ts2: array of time points for second data array
|
|
64
66
|
firstarr: an array of data, simple mixture of waves
|
|
@@ -67,7 +69,6 @@ def unequal_fs_series_data():
|
|
|
67
69
|
fs_1: sampling frequency 1
|
|
68
70
|
fs_2: sampling frequency 2
|
|
69
71
|
"""
|
|
70
|
-
|
|
71
72
|
seglen = 512
|
|
72
73
|
n_segs1 = 10
|
|
73
74
|
n_segs2 = 20
|
|
@@ -92,7 +93,6 @@ def test_coherence_happy(series_data):
|
|
|
92
93
|
|
|
93
94
|
For other tests see timeseries/tests/timeseries.py
|
|
94
95
|
"""
|
|
95
|
-
|
|
96
96
|
firstarr, secondarr, seglen = series_data
|
|
97
97
|
f_s = 0.001
|
|
98
98
|
|
|
@@ -108,8 +108,8 @@ def test_coherence_happy(series_data):
|
|
|
108
108
|
|
|
109
109
|
|
|
110
110
|
def test_coherence_resample(unequal_fs_series_data):
|
|
111
|
-
"""Ensure warning is raised by unequal sampling frequencies
|
|
112
|
-
|
|
111
|
+
"""Ensure warning is raised by unequal sampling frequencies.
|
|
112
|
+
"""
|
|
113
113
|
ts1, ts2, firstarr, secondarr, seglen, fs_1, fs_2 = unequal_fs_series_data
|
|
114
114
|
|
|
115
115
|
# first and second arrays are different, secondarr should have
|
|
@@ -137,8 +137,8 @@ def test_coherence_resample(unequal_fs_series_data):
|
|
|
137
137
|
|
|
138
138
|
|
|
139
139
|
def test_coherence_resample_arg(series_data):
|
|
140
|
-
"""Ensure warning is raised by unequal sampling frequencies
|
|
141
|
-
|
|
140
|
+
"""Ensure warning is raised by unequal sampling frequencies.
|
|
141
|
+
"""
|
|
142
142
|
firstarr, secondarr, seglen = series_data
|
|
143
143
|
f_s = 0.001
|
|
144
144
|
|
|
@@ -50,7 +50,7 @@ LOWPASS_IIR_100HZ = signal.iirdesign(
|
|
|
50
50
|
analog=False, ftype='cheby1', output='zpk',
|
|
51
51
|
)
|
|
52
52
|
LOWPASS_FIR_100HZ = signal.firwin(
|
|
53
|
-
30, 100, window='hamming', width=50.,
|
|
53
|
+
30, 100, window='hamming', width=50., fs=1024.,
|
|
54
54
|
)
|
|
55
55
|
|
|
56
56
|
HIGHPASS_IIR_100HZ = signal.iirdesign(
|
|
@@ -60,7 +60,7 @@ HIGHPASS_IIR_100HZ = signal.iirdesign(
|
|
|
60
60
|
analog=False, ftype='cheby1', output='zpk',
|
|
61
61
|
)
|
|
62
62
|
HIGHPASS_FIR_100HZ = signal.firwin(
|
|
63
|
-
45, 100, window='hamming', pass_zero=False, width=-100/3.,
|
|
63
|
+
45, 100, window='hamming', pass_zero=False, width=-100/3., fs=1024.,
|
|
64
64
|
)
|
|
65
65
|
|
|
66
66
|
BANDPASS_IIR_100HZ_200HZ = signal.iirdesign(
|
|
@@ -70,7 +70,7 @@ BANDPASS_IIR_100HZ_200HZ = signal.iirdesign(
|
|
|
70
70
|
analog=False, ftype='cheby1', output='zpk',
|
|
71
71
|
)
|
|
72
72
|
BANDPASS_FIR_100HZ_200HZ = signal.firwin(
|
|
73
|
-
45, (100, 200.), window='hamming', pass_zero=False,
|
|
73
|
+
45, (100, 200.), window='hamming', pass_zero=False, fs=1024.,
|
|
74
74
|
)
|
|
75
75
|
|
|
76
76
|
|