sxs 2025.0.2__tar.gz → 2025.0.4__tar.gz
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-2025.0.2 → sxs-2025.0.4}/.github/workflows/build.yml +1 -1
- {sxs-2025.0.2 → sxs-2025.0.4}/CITATION.cff +2 -2
- {sxs-2025.0.2 → sxs-2025.0.4}/PKG-INFO +3 -2
- {sxs-2025.0.2 → sxs-2025.0.4}/pyproject.toml +3 -2
- sxs-2025.0.4/sxs/__version__.py +1 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/handlers.py +4 -2
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/simulations/__init__.py +1 -0
- sxs-2025.0.4/sxs/simulations/analyze.py +276 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/simulations/simulation.py +95 -21
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/waveforms/__init__.py +1 -1
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/waveforms/alignment.py +379 -55
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/waveforms/format_handlers/lvc.py +3 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/waveforms/memory.py +11 -41
- sxs-2025.0.4/sxs/waveforms/norms.py +270 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/waveforms/waveform_modes.py +84 -0
- sxs-2025.0.4/sxs/waveforms/waveform_mts.py +30 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/tests/conftest.py +30 -0
- sxs-2025.0.4/tests/test_alignment.py +93 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/tests/test_horizons.py +5 -11
- {sxs-2025.0.2 → sxs-2025.0.4}/tests/test_simulation.py +57 -0
- sxs-2025.0.2/sxs/__version__.py +0 -1
- {sxs-2025.0.2 → sxs-2025.0.4}/.codecov.yml +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/.github/dependabot.yml +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/.github/scripts/parse_bump_rule.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/.github/workflows/pr_rtd_link.yml +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/.gitignore +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/.readthedocs.yaml +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/LICENSE +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/README.md +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/docs/api/catalog.md +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/docs/api/horizons.md +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/docs/api/load.md +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/docs/api/metadata.md +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/docs/api/simulation.md +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/docs/api/simulations.md +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/docs/api/time_series.md +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/docs/api/waveforms.md +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/docs/html/main.html +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/docs/images/favicon.ico +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/docs/index.md +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/docs/javascript/mathjax.js +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/docs/julia.md +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/docs/mathematica.md +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/docs/stylesheets/extra.css +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/docs/tutorials/00-Introduction.ipynb +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/docs/tutorials/01-Simulations_and_Metadata.ipynb +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/docs/tutorials/02-Simulation.ipynb +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/docs/tutorials/03-Horizons.ipynb +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/docs/tutorials/04-Waveforms.ipynb +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/docs/tutorials/05-PreprocessingForFFTs.ipynb +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/mkdocs.yml +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/__init__.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/catalog/__init__.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/catalog/catalog.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/catalog/create.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/catalog/description.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/horizons/__init__.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/horizons/spec_horizons_h5.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/horizons/xor_multishuffle_bzip2.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/julia/GWFrames.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/julia/__init__.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/juliapkg.json +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/metadata/__init__.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/metadata/metadata.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/metadata/metric.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/simulations/local.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/simulations/simulations.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/time_series.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/__init__.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/bitwise.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/decimation/__init__.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/decimation/greedy_spline.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/decimation/linear_bisection.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/decimation/peak_greed.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/decimation/suppression.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/dicts.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/downloads.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/files.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/formats.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/inspire.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/lvcnr/__init__.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/lvcnr/comparisons.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/lvcnr/conversion.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/lvcnr/dataset.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/lvcnr/horizons.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/lvcnr/metadata.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/lvcnr/waveform_amp_phase.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/lvcnr/waveforms.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/monotonicity.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/pretty_print.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/references/__init__.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/references/ads.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/references/arxiv.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/references/fairchild_report.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/references/inspire.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/references/journal_abbreviations.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/references/references.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/select.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/smooth_functions.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/string_converters.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/sxs_directories.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/sxs_identifiers.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/utilities/url.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/waveforms/format_handlers/__init__.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/waveforms/format_handlers/grathena.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/waveforms/format_handlers/nrar.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/waveforms/format_handlers/rotating_paired_diff_multishuffle_bzip2.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/waveforms/format_handlers/rotating_paired_xor_multishuffle_bzip2.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/waveforms/format_handlers/spectre_cce_v1.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/waveforms/mode_utilities.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/waveforms/transformations.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/waveforms/waveform_grid.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/waveforms/waveform_mixin.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/waveforms/waveform_signal.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/zenodo/__init__.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/zenodo/api/__init__.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/zenodo/api/deposit.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/zenodo/api/login.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/zenodo/api/records.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/zenodo/catalog.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/zenodo/creators.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/zenodo/simannex.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/sxs/zenodo/surrogatemodeling.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/tests/__init__.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/tests/test_catalog.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/tests/test_julia.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/tests/test_loader.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/tests/test_metadata.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/tests/test_time_series.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/tests/test_transformations.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/tests/test_utilities.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/tests/test_waveform_rotations.py +0 -0
- {sxs-2025.0.2 → sxs-2025.0.4}/tests/test_waveforms.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sxs
|
|
3
|
-
Version: 2025.0.
|
|
3
|
+
Version: 2025.0.4
|
|
4
4
|
Summary: Interface to data produced by the Simulating eXtreme Spacetimes collaboration
|
|
5
5
|
Project-URL: Homepage, https://github.com/sxs-collaboration/sxs
|
|
6
6
|
Project-URL: Documentation, https://sxs.readthedocs.io/
|
|
@@ -40,13 +40,14 @@ Requires-Dist: inflection>=0.5.1
|
|
|
40
40
|
Requires-Dist: juliacall>=0.9.20
|
|
41
41
|
Requires-Dist: numba>=0.55; implementation_name == 'cpython'
|
|
42
42
|
Requires-Dist: numpy>=1.25
|
|
43
|
+
Requires-Dist: packaging
|
|
43
44
|
Requires-Dist: pandas>=1.1.2
|
|
44
45
|
Requires-Dist: pytz>=2020.1
|
|
45
46
|
Requires-Dist: quaternionic>=1.0.15
|
|
46
47
|
Requires-Dist: requests>=2.24.0
|
|
47
48
|
Requires-Dist: scipy>=1.13
|
|
48
49
|
Requires-Dist: spherical>=1.0.15
|
|
49
|
-
Requires-Dist: sxscatalog>=3.0.
|
|
50
|
+
Requires-Dist: sxscatalog>=3.0.12
|
|
50
51
|
Requires-Dist: tqdm>=4.63.1
|
|
51
52
|
Requires-Dist: urllib3>=1.25.10
|
|
52
53
|
Provides-Extra: docs
|
|
@@ -18,7 +18,7 @@ classifiers = [
|
|
|
18
18
|
"Topic :: Scientific/Engineering :: Astronomy"
|
|
19
19
|
]
|
|
20
20
|
dependencies = [
|
|
21
|
-
"sxscatalog >=3.0.
|
|
21
|
+
"sxscatalog >=3.0.12",
|
|
22
22
|
"numpy >=1.25",
|
|
23
23
|
"scipy >=1.13",
|
|
24
24
|
"numba >=0.55; implementation_name == 'cpython'",
|
|
@@ -31,7 +31,8 @@ dependencies = [
|
|
|
31
31
|
"pytz >=2020.1",
|
|
32
32
|
"urllib3 >=1.25.10",
|
|
33
33
|
"pandas >=1.1.2",
|
|
34
|
-
"juliacall >=0.9.20"
|
|
34
|
+
"juliacall >=0.9.20",
|
|
35
|
+
"packaging",
|
|
35
36
|
]
|
|
36
37
|
|
|
37
38
|
[project.optional-dependencies]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2025.0.4"
|
|
@@ -350,7 +350,9 @@ def load(location, download=None, cache=None, progress=None, truepath=None, **kw
|
|
|
350
350
|
return loaded
|
|
351
351
|
|
|
352
352
|
|
|
353
|
-
def load_via_sxs_id(
|
|
353
|
+
def load_via_sxs_id(
|
|
354
|
+
sxsid, location, *, download=None, cache=None, progress=None, truepath=None, timeout=30, **kwargs
|
|
355
|
+
):
|
|
354
356
|
"""Load a path via a (possibly versioned) SXS ID
|
|
355
357
|
|
|
356
358
|
Given some SXS ID like "SXS:BBH:1234" or a versioned ID like
|
|
@@ -373,7 +375,7 @@ def load_via_sxs_id(sxsid, location, *, download=None, cache=None, progress=None
|
|
|
373
375
|
import requests
|
|
374
376
|
from .utilities import sxs_path_to_system_path
|
|
375
377
|
url = f"{doi_url}{sxsid}"
|
|
376
|
-
response = requests.head(url, allow_redirects=True)
|
|
378
|
+
response = requests.head(url, allow_redirects=True, timeout=timeout)
|
|
377
379
|
if response.status_code != 200:
|
|
378
380
|
raise ValueError(f"Could not load via DOI {url=}")
|
|
379
381
|
final_url = f"{response.url}/{location}"
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import multiprocessing
|
|
2
|
+
|
|
3
|
+
from ..waveforms.norms import create_unified_waveforms, L2_difference, mismatch
|
|
4
|
+
from ..waveforms.alignment import align_waveforms, align_simulations
|
|
5
|
+
from ..handlers import load
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def compute_error_summary(wa, wb, t1, t2, modes=None, ASDs=None, total_masses=None):
|
|
11
|
+
"""
|
|
12
|
+
Compute various errors between two waveforms.
|
|
13
|
+
|
|
14
|
+
This computes the time-domain mismatch over the two-sphere,
|
|
15
|
+
the normalized L² norm of the residual, the frequency-domain mismatch
|
|
16
|
+
against whatever detector ASDs are provided with some total mass,
|
|
17
|
+
and the individual modes' residual norms and norms.
|
|
18
|
+
|
|
19
|
+
Parameters
|
|
20
|
+
----------
|
|
21
|
+
wa : WaveformModes
|
|
22
|
+
wb : WaveformModes
|
|
23
|
+
t1 : float
|
|
24
|
+
Beginning of integrals.
|
|
25
|
+
t2 : float
|
|
26
|
+
End of integrals.
|
|
27
|
+
modes : list, optional
|
|
28
|
+
Modes (ell, m) to include in error calculations.
|
|
29
|
+
Default is all modes.
|
|
30
|
+
ASDs : dict of funcs, optional
|
|
31
|
+
Dictionary of functions mapping frequencies to the ASD of a detector(s).
|
|
32
|
+
Default is no frequency-domain mismatch is calculated.
|
|
33
|
+
total_masses : list of floats, optional
|
|
34
|
+
Total masses in solar masses to use for frequency-domain mismatches.
|
|
35
|
+
Default is 1.
|
|
36
|
+
|
|
37
|
+
Returns
|
|
38
|
+
-------
|
|
39
|
+
errors : dict
|
|
40
|
+
Dictionary of the time-domain mismatch over the two-sphere,
|
|
41
|
+
the normalized L² norm of the residual, the frequency-domain mismatch
|
|
42
|
+
against whatever detector ASDs are provided with some total mass,
|
|
43
|
+
and the modes' absolute errors and norms.
|
|
44
|
+
"""
|
|
45
|
+
from .. import m_sun_in_seconds
|
|
46
|
+
|
|
47
|
+
errors = {}
|
|
48
|
+
|
|
49
|
+
errors["t1"] = t1
|
|
50
|
+
errors["t2"] = t2
|
|
51
|
+
|
|
52
|
+
wa, wb = create_unified_waveforms(wa, wb, t1, t2, padding_time_factor=0)
|
|
53
|
+
|
|
54
|
+
errors["mismatch"] = mismatch(wa, wb, t1, t2, modes=modes)
|
|
55
|
+
|
|
56
|
+
errors["residual L2 norm"] = L2_difference(wa, wb, t1, t2, modes=modes)
|
|
57
|
+
|
|
58
|
+
i1 = wa.index_closest_to(t1)
|
|
59
|
+
i0, i2 = max(0, i1-5), min(i1+6, wa.n_times-1)
|
|
60
|
+
Ω1 = np.linalg.norm(wa[i0:i2].angular_velocity, axis=1)[5]
|
|
61
|
+
f1 = 2*Ω1 / (2*np.pi)
|
|
62
|
+
|
|
63
|
+
wa = wa.preprocess(t1, t1 + 0.01 * (t2 - t1), t2 - (t2 - t1) * 0.01, t2)
|
|
64
|
+
wb = wb.preprocess(t1, t1 + 0.01 * (t2 - t1), t2 - (t2 - t1) * 0.01, t2)
|
|
65
|
+
|
|
66
|
+
wa_tilde = wa.fourier_transform()
|
|
67
|
+
wb_tilde = wb.fourier_transform()
|
|
68
|
+
if ASDs is not None:
|
|
69
|
+
if total_masses is None:
|
|
70
|
+
raise ValueError("Need to specify total masses if ASDs are provided.")
|
|
71
|
+
for ASD in ASDs:
|
|
72
|
+
for total_mass in total_masses:
|
|
73
|
+
# Note that we only need to make the frequency unitful, since the
|
|
74
|
+
# magnitude of the strain scales out in the mismatch.
|
|
75
|
+
# We make things unitful here because the fourier transform
|
|
76
|
+
# earlier was called outside of the for loop without a total mass
|
|
77
|
+
# so that it doesn't need to be computed at each iteration.
|
|
78
|
+
frequency_factor = 1 / (total_mass * m_sun_in_seconds)
|
|
79
|
+
|
|
80
|
+
wa_tilde_total_mass = wa_tilde.copy()
|
|
81
|
+
wb_tilde_total_mass = wb_tilde.copy()
|
|
82
|
+
wa_tilde_total_mass.t = wa_tilde_total_mass.t * frequency_factor
|
|
83
|
+
wb_tilde_total_mass.t = wb_tilde_total_mass.t * frequency_factor
|
|
84
|
+
|
|
85
|
+
errors[f"mismatch {ASD} {total_mass}"] = mismatch(
|
|
86
|
+
wa_tilde_total_mass, wb_tilde_total_mass, f1 * frequency_factor, modes=modes, ASD=ASDs[ASD]
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
ell_min = max(wa.ell_min, wb.ell_min)
|
|
90
|
+
ell_max = min(wa.ell_max, wb.ell_max)
|
|
91
|
+
for L in range(ell_min, ell_max + 1):
|
|
92
|
+
for M in range(-L, L + 1):
|
|
93
|
+
absolute_error, norm = L2_difference(
|
|
94
|
+
wa, wb, t1, t2, modes=[(L, M)], modes_for_norm=[(L, M)], normalize=False
|
|
95
|
+
)
|
|
96
|
+
errors[f"(L, M) = {(L, M)} residual L2 norm"] = absolute_error
|
|
97
|
+
errors[f"(L, M) = {(L, M)} L2 norm"] = norm
|
|
98
|
+
|
|
99
|
+
return errors
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def analyze_simulation(
|
|
103
|
+
sim_name,
|
|
104
|
+
analyze_levs=True,
|
|
105
|
+
analyze_extrapolation=True,
|
|
106
|
+
analyze_psi4=True,
|
|
107
|
+
ASDs=None,
|
|
108
|
+
total_masses=None,
|
|
109
|
+
nprocs=None,
|
|
110
|
+
):
|
|
111
|
+
"""
|
|
112
|
+
Analyze a simulation's waveform agreement across
|
|
113
|
+
Levs, extrapolation orders, and the psi4 constraint.
|
|
114
|
+
|
|
115
|
+
For each Lev in a simulation, align the strain waveforms
|
|
116
|
+
(if it's the highest Lev pair, then a 4d optimization is also performed
|
|
117
|
+
and the transformation from the low to high lev is included in the returned dictionary)
|
|
118
|
+
via the independent alignment method. Then, compute the L² norm of the residual between
|
|
119
|
+
the two aligned waveforms (if it's the highest Lev pair, then the
|
|
120
|
+
mismatch against the ASDs and total masses is also computed, as well as the
|
|
121
|
+
individual modes' residual norms and norms). For the
|
|
122
|
+
extrapolation order and psi4 analyses, only the L² norm of the residuals are computed.
|
|
123
|
+
|
|
124
|
+
Parameters
|
|
125
|
+
----------
|
|
126
|
+
sim_name : str
|
|
127
|
+
Simulation name, e.g., "SXS:BBH:2092".
|
|
128
|
+
analyze_levs : bool, optional
|
|
129
|
+
Whether or not to analyze the various Levs.
|
|
130
|
+
Default is True.
|
|
131
|
+
analyze_extrapolation : bool, optional
|
|
132
|
+
Whether or not to analyze the various extrapolation orders.
|
|
133
|
+
Default is True.
|
|
134
|
+
analyze_psi4 : bool, optional
|
|
135
|
+
Whether or not to analyze the psi4 constraint -h.ddot = psi4..
|
|
136
|
+
Default is True.
|
|
137
|
+
ASDs : dict of funcs, optional
|
|
138
|
+
Dictionary of functions mapping frequencies to the ASD of a detector(s).
|
|
139
|
+
Default is no frequency-domain mismatch is calculated.
|
|
140
|
+
total_masses : list of floats, optional
|
|
141
|
+
Total masses in solar masses to use for frequency-domain mismatches.
|
|
142
|
+
Default is 1.
|
|
143
|
+
nprocs : int, optional
|
|
144
|
+
Number of cpus to use. Default is maximum number. If -1 is provided,
|
|
145
|
+
then no multiprocessing is performed.
|
|
146
|
+
|
|
147
|
+
Returns
|
|
148
|
+
-------
|
|
149
|
+
errors : dict
|
|
150
|
+
Dictionary of the errors described above.
|
|
151
|
+
"""
|
|
152
|
+
errors = {}
|
|
153
|
+
|
|
154
|
+
sim = load(sim_name)
|
|
155
|
+
|
|
156
|
+
# Lev analysis
|
|
157
|
+
if analyze_levs:
|
|
158
|
+
for i, lev in enumerate(sim.lev_numbers[1:][::-1]):
|
|
159
|
+
sim_low_lev = load(f"{sim_name}/Lev{lev - 1}")
|
|
160
|
+
sim_high_lev = load(f"{sim_name}/Lev{lev}")
|
|
161
|
+
|
|
162
|
+
w_high_lev = sim_high_lev.h
|
|
163
|
+
if i == 0:
|
|
164
|
+
w_low_lev_prime, transformation, L2_norm, t1, t2 = align_simulations(
|
|
165
|
+
sim_low_lev, sim_high_lev, alignment_method="4d", nprocs=nprocs
|
|
166
|
+
)
|
|
167
|
+
errors[f"(Lev{lev - 1}, Lev{lev}) 4d"] = compute_error_summary(w_low_lev_prime, w_high_lev, t1, t2)
|
|
168
|
+
errors[f"(Lev{lev - 1}, Lev{lev}) 4d transformation"] = transformation
|
|
169
|
+
|
|
170
|
+
w_low_lev_prime, transformation, _, t1, t2 = align_simulations(
|
|
171
|
+
sim_low_lev, sim_high_lev, alignment_method="independent alignment", nprocs=nprocs
|
|
172
|
+
)
|
|
173
|
+
errors[f"(Lev{lev - 1}, Lev{lev})"] = L2_difference(w_high_lev, w_low_lev_prime, t1, t2)
|
|
174
|
+
|
|
175
|
+
# Extrapolation order analysis
|
|
176
|
+
if analyze_extrapolation:
|
|
177
|
+
for extrapolation in ["N3", "N4", "Outer"]:
|
|
178
|
+
other = load(sim_name, extrapolation=extrapolation)
|
|
179
|
+
|
|
180
|
+
w_n2 = sim.h
|
|
181
|
+
w_other = other.h
|
|
182
|
+
|
|
183
|
+
t1 = sim.metadata.relaxation_time
|
|
184
|
+
|
|
185
|
+
w_other_prime, transformation, _, t1, t2 = align_waveforms(
|
|
186
|
+
w_other, w_n2, t1, alignment_method="independent alignment", nprocs=nprocs
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
L2_norm = L2_difference(w_n2, w_other_prime, t1, t2)
|
|
190
|
+
|
|
191
|
+
errors[f"(N2, {extrapolation})"] = L2_norm
|
|
192
|
+
|
|
193
|
+
# Psi4 analysis
|
|
194
|
+
if analyze_psi4:
|
|
195
|
+
h_as_psi4 = -sim.h.ddot
|
|
196
|
+
psi4 = sim.psi4
|
|
197
|
+
|
|
198
|
+
t1 = sim.metadata.relaxation_time
|
|
199
|
+
t2 = h_as_psi4.t[-1]
|
|
200
|
+
|
|
201
|
+
L2_norm = L2_difference(h_as_psi4, psi4, t1, t2)
|
|
202
|
+
|
|
203
|
+
errors[f"(-h.ddot, psi4)"] = L2_norm
|
|
204
|
+
|
|
205
|
+
return errors
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def analyze_simulations(
|
|
209
|
+
sim_names,
|
|
210
|
+
analyze_levs=True,
|
|
211
|
+
analyze_extrapolation=True,
|
|
212
|
+
analyze_psi4=True,
|
|
213
|
+
ASDs=None,
|
|
214
|
+
total_masses=None,
|
|
215
|
+
nprocs=None,
|
|
216
|
+
):
|
|
217
|
+
"""
|
|
218
|
+
Analyze simulations' waveform agreement across
|
|
219
|
+
Levs, extrapolation orders, and the psi4 constraint.
|
|
220
|
+
|
|
221
|
+
For each Lev in a simulation, align the strain waveforms
|
|
222
|
+
(if it's the highest Lev pair, then a 4d optimization is also performed
|
|
223
|
+
and the transformation from the low to high lev is included in the returned dictionary)
|
|
224
|
+
via the independent alignment method. Then, compute mismatches between
|
|
225
|
+
the two aligned waveforms (if it's the highest Lev pair, then the
|
|
226
|
+
relative L² norm error over the two-sphere is computed, as well as the
|
|
227
|
+
individual mode absolute errors and norms). For the
|
|
228
|
+
extrapolation order and psi4 analyses, only mismatches are computed.
|
|
229
|
+
|
|
230
|
+
Parameters
|
|
231
|
+
----------
|
|
232
|
+
sim_names : list of strs
|
|
233
|
+
Simulation names, e.g., ["SXS:BBH:2092"].
|
|
234
|
+
analyze_levs : bool, optional
|
|
235
|
+
Whether or not to analyze the various Levs.
|
|
236
|
+
Default is True.
|
|
237
|
+
analyze_extrapolation : bool, optional
|
|
238
|
+
Whether or not to analyze the various extrapolation orders.
|
|
239
|
+
Default is True.
|
|
240
|
+
analyze_psi4 : bool, optional
|
|
241
|
+
Whether or not to analyze the psi4 constraint -h.ddot = psi4..
|
|
242
|
+
Default is True.
|
|
243
|
+
ASDs : dict of funcs, optional
|
|
244
|
+
Dictionary of functions mapping frequencies to the ASD of a detector(s).
|
|
245
|
+
Default is no frequency-domain mismatch is calculated.
|
|
246
|
+
total_masses : list of floats, optional
|
|
247
|
+
Total masses in solar masses to use for frequency-domain mismatches.
|
|
248
|
+
Default is 1.
|
|
249
|
+
nprocs : int, optional
|
|
250
|
+
Number of cpus to use. Default is maximum number. If -1 is provided,
|
|
251
|
+
then no multiprocessing is performed.
|
|
252
|
+
|
|
253
|
+
Returns
|
|
254
|
+
-------
|
|
255
|
+
errors : dict
|
|
256
|
+
Dictionary of the errors described above.
|
|
257
|
+
"""
|
|
258
|
+
errors = {}
|
|
259
|
+
if nprocs != -1:
|
|
260
|
+
with multiprocessing.Pool(processes=nprocs) as pool:
|
|
261
|
+
results = pool.starmap(
|
|
262
|
+
analyze_simulation,
|
|
263
|
+
[
|
|
264
|
+
(sim_name, analyze_levs, analyze_extrapolation, analyze_psi4, ASDs, total_masses, -1)
|
|
265
|
+
for sim_name in sim_names
|
|
266
|
+
],
|
|
267
|
+
)
|
|
268
|
+
for i, sim_name in enumerate(sim_names):
|
|
269
|
+
errors[sim_name] = results[i]
|
|
270
|
+
else:
|
|
271
|
+
for sim_name in sim_names:
|
|
272
|
+
errors[sim_name] = analyze_simulation(
|
|
273
|
+
sim_name, analyze_levs, analyze_extrapolation, analyze_psi4, ASDs, total_masses, -1
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
return errors
|
|
@@ -100,18 +100,22 @@ def Simulation(location, *args, **kwargs):
|
|
|
100
100
|
arguments other than those listed above.
|
|
101
101
|
|
|
102
102
|
"""
|
|
103
|
+
import numpy as np
|
|
104
|
+
from packaging.version import Version
|
|
103
105
|
from .. import load, sxs_directory
|
|
104
106
|
from ..metadata.metric import MetadataMetric
|
|
105
107
|
|
|
106
108
|
# Load the simulation catalog
|
|
107
109
|
simulations = load("simulations")
|
|
110
|
+
v = Version(simulations.tag)
|
|
111
|
+
latest_version = f"v{v.major}.{v.minor}"
|
|
108
112
|
|
|
109
113
|
# Extract the simulation ID, version, and Lev from the location string
|
|
110
114
|
simulation_id, input_version = sxs_id_and_version(location)
|
|
111
115
|
if not simulation_id:
|
|
112
116
|
if location.split("/Lev")[0] in simulations:
|
|
113
117
|
simulation_id = location.split("/Lev")[0]
|
|
114
|
-
input_version =
|
|
118
|
+
input_version = latest_version
|
|
115
119
|
else:
|
|
116
120
|
raise ValueError(f"Invalid SXS ID in '{simulation_id}'")
|
|
117
121
|
input_lev_number = lev_number(location) # Will be `None` if not present
|
|
@@ -132,8 +136,8 @@ def Simulation(location, *args, **kwargs):
|
|
|
132
136
|
|
|
133
137
|
# Check if the specified version exists in the simulation catalog
|
|
134
138
|
if not hasattr(metadata, "DOI_versions"):
|
|
135
|
-
input_version =
|
|
136
|
-
if input_version !=
|
|
139
|
+
input_version = latest_version
|
|
140
|
+
if input_version != latest_version and input_version not in metadata.DOI_versions:
|
|
137
141
|
raise ValueError(f"Version '{input_version}' not found in simulation catalog for '{simulation_id}'")
|
|
138
142
|
|
|
139
143
|
# Set various pieces of information about the simulation
|
|
@@ -206,17 +210,31 @@ def Simulation(location, *args, **kwargs):
|
|
|
206
210
|
# Note the deprecation status in the kwargs, even if ignoring deprecation
|
|
207
211
|
kwargs["deprecated"] = deprecated
|
|
208
212
|
|
|
209
|
-
# TODO: Default to not downloading file info
|
|
210
|
-
# TODO: In that case, deal with Lev numbers somehow
|
|
211
|
-
|
|
212
213
|
# We want to do this *after* deprecation checking, to avoid possibly unnecessary web requests
|
|
214
|
+
if 1 <= float(version[1:]) < 3.0 and "files" in metadata:
|
|
215
|
+
# The simulation metadata is points to files with a different version
|
|
216
|
+
del metadata["files"]
|
|
213
217
|
files = get_file_info(metadata, sxs_id, download=kwargs.get("download_file_info", None))
|
|
214
218
|
|
|
215
219
|
# If Lev is given as part of `location`, use it; otherwise, use the highest available
|
|
216
220
|
lev_numbers = sorted({lev for f in files if (lev:=lev_number(f))})
|
|
217
|
-
|
|
221
|
+
if input_lev_number is not None and lev_numbers:
|
|
222
|
+
if input_lev_number not in lev_numbers:
|
|
223
|
+
raise ValueError(
|
|
224
|
+
f"Lev number '{input_lev_number}' not found in simulation files for {sxs_id}"
|
|
225
|
+
)
|
|
226
|
+
max_lev_number = max(lev_numbers, default=np.nan)
|
|
227
|
+
output_lev_number = input_lev_number or max_lev_number
|
|
218
228
|
location = f"{sxs_id_stem}{version}/Lev{output_lev_number}"
|
|
219
229
|
|
|
230
|
+
# Keep the metadata around unless we're asking for an old version
|
|
231
|
+
# or a less-than-maximal Lev
|
|
232
|
+
if (
|
|
233
|
+
version != latest_version
|
|
234
|
+
or (lev_numbers and output_lev_number != max_lev_number)
|
|
235
|
+
):
|
|
236
|
+
metadata = None
|
|
237
|
+
|
|
220
238
|
# Finally, figure out which version of the simulation to load and dispatch
|
|
221
239
|
version_number = float(version[1:])
|
|
222
240
|
if 1 <= version_number < 2.0:
|
|
@@ -227,7 +245,7 @@ def Simulation(location, *args, **kwargs):
|
|
|
227
245
|
sim = Simulation_v2(
|
|
228
246
|
metadata, series, version, sxs_id_stem, sxs_id, url, files, lev_numbers, output_lev_number, location, *args, **kwargs
|
|
229
247
|
)
|
|
230
|
-
elif 3 <= version_number < 4.0 or version ==
|
|
248
|
+
elif 3 <= version_number < 4.0 or version == latest_version:
|
|
231
249
|
sim = Simulation_v3(
|
|
232
250
|
metadata, series, version, sxs_id_stem, sxs_id, url, files, lev_numbers, output_lev_number, location, *args, **kwargs
|
|
233
251
|
)
|
|
@@ -245,8 +263,9 @@ class SimulationBase:
|
|
|
245
263
|
|
|
246
264
|
Attributes
|
|
247
265
|
----------
|
|
248
|
-
metadata : Metadata
|
|
249
|
-
Metadata object for the simulation
|
|
266
|
+
metadata : Metadata or None
|
|
267
|
+
Metadata object for the simulation. If `None`, the metadata
|
|
268
|
+
will be loaded automatically.
|
|
250
269
|
series : pandas.Series
|
|
251
270
|
The metadata, as extracted from the `simulations.dataframe`,
|
|
252
271
|
meaning that it has columns consistent with other simulations,
|
|
@@ -307,7 +326,6 @@ class SimulationBase:
|
|
|
307
326
|
metadata, series, version, sxs_id_stem, sxs_id, url, files, lev_numbers, lev_number, location,
|
|
308
327
|
*args, **kwargs
|
|
309
328
|
):
|
|
310
|
-
self.metadata = metadata
|
|
311
329
|
self.series = series
|
|
312
330
|
self.version = version
|
|
313
331
|
self.sxs_id_stem = sxs_id_stem
|
|
@@ -318,6 +336,7 @@ class SimulationBase:
|
|
|
318
336
|
self.lev_number = lev_number
|
|
319
337
|
self.location = location
|
|
320
338
|
self.deprecated = kwargs.get("deprecated", False)
|
|
339
|
+
self.metadata = metadata or self.load_metadata()
|
|
321
340
|
|
|
322
341
|
def __repr__(self):
|
|
323
342
|
chi1 = self.series["reference_dimensionless_spin1"]
|
|
@@ -400,7 +419,7 @@ class SimulationBase:
|
|
|
400
419
|
warning_threshold : float, optional
|
|
401
420
|
Threshold distance above which a warning will be issued
|
|
402
421
|
that the closest simulation is fairly distant. Default is
|
|
403
|
-
1e-
|
|
422
|
+
1e-2.
|
|
404
423
|
|
|
405
424
|
Returns
|
|
406
425
|
-------
|
|
@@ -430,12 +449,34 @@ class SimulationBase:
|
|
|
430
449
|
|
|
431
450
|
@property
|
|
432
451
|
def lev(self):
|
|
433
|
-
|
|
452
|
+
if self.lev_number is None:
|
|
453
|
+
return ""
|
|
454
|
+
else:
|
|
455
|
+
return f"Lev{self.lev_number}"
|
|
434
456
|
|
|
435
457
|
@property
|
|
436
458
|
def Lev(self):
|
|
437
459
|
return self.lev
|
|
438
460
|
|
|
461
|
+
@property
|
|
462
|
+
def metadata_path(self):
|
|
463
|
+
for separator in [":", "/"]:
|
|
464
|
+
for ending in [".json", ".txt"]:
|
|
465
|
+
prefix = f"{self.lev}{separator}" if self.lev else ""
|
|
466
|
+
if (fn := f"{prefix}metadata{ending}") in self.files:
|
|
467
|
+
return fn
|
|
468
|
+
raise ValueError(
|
|
469
|
+
f"Metadata file not found in simulation files for {self.location}"
|
|
470
|
+
)
|
|
471
|
+
|
|
472
|
+
def load_metadata(self):
|
|
473
|
+
from .. import load
|
|
474
|
+
metadata_path = self.metadata_path
|
|
475
|
+
metadata_location = self.files.get(metadata_path)["link"]
|
|
476
|
+
sxs_id_path = Path(self.sxs_id)
|
|
477
|
+
metadata_truepath = Path(sxs_path_to_system_path(sxs_id_path / metadata_path))
|
|
478
|
+
return Metadata(load(metadata_location, truepath=metadata_truepath))
|
|
479
|
+
|
|
439
480
|
def load_horizons(self):
|
|
440
481
|
from .. import load
|
|
441
482
|
sxs_id_path = Path(self.sxs_id)
|
|
@@ -717,7 +758,8 @@ class Simulation_v1(SimulationBase):
|
|
|
717
758
|
|
|
718
759
|
@property
|
|
719
760
|
def horizons_path(self):
|
|
720
|
-
|
|
761
|
+
prefix = f"{self.lev}/" if self.lev else ""
|
|
762
|
+
return f"{prefix}Horizons.h5"
|
|
721
763
|
|
|
722
764
|
def load_horizons(self):
|
|
723
765
|
from .. import load
|
|
@@ -739,25 +781,27 @@ class Simulation_v1(SimulationBase):
|
|
|
739
781
|
|
|
740
782
|
@property
|
|
741
783
|
def strain_path(self):
|
|
784
|
+
prefix = f"{self.lev}/" if self.lev else ""
|
|
742
785
|
extrapolation = (
|
|
743
786
|
f"Extrapolated_{self.extrapolation}.dir"
|
|
744
787
|
if self.extrapolation != "Outer"
|
|
745
788
|
else "OutermostExtraction.dir"
|
|
746
789
|
)
|
|
747
790
|
return (
|
|
748
|
-
f"{
|
|
791
|
+
f"{prefix}rhOverM_Asymptotic_GeometricUnits_CoM.h5",
|
|
749
792
|
extrapolation
|
|
750
793
|
)
|
|
751
794
|
|
|
752
795
|
@property
|
|
753
796
|
def psi4_path(self):
|
|
797
|
+
prefix = f"{self.lev}/" if self.lev else ""
|
|
754
798
|
extrapolation = (
|
|
755
799
|
f"Extrapolated_{self.extrapolation}.dir"
|
|
756
800
|
if self.extrapolation != "Outer"
|
|
757
801
|
else "OutermostExtraction.dir"
|
|
758
802
|
)
|
|
759
803
|
return (
|
|
760
|
-
f"{
|
|
804
|
+
f"{prefix}rMPsi4_Asymptotic_GeometricUnits_CoM.h5",
|
|
761
805
|
extrapolation
|
|
762
806
|
)
|
|
763
807
|
|
|
@@ -795,18 +839,23 @@ class Simulation_v2(SimulationBase):
|
|
|
795
839
|
also `SimulationBase` for the base class that this class inherits
|
|
796
840
|
from.
|
|
797
841
|
"""
|
|
842
|
+
# Default extrapolation order for this simulation version
|
|
843
|
+
default_extrapolation = "N2"
|
|
844
|
+
|
|
798
845
|
def __init__(self, *args, **kwargs):
|
|
799
846
|
super().__init__(*args, **kwargs)
|
|
800
|
-
self.extrapolation = kwargs.get("extrapolation",
|
|
847
|
+
self.extrapolation = kwargs.get("extrapolation", self.default_extrapolation)
|
|
801
848
|
|
|
802
849
|
@property
|
|
803
850
|
def horizons_path(self):
|
|
804
|
-
|
|
851
|
+
prefix = f"{self.lev}:" if self.lev else ""
|
|
852
|
+
return f"{prefix}Horizons.h5"
|
|
805
853
|
|
|
806
854
|
@property
|
|
807
855
|
def strain_path(self):
|
|
856
|
+
prefix = f"{self.lev}:" if self.lev else ""
|
|
808
857
|
return (
|
|
809
|
-
f"{
|
|
858
|
+
f"{prefix}Strain_{self.extrapolation}",
|
|
810
859
|
"/"
|
|
811
860
|
)
|
|
812
861
|
|
|
@@ -817,8 +866,9 @@ class Simulation_v2(SimulationBase):
|
|
|
817
866
|
if self.extrapolation != "Outer"
|
|
818
867
|
else "OutermostExtraction.dir"
|
|
819
868
|
)
|
|
869
|
+
prefix = f"{self.lev}:" if self.lev else ""
|
|
820
870
|
return (
|
|
821
|
-
f"{
|
|
871
|
+
f"{prefix}ExtraWaveforms",
|
|
822
872
|
f"/rMPsi4_Asymptotic_GeometricUnits_CoM_Mem/{extrapolation}"
|
|
823
873
|
)
|
|
824
874
|
|
|
@@ -846,7 +896,31 @@ class Simulation_v2(SimulationBase):
|
|
|
846
896
|
|
|
847
897
|
|
|
848
898
|
class Simulation_v3(Simulation_v2):
|
|
849
|
-
|
|
899
|
+
# Default extrapolation order for this simulation version
|
|
900
|
+
default_extrapolation = "N2"
|
|
901
|
+
|
|
902
|
+
def __init__(self, *args, **kwargs):
|
|
903
|
+
super().__init__(*args, **kwargs)
|
|
904
|
+
self.extrapolation = kwargs.get("extrapolation", self.default_extrapolation)
|
|
905
|
+
|
|
906
|
+
@property
|
|
907
|
+
def strain_path(self):
|
|
908
|
+
prefix = f"{self.lev}:" if self.lev else ""
|
|
909
|
+
return (
|
|
910
|
+
f"{prefix}Strain_{self.extrapolation}",
|
|
911
|
+
"/"
|
|
912
|
+
) if self.extrapolation == self.default_extrapolation else (
|
|
913
|
+
f"{prefix}ExtraWaveforms",
|
|
914
|
+
f"/Strain_{self.extrapolation}.dir"
|
|
915
|
+
)
|
|
916
|
+
|
|
917
|
+
@property
|
|
918
|
+
def psi4_path(self):
|
|
919
|
+
prefix = f"{self.lev}:" if self.lev else ""
|
|
920
|
+
return (
|
|
921
|
+
f"{prefix}ExtraWaveforms",
|
|
922
|
+
f"/Psi4_{self.extrapolation}.dir"
|
|
923
|
+
)
|
|
850
924
|
|
|
851
925
|
|
|
852
926
|
def get_file_info(metadata, sxs_id, download=None):
|
|
@@ -18,7 +18,7 @@ from .format_handlers import (
|
|
|
18
18
|
grathena,
|
|
19
19
|
)
|
|
20
20
|
from .format_handlers.lvc import to_lvc_conventions
|
|
21
|
-
from . import memory, transformations, alignment
|
|
21
|
+
from . import memory, transformations, alignment, norms
|
|
22
22
|
|
|
23
23
|
# Map a format string to the module that should be used to load a waveform with that format
|
|
24
24
|
formats = {
|