sxs 2023.3.2__py3-none-any.whl → 2024.0.0__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.
- sxs/__init__.py +5 -5
- sxs/__version__.py +1 -1
- sxs/catalog/catalog.py +10 -1
- sxs/handlers.py +150 -48
- sxs/horizons/__init__.py +3 -3
- sxs/juliapkg.json +2 -2
- sxs/simulations/__init__.py +2 -0
- sxs/simulations/simulation.py +521 -0
- sxs/simulations/simulations.py +369 -0
- sxs/time_series.py +59 -0
- sxs/utilities/__init__.py +6 -2
- sxs/utilities/sxs_identifiers.py +19 -9
- sxs/utilities/url.py +4 -1
- sxs/waveforms/format_handlers/rotating_paired_diff_multishuffle_bzip2.py +9 -6
- sxs/waveforms/waveform_modes.py +7 -0
- sxs/zenodo/__init__.py +13 -0
- sxs/zenodo/api/deposit.py +2 -2
- {sxs-2023.3.2.dist-info → sxs-2024.0.0.dist-info}/METADATA +14 -15
- {sxs-2023.3.2.dist-info → sxs-2024.0.0.dist-info}/RECORD +21 -18
- {sxs-2023.3.2.dist-info → sxs-2024.0.0.dist-info}/WHEEL +0 -0
- {sxs-2023.3.2.dist-info → sxs-2024.0.0.dist-info}/licenses/LICENSE +0 -0
sxs/__init__.py
CHANGED
|
@@ -11,6 +11,9 @@ except ModuleNotFoundError: # pragma: no cover
|
|
|
11
11
|
|
|
12
12
|
__version__ = importlib_metadata.version(__name__)
|
|
13
13
|
|
|
14
|
+
doi_prefix = "10.26138"
|
|
15
|
+
doi_url = f"https://doi.org/{doi_prefix}/"
|
|
16
|
+
|
|
14
17
|
from . import utilities
|
|
15
18
|
from .utilities import (
|
|
16
19
|
file_format, sxs_directory, read_config, write_config,
|
|
@@ -26,7 +29,8 @@ from .waveforms import rotating_paired_xor_multishuffle_bzip2 as rpxmb
|
|
|
26
29
|
from .waveforms import rotating_paired_diff_multishuffle_bzip2 as rpdmb
|
|
27
30
|
from .waveforms import spectre_cce_v1
|
|
28
31
|
from . import catalog, metadata, horizons, waveforms, zenodo, caltechdata
|
|
29
|
-
from .
|
|
32
|
+
from .simulations import Simulation, Simulations
|
|
33
|
+
from .handlers import load, load_via_sxs_id, loadcontext, load_lvc
|
|
30
34
|
|
|
31
35
|
# The speed of light is, of course, defined to be exactly
|
|
32
36
|
speed_of_light = 299_792_458.0 # m/s
|
|
@@ -50,7 +54,3 @@ solar_mass_parameter = 1.32712440041e20 # m^3/s^2
|
|
|
50
54
|
# precision than is warranted by the measurement.
|
|
51
55
|
m_sun_in_meters = 1476.6250385063112526099633973363 # m
|
|
52
56
|
m_sun_in_seconds = 4.925490949162941425997992909269e-06 # s
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
doi_prefix = "10.26138"
|
|
56
|
-
doi_url = f"https://doi.org/{doi_prefix}/"
|
sxs/__version__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "
|
|
1
|
+
__version__ = "2024.0.0"
|
sxs/catalog/catalog.py
CHANGED
|
@@ -49,11 +49,20 @@ class Catalog(object):
|
|
|
49
49
|
|
|
50
50
|
"""
|
|
51
51
|
import json
|
|
52
|
-
import tempfile
|
|
53
52
|
import zipfile
|
|
54
53
|
from .. import sxs_directory, read_config
|
|
55
54
|
from ..utilities import download_file
|
|
56
55
|
|
|
56
|
+
from warnings import warn
|
|
57
|
+
deprecation_notice = """
|
|
58
|
+
|
|
59
|
+
You have called a function that uses the `Catalog` class,
|
|
60
|
+
which, as of `sxs` version 2024.0.0, has been deprecated in
|
|
61
|
+
favor of the `Simulations` interface. See the documentation
|
|
62
|
+
for more information.
|
|
63
|
+
"""
|
|
64
|
+
warn(deprecation_notice, DeprecationWarning)
|
|
65
|
+
|
|
57
66
|
progress = read_config("download_progress", True)
|
|
58
67
|
|
|
59
68
|
cache_path = sxs_directory("cache") / "catalog.zip"
|
sxs/handlers.py
CHANGED
|
@@ -1,7 +1,25 @@
|
|
|
1
1
|
"""Functions to facilitate generic handling of SXS-format data files"""
|
|
2
2
|
|
|
3
3
|
import contextlib
|
|
4
|
-
from . import waveforms
|
|
4
|
+
from . import waveforms, doi_url
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class JSONHandler:
|
|
8
|
+
"""Utility for loading and saving JSON files"""
|
|
9
|
+
@classmethod
|
|
10
|
+
def load(cls, file, **kwargs):
|
|
11
|
+
import json
|
|
12
|
+
if hasattr(file, "read"):
|
|
13
|
+
return json.load(file, **kwargs)
|
|
14
|
+
with open(file, "r") as f:
|
|
15
|
+
return json.load(f, **kwargs)
|
|
16
|
+
@classmethod
|
|
17
|
+
def save(cls, obj, file, **kwargs):
|
|
18
|
+
import json
|
|
19
|
+
if hasattr(file, "write"):
|
|
20
|
+
return json.dump(obj, file, **kwargs)
|
|
21
|
+
with open(file, "w") as f:
|
|
22
|
+
return json.dump(obj, f, **kwargs)
|
|
5
23
|
|
|
6
24
|
|
|
7
25
|
def sxs_handler(format_string):
|
|
@@ -42,6 +60,8 @@ def sxs_handler(format_string):
|
|
|
42
60
|
elif format_string.lower().startswith("waveforms"):
|
|
43
61
|
format_string = re.sub(r"^waveforms\.?", "", format_string, count=1, flags=re.IGNORECASE)
|
|
44
62
|
return waveforms.formats.get(format_string, waveforms.formats[None])
|
|
63
|
+
elif format_string.lower() == "json":
|
|
64
|
+
return JSONHandler
|
|
45
65
|
else:
|
|
46
66
|
format_list = [
|
|
47
67
|
catalog.formats,
|
|
@@ -97,12 +117,14 @@ def sxs_loader(file, group=None):
|
|
|
97
117
|
file_string = str(pathlib.Path(file).name).lower()
|
|
98
118
|
if "catalog" in file_string:
|
|
99
119
|
format_string = "catalog"
|
|
100
|
-
elif "metadata"
|
|
120
|
+
elif file_string.startswith("metadata"):
|
|
101
121
|
format_string = "metadata"
|
|
102
122
|
elif "horizons" in file_string:
|
|
103
123
|
format_string = "horizons"
|
|
104
124
|
elif re.match("(rh_|rhoverm_|rpsi4_|rmpsi4_)", file_string):
|
|
105
125
|
format_string = "waveforms"
|
|
126
|
+
elif file_string.endswith("json"):
|
|
127
|
+
format_string = "json"
|
|
106
128
|
else:
|
|
107
129
|
raise ValueError(f"File '{file}' contains no recognized format information")
|
|
108
130
|
handler = sxs_handler(format_string)
|
|
@@ -110,74 +132,111 @@ def sxs_loader(file, group=None):
|
|
|
110
132
|
return handler.load
|
|
111
133
|
|
|
112
134
|
|
|
113
|
-
def
|
|
135
|
+
def _safe_resolve_exists(path):
|
|
136
|
+
"""Evaluate `path.resolve().exists()` without throwing exception
|
|
137
|
+
|
|
138
|
+
This is just here to work around a bug that turned up in Windows
|
|
139
|
+
on python 3.8. It's not clear if it turns up in other versions.
|
|
140
|
+
"""
|
|
141
|
+
try:
|
|
142
|
+
return path.resolve().exists()
|
|
143
|
+
except:
|
|
144
|
+
return False
|
|
145
|
+
|
|
146
|
+
def load(location, download=None, cache=None, progress=None, truepath=None, **kwargs):
|
|
114
147
|
"""Load an SXS-format dataset, optionally downloading and caching
|
|
115
148
|
|
|
116
|
-
The dataset can be the full catalog of all SXS simulations, or
|
|
117
|
-
horizon data, or a waveform from an individual
|
|
149
|
+
The dataset can be the full catalog of all SXS simulations, or
|
|
150
|
+
metadata, horizon data, or a waveform from an individual
|
|
151
|
+
simulation.
|
|
118
152
|
|
|
119
153
|
Parameters
|
|
120
154
|
----------
|
|
121
155
|
location : {str, pathlib.Path}
|
|
122
|
-
A local file path, URL, SXS path, or SXS path pattern. See
|
|
156
|
+
A local file path, URL, SXS path, or SXS path pattern. See
|
|
157
|
+
Notes below.
|
|
123
158
|
download : {None, bool}, optional
|
|
124
|
-
If this is True and the data is recognized as starting with an
|
|
125
|
-
cannot be found in the cache, the data will be
|
|
126
|
-
If this is None (the default) and
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
159
|
+
If this is True and the data is recognized as starting with an
|
|
160
|
+
SXS ID but cannot be found in the cache, the data will be
|
|
161
|
+
downloaded automatically. If this is None (the default) and
|
|
162
|
+
an SXS configuration file is found with a `download` key, that
|
|
163
|
+
value will be used. If this is False, any configuration will
|
|
164
|
+
be ignored, and no files will be downloaded. Note that if
|
|
165
|
+
this is True but `cache` is None, `cache` will automatically
|
|
166
|
+
be switched to True.
|
|
131
167
|
cache : {None, bool}, optional
|
|
132
|
-
The cache directory is determined by `sxs.sxs_directory`, and
|
|
133
|
-
will be stored in that directory. If this is
|
|
134
|
-
`download` is True it will be set to
|
|
135
|
-
|
|
136
|
-
|
|
168
|
+
The cache directory is determined by `sxs.sxs_directory`, and
|
|
169
|
+
any downloads will be stored in that directory. If this is
|
|
170
|
+
None (the default) and `download` is True it will be set to
|
|
171
|
+
True. If this is False, any configuration will be ignored and
|
|
172
|
+
any files will be downloaded to a temporary directory that
|
|
173
|
+
will be deleted when python exits.
|
|
137
174
|
progress : {None, bool}, optional
|
|
138
|
-
If True, full file names will be shown and, if a nonzero
|
|
139
|
-
header is returned, a progress bar will be
|
|
140
|
-
Default is None, which just reads
|
|
141
|
-
`read_config("download_progress",
|
|
175
|
+
If True, full file names will be shown and, if a nonzero
|
|
176
|
+
Content-Length header is returned, a progress bar will be
|
|
177
|
+
shown during any downloads. Default is None, which just reads
|
|
178
|
+
the configuration value with `read_config("download_progress",
|
|
179
|
+
True)`, defaulting to True.
|
|
180
|
+
truepath : {None, str}, optional
|
|
181
|
+
If the file is downloaded, this allows the output path to be
|
|
182
|
+
overridden, rather than selected automatically. The output
|
|
183
|
+
path will be stored in `truepath` relative to the cache
|
|
184
|
+
directory.
|
|
142
185
|
|
|
143
186
|
Keyword Parameters
|
|
144
187
|
------------------
|
|
145
|
-
All remaining parameters are passed to the `load` function
|
|
146
|
-
requested data.
|
|
188
|
+
All remaining parameters are passed to the `load` function
|
|
189
|
+
responsible for the requested data.
|
|
147
190
|
|
|
148
191
|
See Also
|
|
149
192
|
--------
|
|
150
193
|
sxs.sxs_directory : Locate configuration and cache files
|
|
151
|
-
sxs.write_config : Set defaults for `download` and `cache`
|
|
194
|
+
sxs.write_config : Set defaults for `download` and `cache`
|
|
195
|
+
parameters
|
|
152
196
|
|
|
153
197
|
Notes
|
|
154
198
|
-----
|
|
155
199
|
This function can load data in various ways.
|
|
156
200
|
|
|
157
|
-
1) Given an absolute or relative path to a local file, it just
|
|
158
|
-
directly.
|
|
201
|
+
1) Given an absolute or relative path to a local file, it just
|
|
202
|
+
loads the data directly.
|
|
203
|
+
|
|
204
|
+
2) If `truepath` is set, and points to a file that exists —
|
|
205
|
+
whether absolute, relative to the current working directory,
|
|
206
|
+
or relative to the cache directory — that file will be
|
|
207
|
+
loaded.
|
|
159
208
|
|
|
160
|
-
|
|
161
|
-
it will be downloaded regardless of the
|
|
162
|
-
optionally cached.
|
|
209
|
+
3) If `location` is a valid URL including the scheme (https://,
|
|
210
|
+
or http://), it will be downloaded regardless of the
|
|
211
|
+
`download` parameter and optionally cached.
|
|
163
212
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
213
|
+
4) Given an SXS simulation specification — like "SXS:BBH:1234",
|
|
214
|
+
"SXS:BBH:1234v2.0", "SXS:BBH:1234/Lev5", or
|
|
215
|
+
"SXS:BBH:1234v2.0/Lev5" — the simulation is loaded as an
|
|
216
|
+
`sxs.Simulation` object.
|
|
167
217
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
218
|
+
5) Given an SXS path — like
|
|
219
|
+
"SXS:BBH:1234/Lev5/h_Extrapolated_N2.h5" — the file is
|
|
220
|
+
located in the catalog for details. This function then looks
|
|
221
|
+
in the local cache directory and loads it if present.
|
|
172
222
|
|
|
173
|
-
|
|
174
|
-
|
|
223
|
+
6) If the SXS path is not found in the cache directory and
|
|
224
|
+
`download` is set to `True` (when this function is called, or
|
|
225
|
+
in the sxs config file) this function attempts to download
|
|
226
|
+
the data. Note that `download` must be explicitly set in
|
|
227
|
+
this case, or a ValueError will be raised.
|
|
228
|
+
|
|
229
|
+
If the file is downloaded, it will be stored in the cache
|
|
230
|
+
according to the `location`, unless `truepath` is set as noted
|
|
231
|
+
above, in which case it is stored there. Note that downloading is
|
|
232
|
+
switched off by default, but if it is switched on (set to True),
|
|
233
|
+
the cache is also switched on by default.
|
|
175
234
|
|
|
176
235
|
"""
|
|
177
236
|
import pathlib
|
|
178
237
|
import urllib.request
|
|
179
|
-
from . import
|
|
180
|
-
from .utilities import url, download_file, sxs_path_to_system_path
|
|
238
|
+
from . import Simulations, Simulation, read_config, sxs_directory, Catalog
|
|
239
|
+
from .utilities import url, download_file, sxs_path_to_system_path, sxs_id_version_lev_exact_re
|
|
181
240
|
|
|
182
241
|
# Note: `download` and/or `cache` may still be `None` after this
|
|
183
242
|
if download is None:
|
|
@@ -197,26 +256,38 @@ def load(location, download=None, cache=None, progress=None, **kwargs):
|
|
|
197
256
|
json_path = path.with_suffix('.json')
|
|
198
257
|
|
|
199
258
|
if not path.exists():
|
|
200
|
-
if
|
|
259
|
+
if truepath and (testpath := pathlib.Path(truepath).expanduser()).exists():
|
|
260
|
+
path = testpath
|
|
261
|
+
|
|
262
|
+
elif truepath and (testpath := cache_path / truepath).exists():
|
|
263
|
+
path = testpath
|
|
264
|
+
|
|
265
|
+
elif _safe_resolve_exists(h5_path):
|
|
201
266
|
path = h5_path
|
|
202
267
|
|
|
203
|
-
elif json_path
|
|
268
|
+
elif _safe_resolve_exists(json_path):
|
|
204
269
|
path = json_path
|
|
205
270
|
|
|
206
271
|
elif "scheme" in url.parse(location):
|
|
207
272
|
m = url.parse(location)
|
|
208
|
-
|
|
209
|
-
path = cache_path /
|
|
273
|
+
truepath = truepath or urllib.request.url2pathname(f"{m['host']}/{m['port']}/{m['resource']}")
|
|
274
|
+
path = cache_path / truepath
|
|
210
275
|
if not path.resolve().exists():
|
|
211
276
|
if download is False: # Again, we want literal False, not casting to False
|
|
212
|
-
raise ValueError(f"File '{
|
|
277
|
+
raise ValueError(f"File '{truepath}' not found in cache, but downloading turned off")
|
|
213
278
|
download_file(location, path, progress=progress)
|
|
214
279
|
|
|
215
280
|
elif location == "catalog":
|
|
216
281
|
return Catalog.load(download=download)
|
|
217
282
|
|
|
283
|
+
elif location == "simulations":
|
|
284
|
+
return Simulations.load(download=download)
|
|
285
|
+
|
|
286
|
+
elif sxs_id_version_lev_exact_re.match(location):
|
|
287
|
+
return Simulation(location, download=download, cache=cache, progress=progress, **kwargs)
|
|
288
|
+
|
|
218
289
|
else:
|
|
219
|
-
# Try to find an appropriate SXS file
|
|
290
|
+
# Try to find an appropriate SXS file in the catalog
|
|
220
291
|
catalog = Catalog.load(download=download)
|
|
221
292
|
selections = catalog.select_files(location)
|
|
222
293
|
if not selections:
|
|
@@ -226,7 +297,7 @@ def load(location, download=None, cache=None, progress=None, **kwargs):
|
|
|
226
297
|
print(" " + "\n ".join(selections))
|
|
227
298
|
paths = []
|
|
228
299
|
for sxs_path, file_info in selections.items():
|
|
229
|
-
truepath = sxs_path_to_system_path(file_info.get("truepath", sxs_path))
|
|
300
|
+
truepath = truepath or sxs_path_to_system_path(file_info.get("truepath", sxs_path))
|
|
230
301
|
path = cache_path / truepath
|
|
231
302
|
if not path.resolve().exists():
|
|
232
303
|
download_url = file_info["download"]
|
|
@@ -243,6 +314,37 @@ def load(location, download=None, cache=None, progress=None, **kwargs):
|
|
|
243
314
|
return loader(path, **kwargs)
|
|
244
315
|
|
|
245
316
|
|
|
317
|
+
def load_via_sxs_id(sxsid, location, *, download=None, cache=None, progress=None, truepath=None, **kwargs):
|
|
318
|
+
"""Load a path via a (possibly versioned) SXS ID
|
|
319
|
+
|
|
320
|
+
Given some SXS ID like "SXS:BBH:1234" or a versioned ID like
|
|
321
|
+
"SXS:BBH:1234v2.0", we may wish to first resolve the DOI to a
|
|
322
|
+
specific Zenodo record, and then load a specific path under that
|
|
323
|
+
record — for example, we may want to export the Zenodo record as
|
|
324
|
+
JSON by appending "export/json" to the Zenodo URL, which we
|
|
325
|
+
*could* get as
|
|
326
|
+
|
|
327
|
+
load("https://zenodo.org/records/13152488/export/json")
|
|
328
|
+
|
|
329
|
+
because that's the record "SXS:BBH:1234v2.0" resolves to.
|
|
330
|
+
However, we don't want to keep track of the Zenodo URL, so we
|
|
331
|
+
just use this function instead, as
|
|
332
|
+
|
|
333
|
+
load_via_sxs_id("SXS:BBH:1234v2.0", "export/json")
|
|
334
|
+
|
|
335
|
+
"""
|
|
336
|
+
from pathlib import Path
|
|
337
|
+
import requests
|
|
338
|
+
from .utilities import sxs_path_to_system_path
|
|
339
|
+
url = f"{doi_url}{sxsid}"
|
|
340
|
+
response = requests.head(url, allow_redirects=True)
|
|
341
|
+
if response.status_code != 200:
|
|
342
|
+
raise ValueError(f"Could not load via DOI {url=}")
|
|
343
|
+
final_url = f"{response.url}/{location}"
|
|
344
|
+
truepath = truepath or Path(sxs_path_to_system_path(sxsid)) / location
|
|
345
|
+
return load(final_url, download, cache, progress, truepath, **kwargs)
|
|
346
|
+
|
|
347
|
+
|
|
246
348
|
@contextlib.contextmanager
|
|
247
349
|
def loadcontext(*args, **kwargs):
|
|
248
350
|
"""Context manager for backwards compatibility
|
sxs/horizons/__init__.py
CHANGED
|
@@ -308,7 +308,7 @@ class Horizons(object):
|
|
|
308
308
|
)
|
|
309
309
|
|
|
310
310
|
"""
|
|
311
|
-
from scipy.integrate import
|
|
311
|
+
from scipy.integrate import simpson
|
|
312
312
|
|
|
313
313
|
t = self.A.time
|
|
314
314
|
com = self.newtonian_com
|
|
@@ -319,8 +319,8 @@ class Horizons(object):
|
|
|
319
319
|
i_i, i_f = np.argmin(np.abs(t - t_i)), np.argmin(np.abs(t - t_f))
|
|
320
320
|
|
|
321
321
|
# Find the optimum analytically
|
|
322
|
-
com_0 =
|
|
323
|
-
com_1 =
|
|
322
|
+
com_0 = simpson(com[i_i : i_f + 1], x=t[i_i : i_f + 1], axis=0)
|
|
323
|
+
com_1 = simpson((t[:, np.newaxis] * com)[i_i : i_f + 1], x=t[i_i : i_f + 1], axis=0)
|
|
324
324
|
x_i = 2 * (com_0 * (2 * t_f ** 3 - 2 * t_i ** 3) + com_1 * (-3 * t_f ** 2 + 3 * t_i ** 2)) / (t_f - t_i) ** 4
|
|
325
325
|
v_i = 6 * (com_0 * (-t_f - t_i) + 2 * com_1) / (t_f - t_i) ** 3
|
|
326
326
|
|
sxs/juliapkg.json
CHANGED