sxs 2024.0.43__tar.gz → 2025.0.1__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.
Files changed (141) hide show
  1. {sxs-2024.0.43 → sxs-2025.0.1}/CITATION.cff +2 -2
  2. {sxs-2024.0.43 → sxs-2025.0.1}/PKG-INFO +2 -1
  3. {sxs-2024.0.43 → sxs-2025.0.1}/pyproject.toml +1 -0
  4. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/__init__.py +1 -1
  5. sxs-2025.0.1/sxs/__version__.py +1 -0
  6. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/handlers.py +3 -16
  7. sxs-2025.0.1/sxs/metadata/__init__.py +2 -0
  8. sxs-2025.0.1/sxs/metadata/metadata.py +2 -0
  9. sxs-2025.0.1/sxs/metadata/metric.py +1 -0
  10. sxs-2025.0.1/sxs/simulations/local.py +1 -0
  11. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/simulations/simulation.py +19 -4
  12. sxs-2025.0.1/sxs/simulations/simulations.py +2 -0
  13. sxs-2025.0.1/sxs/utilities/downloads.py +2 -0
  14. sxs-2025.0.1/sxs/utilities/string_converters.py +1 -0
  15. sxs-2025.0.1/sxs/utilities/sxs_directories.py +69 -0
  16. sxs-2025.0.1/sxs/utilities/sxs_identifiers.py +2 -0
  17. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/zenodo/__init__.py +1 -11
  18. sxs-2024.0.43/sxs/__version__.py +0 -1
  19. sxs-2024.0.43/sxs/caltechdata/__init__.py +0 -342
  20. sxs-2024.0.43/sxs/caltechdata/catalog.py +0 -85
  21. sxs-2024.0.43/sxs/caltechdata/login.py +0 -506
  22. sxs-2024.0.43/sxs/metadata/__init__.py +0 -10
  23. sxs-2024.0.43/sxs/metadata/metadata.py +0 -641
  24. sxs-2024.0.43/sxs/metadata/metric.py +0 -152
  25. sxs-2024.0.43/sxs/simulations/local.py +0 -227
  26. sxs-2024.0.43/sxs/simulations/simulations.py +0 -573
  27. sxs-2024.0.43/sxs/utilities/downloads.py +0 -104
  28. sxs-2024.0.43/sxs/utilities/string_converters.py +0 -47
  29. sxs-2024.0.43/sxs/utilities/sxs_directories.py +0 -273
  30. sxs-2024.0.43/sxs/utilities/sxs_identifiers.py +0 -127
  31. {sxs-2024.0.43 → sxs-2025.0.1}/.codecov.yml +0 -0
  32. {sxs-2024.0.43 → sxs-2025.0.1}/.github/dependabot.yml +0 -0
  33. {sxs-2024.0.43 → sxs-2025.0.1}/.github/scripts/parse_bump_rule.py +0 -0
  34. {sxs-2024.0.43 → sxs-2025.0.1}/.github/workflows/build.yml +0 -0
  35. {sxs-2024.0.43 → sxs-2025.0.1}/.github/workflows/pr_rtd_link.yml +0 -0
  36. {sxs-2024.0.43 → sxs-2025.0.1}/.gitignore +0 -0
  37. {sxs-2024.0.43 → sxs-2025.0.1}/.readthedocs.yaml +0 -0
  38. {sxs-2024.0.43 → sxs-2025.0.1}/LICENSE +0 -0
  39. {sxs-2024.0.43 → sxs-2025.0.1}/README.md +0 -0
  40. {sxs-2024.0.43 → sxs-2025.0.1}/docs/api/catalog.md +0 -0
  41. {sxs-2024.0.43 → sxs-2025.0.1}/docs/api/horizons.md +0 -0
  42. {sxs-2024.0.43 → sxs-2025.0.1}/docs/api/load.md +0 -0
  43. {sxs-2024.0.43 → sxs-2025.0.1}/docs/api/metadata.md +0 -0
  44. {sxs-2024.0.43 → sxs-2025.0.1}/docs/api/simulation.md +0 -0
  45. {sxs-2024.0.43 → sxs-2025.0.1}/docs/api/simulations.md +0 -0
  46. {sxs-2024.0.43 → sxs-2025.0.1}/docs/api/time_series.md +0 -0
  47. {sxs-2024.0.43 → sxs-2025.0.1}/docs/api/waveforms.md +0 -0
  48. {sxs-2024.0.43 → sxs-2025.0.1}/docs/html/main.html +0 -0
  49. {sxs-2024.0.43 → sxs-2025.0.1}/docs/images/favicon.ico +0 -0
  50. {sxs-2024.0.43 → sxs-2025.0.1}/docs/index.md +0 -0
  51. {sxs-2024.0.43 → sxs-2025.0.1}/docs/javascript/mathjax.js +0 -0
  52. {sxs-2024.0.43 → sxs-2025.0.1}/docs/julia.md +0 -0
  53. {sxs-2024.0.43 → sxs-2025.0.1}/docs/mathematica.md +0 -0
  54. {sxs-2024.0.43 → sxs-2025.0.1}/docs/stylesheets/extra.css +0 -0
  55. {sxs-2024.0.43 → sxs-2025.0.1}/docs/tutorials/00-Introduction.ipynb +0 -0
  56. {sxs-2024.0.43 → sxs-2025.0.1}/docs/tutorials/01-Simulations_and_Metadata.ipynb +0 -0
  57. {sxs-2024.0.43 → sxs-2025.0.1}/docs/tutorials/02-Simulation.ipynb +0 -0
  58. {sxs-2024.0.43 → sxs-2025.0.1}/docs/tutorials/03-Horizons.ipynb +0 -0
  59. {sxs-2024.0.43 → sxs-2025.0.1}/docs/tutorials/04-Waveforms.ipynb +0 -0
  60. {sxs-2024.0.43 → sxs-2025.0.1}/docs/tutorials/05-PreprocessingForFFTs.ipynb +0 -0
  61. {sxs-2024.0.43 → sxs-2025.0.1}/mkdocs.yml +0 -0
  62. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/catalog/__init__.py +0 -0
  63. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/catalog/catalog.py +0 -0
  64. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/catalog/create.py +0 -0
  65. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/catalog/description.py +0 -0
  66. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/horizons/__init__.py +0 -0
  67. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/horizons/spec_horizons_h5.py +0 -0
  68. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/horizons/xor_multishuffle_bzip2.py +0 -0
  69. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/julia/GWFrames.py +0 -0
  70. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/julia/__init__.py +0 -0
  71. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/juliapkg.json +0 -0
  72. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/simulations/__init__.py +0 -0
  73. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/time_series.py +0 -0
  74. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/utilities/__init__.py +0 -0
  75. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/utilities/bitwise.py +0 -0
  76. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/utilities/decimation/__init__.py +0 -0
  77. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/utilities/decimation/greedy_spline.py +0 -0
  78. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/utilities/decimation/linear_bisection.py +0 -0
  79. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/utilities/decimation/peak_greed.py +0 -0
  80. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/utilities/decimation/suppression.py +0 -0
  81. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/utilities/dicts.py +0 -0
  82. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/utilities/files.py +0 -0
  83. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/utilities/formats.py +0 -0
  84. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/utilities/inspire.py +0 -0
  85. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/utilities/lvcnr/__init__.py +0 -0
  86. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/utilities/lvcnr/comparisons.py +0 -0
  87. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/utilities/lvcnr/conversion.py +0 -0
  88. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/utilities/lvcnr/dataset.py +0 -0
  89. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/utilities/lvcnr/horizons.py +0 -0
  90. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/utilities/lvcnr/metadata.py +0 -0
  91. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/utilities/lvcnr/waveform_amp_phase.py +0 -0
  92. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/utilities/lvcnr/waveforms.py +0 -0
  93. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/utilities/monotonicity.py +0 -0
  94. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/utilities/pretty_print.py +0 -0
  95. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/utilities/references/__init__.py +0 -0
  96. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/utilities/references/ads.py +0 -0
  97. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/utilities/references/arxiv.py +0 -0
  98. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/utilities/references/fairchild_report.py +0 -0
  99. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/utilities/references/inspire.py +0 -0
  100. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/utilities/references/journal_abbreviations.py +0 -0
  101. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/utilities/references/references.py +0 -0
  102. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/utilities/select.py +0 -0
  103. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/utilities/smooth_functions.py +0 -0
  104. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/utilities/url.py +0 -0
  105. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/waveforms/__init__.py +0 -0
  106. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/waveforms/alignment.py +0 -0
  107. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/waveforms/format_handlers/__init__.py +0 -0
  108. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/waveforms/format_handlers/grathena.py +0 -0
  109. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/waveforms/format_handlers/lvc.py +0 -0
  110. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/waveforms/format_handlers/nrar.py +0 -0
  111. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/waveforms/format_handlers/rotating_paired_diff_multishuffle_bzip2.py +0 -0
  112. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/waveforms/format_handlers/rotating_paired_xor_multishuffle_bzip2.py +0 -0
  113. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/waveforms/format_handlers/spectre_cce_v1.py +0 -0
  114. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/waveforms/memory.py +0 -0
  115. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/waveforms/mode_utilities.py +0 -0
  116. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/waveforms/transformations.py +0 -0
  117. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/waveforms/waveform_grid.py +0 -0
  118. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/waveforms/waveform_mixin.py +0 -0
  119. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/waveforms/waveform_modes.py +0 -0
  120. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/waveforms/waveform_signal.py +0 -0
  121. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/zenodo/api/__init__.py +0 -0
  122. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/zenodo/api/deposit.py +0 -0
  123. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/zenodo/api/login.py +0 -0
  124. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/zenodo/api/records.py +0 -0
  125. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/zenodo/catalog.py +0 -0
  126. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/zenodo/creators.py +0 -0
  127. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/zenodo/simannex.py +0 -0
  128. {sxs-2024.0.43 → sxs-2025.0.1}/sxs/zenodo/surrogatemodeling.py +0 -0
  129. {sxs-2024.0.43 → sxs-2025.0.1}/tests/__init__.py +0 -0
  130. {sxs-2024.0.43 → sxs-2025.0.1}/tests/conftest.py +0 -0
  131. {sxs-2024.0.43 → sxs-2025.0.1}/tests/test_catalog.py +0 -0
  132. {sxs-2024.0.43 → sxs-2025.0.1}/tests/test_horizons.py +0 -0
  133. {sxs-2024.0.43 → sxs-2025.0.1}/tests/test_julia.py +0 -0
  134. {sxs-2024.0.43 → sxs-2025.0.1}/tests/test_loader.py +0 -0
  135. {sxs-2024.0.43 → sxs-2025.0.1}/tests/test_metadata.py +0 -0
  136. {sxs-2024.0.43 → sxs-2025.0.1}/tests/test_simulation.py +0 -0
  137. {sxs-2024.0.43 → sxs-2025.0.1}/tests/test_time_series.py +0 -0
  138. {sxs-2024.0.43 → sxs-2025.0.1}/tests/test_transformations.py +0 -0
  139. {sxs-2024.0.43 → sxs-2025.0.1}/tests/test_utilities.py +0 -0
  140. {sxs-2024.0.43 → sxs-2025.0.1}/tests/test_waveform_rotations.py +0 -0
  141. {sxs-2024.0.43 → sxs-2025.0.1}/tests/test_waveforms.py +0 -0
@@ -10,5 +10,5 @@ authors:
10
10
  title: "The sxs package"
11
11
  license: MIT
12
12
  doi: 10.5281/zenodo.4034006
13
- version: 2024.0.43
14
- date-released: 2025-03-04
13
+ version: 2025.0.1
14
+ date-released: 2025-03-19
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sxs
3
- Version: 2024.0.43
3
+ Version: 2025.0.1
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/
@@ -46,6 +46,7 @@ Requires-Dist: quaternionic>=1.0.15
46
46
  Requires-Dist: requests>=2.24.0
47
47
  Requires-Dist: scipy>=1.13
48
48
  Requires-Dist: spherical>=1.0.15
49
+ Requires-Dist: sxscatalog>=3.0.0a1
49
50
  Requires-Dist: tqdm>=4.63.1
50
51
  Requires-Dist: urllib3>=1.25.10
51
52
  Provides-Extra: docs
@@ -18,6 +18,7 @@ classifiers = [
18
18
  "Topic :: Scientific/Engineering :: Astronomy"
19
19
  ]
20
20
  dependencies = [
21
+ "sxscatalog >=3.0.0a1",
21
22
  "numpy >=1.25",
22
23
  "scipy >=1.13",
23
24
  "numba >=0.55; implementation_name == 'cpython'",
@@ -28,7 +28,7 @@ from .waveforms import WaveformModes, WaveformModesDict, to_lvc_conventions #, W
28
28
  from .waveforms import rotating_paired_xor_multishuffle_bzip2 as rpxmb
29
29
  from .waveforms import rotating_paired_diff_multishuffle_bzip2 as rpdmb
30
30
  from .waveforms import spectre_cce_v1
31
- from . import catalog, metadata, horizons, waveforms, zenodo, caltechdata
31
+ from . import catalog, metadata, horizons, waveforms, zenodo
32
32
  from .simulations import Simulation, Simulations, write_local_simulations, local_simulations
33
33
  from .handlers import load, load_via_sxs_id, loadcontext, load_lvc
34
34
 
@@ -0,0 +1 @@
1
+ __version__ = "2025.0.1"
@@ -1,6 +1,7 @@
1
1
  """Functions to facilitate generic handling of SXS-format data files"""
2
2
 
3
3
  import contextlib
4
+ import sxscatalog
4
5
  from . import waveforms, doi_url
5
6
 
6
7
 
@@ -280,22 +281,8 @@ def load(location, download=None, cache=None, progress=None, truepath=None, **kw
280
281
  elif location == "catalog":
281
282
  return Catalog.load(download=download)
282
283
 
283
- elif location == "simulations":
284
- return Simulations.load(
285
- download=download,
286
- local=kwargs.get("local", False),
287
- annex_dir=kwargs.get("annex_dir", None),
288
- output_file=kwargs.get("output_file", None),
289
- compute_md5=kwargs.get("compute_md5", False),
290
- show_progress=kwargs.get("show_progress", False)
291
- )
292
-
293
- elif location == "dataframe":
294
- return Simulations.load(
295
- download=download,
296
- local=kwargs.get("local", False),
297
- annex_dir=kwargs.get("annex_dir", None)
298
- ).dataframe
284
+ elif location in ["simulations", "dataframe"]:
285
+ return sxscatalog.load(location, download=download, **kwargs)
299
286
 
300
287
  elif sxs_id_version_lev_exact_re.match(location):
301
288
  return Simulation(location, download=download, cache=cache, progress=progress, **kwargs)
@@ -0,0 +1,2 @@
1
+ """Interface to SXS metadata files"""
2
+ from sxscatalog.metadata import *
@@ -0,0 +1,2 @@
1
+ """Container for metadata of individual simulations"""
2
+ from sxscatalog.metadata.metadata import *
@@ -0,0 +1 @@
1
+ from sxscatalog.metadata.metric import *
@@ -0,0 +1 @@
1
+ from sxscatalog.simulations.local import *
@@ -124,9 +124,15 @@ def Simulation(location, *args, **kwargs):
124
124
  metadata = Metadata(simulations[simulation_id])
125
125
  series = simulations.dataframe.loc[simulation_id]
126
126
 
127
+ # If input_version is not the default, remove "files" from metadata
128
+ if input_version and input_version != max(metadata.get("DOI_versions", []), default=""):
129
+ metadata = type(metadata)({
130
+ key: value for key, value in metadata.items() if key != "files"
131
+ })
132
+
127
133
  # Check if the specified version exists in the simulation catalog
128
134
  if not hasattr(metadata, "DOI_versions"):
129
- input_version = "v0.0"
135
+ input_version = "v0.0" # A fake version, to signal this sim doesn't know about DOIs
130
136
  if input_version != "v0.0" and input_version not in metadata.DOI_versions:
131
137
  raise ValueError(f"Version '{input_version}' not found in simulation catalog for '{simulation_id}'")
132
138
 
@@ -217,10 +223,14 @@ def Simulation(location, *args, **kwargs):
217
223
  sim = Simulation_v1(
218
224
  metadata, series, version, sxs_id_stem, sxs_id, url, files, lev_numbers, output_lev_number, location, *args, **kwargs
219
225
  )
220
- elif 2 <= version_number < 3.0 or version == "v0.0":
226
+ elif 2 <= version_number < 3.0:
221
227
  sim = Simulation_v2(
222
228
  metadata, series, version, sxs_id_stem, sxs_id, url, files, lev_numbers, output_lev_number, location, *args, **kwargs
223
229
  )
230
+ elif 3 <= version_number < 4.0 or version == "v0.0":
231
+ sim = Simulation_v3(
232
+ metadata, series, version, sxs_id_stem, sxs_id, url, files, lev_numbers, output_lev_number, location, *args, **kwargs
233
+ )
224
234
  else:
225
235
  raise ValueError(f"Version '{version}' not yet supported")
226
236
  sim.__file__ = str(sxs_directory("cache") / sxs_path_to_system_path(sim.sxs_id))
@@ -365,7 +375,7 @@ class SimulationBase:
365
375
  dataframe = load("simulations").dataframe
366
376
  metadata_metric = metadata_metric or MetadataMetric()
367
377
  if drop_deprecated:
368
- dataframe = dataframe[~dataframe.deprecated]
378
+ dataframe = dataframe[~dataframe["deprecated"]]
369
379
  return dataframe.apply(
370
380
  lambda m: sqrt(metadata_metric(self.metadata, m)),
371
381
  axis=1
@@ -824,16 +834,21 @@ class Simulation_v2(SimulationBase):
824
834
  json_location = self.files.get(json_path)["link"]
825
835
  h5_truepath = Path(sxs_path_to_system_path(sxs_id_path / h5_path))
826
836
  json_truepath = Path(sxs_path_to_system_path(sxs_id_path / json_path))
837
+ json_truepath = sxs_directory("cache") / json_truepath
827
838
  if not Path(json_location).exists() and not json_truepath.exists():
828
839
  if not read_config("download", True):
829
840
  raise ValueError(f"{json_truepath} not found and download is disabled")
830
- download_file(json_location, sxs_directory("cache") / json_truepath)
841
+ download_file(json_location, json_truepath)
831
842
  return load(
832
843
  h5_location, truepath=h5_truepath, group=group, metadata=self.metadata,
833
844
  transform_to_inertial=transform_to_inertial
834
845
  )
835
846
 
836
847
 
848
+ class Simulation_v3(Simulation_v2):
849
+ pass
850
+
851
+
837
852
  def get_file_info(metadata, sxs_id, download=None):
838
853
  # TODO: Allow an existing zenodo_metadata.json file to be used
839
854
  from .. import load_via_sxs_id
@@ -0,0 +1,2 @@
1
+ """Container interface to the catalog of SXS simulations"""
2
+ from sxscatalog.simulations.simulations import *
@@ -0,0 +1,2 @@
1
+ """A core utility function for downloading efficiently and robustly"""
2
+ from sxscatalog.utilities.downloads import *
@@ -0,0 +1 @@
1
+ from sxscatalog.utilities.string_converters import *
@@ -0,0 +1,69 @@
1
+ """Functions to find user-specific config and cache directories"""
2
+
3
+ import re
4
+ import platform
5
+ import functools
6
+ from sxscatalog.utilities import read_config, write_config, sxs_directory
7
+ from .sxs_identifiers import sxs_path_re
8
+
9
+ _platform_system = platform.system()
10
+
11
+
12
+ def sxs_path_to_system_path(path):
13
+ r"""Translate SXS path to a system-compatible path
14
+
15
+ Parameters
16
+ ----------
17
+ path : str
18
+ SXS-style path to a file — for example, r"SXS:BBH:0123\Lev4:Horizons.h5"
19
+ becomes r"SXS_BBH_0123\Lev4_Horizons.h5" on Windows. Other systems can
20
+ handle the original path, so are not changed.
21
+
22
+ Notes
23
+ -----
24
+ SXS-style paths begin with SXS IDs, which contain colon characters. These
25
+ colons are incompatible with Windows file systems, so we simply replace the
26
+ colons with underscores.
27
+
28
+ """
29
+ if _platform_system == "Windows":
30
+ return sxs_path_re.sub(lambda s: s.group(0).replace(":", "_"), str(path))
31
+ else:
32
+ return path
33
+
34
+
35
+ def cached_path(path_pattern):
36
+ """Return path to file in local cache
37
+
38
+ Parameters
39
+ ----------
40
+ path_pattern : str
41
+ A pattern to search for among the catalog files. See the docstring of
42
+ `sxs.Catalog.select` for details about how this pattern is used.
43
+
44
+ Returns
45
+ -------
46
+ path : pathlib.Path
47
+ Full path to file on this system. The file may not exist.
48
+
49
+ Raises
50
+ ------
51
+ StopIteration
52
+ When no result is found for the input `path_pattern`.
53
+
54
+ Notes
55
+ -----
56
+ This function returns just one path, corresponding to the first result returned
57
+ by `catalog.select_files(path_pattern)`.
58
+
59
+ """
60
+ import contextlib
61
+ from .. import load
62
+ with contextlib.redirect_stdout(None):
63
+ catalog = load("catalog")
64
+ file_infos = catalog.select_files(path_pattern)
65
+ sxs_path, file_info = next(iter(file_infos.items()))
66
+ cache_path = sxs_directory("cache")
67
+ truepath = sxs_path_to_system_path(file_info.get("truepath", sxs_path))
68
+ path = cache_path / truepath
69
+ return path
@@ -0,0 +1,2 @@
1
+ """Simple regexes to understand SXS IDs"""
2
+ from sxscatalog.utilities.sxs_identifiers import *
@@ -4,7 +4,7 @@
4
4
 
5
5
  import os.path
6
6
  from pathlib import Path
7
-
7
+ from sxscatalog.utilities import path_to_invenio, invenio_to_path
8
8
  from .api import Login, Deposit, Records
9
9
  from . import catalog, simannex, surrogatemodeling
10
10
 
@@ -12,16 +12,6 @@ from . import catalog, simannex, surrogatemodeling
12
12
  # The other python API interface I found is here: https://github.com/moble/zenodo-python
13
13
 
14
14
 
15
- def path_to_invenio(file_path):
16
- """Convert a file path to an invenio-compatible name"""
17
- return str(file_path).replace(os.path.sep, ":")
18
-
19
-
20
- def invenio_to_path(file_name):
21
- """Convert an invenio-compatible name to a file path"""
22
- return Path(file_name.replace(":", os.path.sep))
23
-
24
-
25
15
  def translate(sxs_identifier, url=False):
26
16
  """Query data.black-holes.org to get the current Zenodo equivalent of the given SXS ID
27
17
 
@@ -1 +0,0 @@
1
- __version__ = "2024.0.43"
@@ -1,342 +0,0 @@
1
- import time
2
- import warnings
3
- import re
4
- import datetime
5
- import json
6
- import pathlib
7
-
8
- from .login import Login
9
- from .. import sxs_id, Metadata
10
- from ..utilities import sxs_identifier_regex, SimpleVersion
11
-
12
-
13
- def mtime(f):
14
- """Look for git or filesystem modification time
15
-
16
- If the git call fails for any reason, we fall back to the filesystem's `mtime`.
17
-
18
- The input `f` is expected to be a path to the file. The return value is a DateTime object
19
-
20
- """
21
- from subprocess import check_output
22
- try:
23
- timestamp = check_output(f"""git log -1 --format="%ad" --date=unix -- {f}""", shell=True).decode().strip()
24
- except:
25
- timestamp = f.stat().st_mtime
26
- finally:
27
- mt = datetime.datetime.fromtimestamp(timestamp, tz=datetime.timezone.utc)
28
- return mt
29
-
30
-
31
- def filter_files(files, utime):
32
- return [f for f in files if mtime(f) > utime]
33
-
34
-
35
- def upload_simannex_dir(login, directory, date=None):
36
- """Update/upload data from the SimAnnex to CaltechDATA
37
-
38
- Parameters
39
- ----------
40
- login : caltechdata.Login
41
- This is used to communicate with CaltechDATA.
42
- directory : str or pathlib.Path
43
- This must contain either "Public" or "Private", followed by the specific
44
- directory in SimAnnex to upload. It can also contain higher path elements
45
- — for example, you can give the absolute path.
46
- date : str, optional
47
- If given, this is used as the `publicationDate` field. It must be
48
- formatted as "%Y-%m-%d". The default is just the current UTC time.
49
-
50
- """
51
- raise Exception("This interface is for the old TIND API; use zenodo for the new API")
52
- from caltechdata_api import customize_schema, decustomize_schema
53
-
54
- allowed_files = [
55
- "metadata.json",
56
- "Strain_Outer.h5",
57
- "Strain_N2.h5",
58
- "Strain_N3.h5",
59
- "Strain_N4.h5",
60
- "Horizons.h5",
61
- "Matter.h5",
62
- ]
63
-
64
- # Check that `directory` is a valid SXS dataset
65
- # - Name must contain either "Public" or "Private" followed by at least one subdirectory
66
- # - Must contain common-metadata.txt (though this will not be uploaded)
67
- # - Must contain at least one Lev* subdirectory
68
- # - Each Lev* subdirectory must contain these files (which will be uploaded)
69
- # - metadata.json
70
- # - Strain_Outer.h5
71
- # - Strain_N2.h5
72
- # - Strain_N3.h5
73
- # - Strain_N4.h5
74
- # - Horizons.h5 and/or Matter.h5
75
- directory = pathlib.Path(directory).expanduser().resolve()
76
- if "/Public/" not in str(directory) and "/Private/" not in str(directory):
77
- raise ValueError(f"""Either "Public" or "Private" must be in the input directory "{directory}".""")
78
- if not (directory / "common-metadata.txt").exists():
79
- raise ValueError(f"Missing common-metadata.txt in {directory}")
80
- if not directory.glob("Lev*"):
81
- raise ValueError(f"Missing Lev* in {directory}")
82
- for lev in directory.glob("Lev*"):
83
- warnings.warn("Temporarily skipping file checks")
84
- # for f in ["Strain_Outer.h5", "Strain_N2.h5", "Strain_N3.h5", "Strain_N4.h5"]:
85
- # if not (lev / f).exists():
86
- # raise ValueError(f"""Missing file "{f}" in "{lev}".""")
87
- # if not (lev / "metadata.json").exists():
88
- # mtxt = lev / "metadata.txt"
89
- # if not mtxt.exists():
90
- # raise ValueError(f"""Missing both "metadata.txt" and "metadata.json" in "{lev}".""")
91
- # else:
92
- # m = Metadata.from_txt_file(str(mtxt), cache_json=False).add_extras().reorder_keys()
93
- # m.to_json_file(str(mtxt.with_suffix(".json")))
94
- # if not (lev / "Horizons.h5").exists() and not (lev / "Matter.h5").exists():
95
- # raise ValueError(f"""Missing both "Horizons.h5" and "Matter.h5" in "{lev}".""")
96
-
97
- # Figure out the "original name" of this simulation — something like "q4_7d/022" or "HighSpinMethods/BBH_SKS_d15.4_q1_..."
98
- root_dir = [par for par in directory.parents if par.match("Public") or par.match("Private")][0]
99
- original_name = str(directory.relative_to(root_dir))
100
-
101
- # Get the SXS ID from common-metadata.txt
102
- sxs_system_re = re.compile(sxs_identifier_regex)
103
- sxs_system_type = None
104
- sxs_system_number = None
105
- with (directory / "common-metadata.txt").open("r") as f:
106
- for line in f.readlines():
107
- line = line.strip()
108
- if "alternative-names" in line:
109
- m = sxs_system_re.search(line)
110
- if m:
111
- sxs_system_type = m["simulation_type"]
112
- sxs_system_number = m["sxs_number"]
113
- break
114
- if not sxs_system_type or not sxs_system_number:
115
- raise ValueError(f"No SXS identifier found in {directory / 'common-metadata.txt'}")
116
- sxs_system = f"SXS:{sxs_system_type}:{sxs_system_number}"
117
- spec_url = "https://www.black-holes.org/code/SpEC.html"
118
- if sxs_system_type == "BBH":
119
- title = f"Binary black-hole simulation {sxs_system}"
120
- description = f"""Simulation of a black-hole binary system evolved by the <a href="{spec_url}">SpEC code</a>."""
121
- elif sxs_system_type == "BHNS":
122
- title = f"Black-hole neutron-star binary simulation {sxs_system}"
123
- description = f"""Simulation of a black-hole neutron-star binary system evolved by the <a href="{spec_url}">SpEC code</a>."""
124
- elif sxs_system_type == "NSNS":
125
- title = f"Binary neutron-star simulation {sxs_system}"
126
- description = f"""Simulation of a neutron-star binary system evolved by the <a href="{spec_url}">SpEC code</a>."""
127
- else:
128
- raise ValueError(
129
- f"""Did not recognize SXS system type "{sxs_system_type}" in directory "{directory}"; should be BBH, BHNS, or NSNS."""
130
- )
131
- print(f"Beginning work on {sxs_system}")
132
-
133
- # Find our list of files
134
- files = sorted(
135
- [f for f in directory.glob("Lev*/*") if f.name in allowed_files],
136
- key=lambda f: f"{f.parent}/{allowed_files.index(f.name)}"
137
- )
138
-
139
- # Search for an existing record with this name
140
- existing = login.search(f'"{sxs_system}"')
141
- exists = existing.get("hits", {}).get("total", 0) > 0
142
-
143
- if exists:
144
- # Find the latest version
145
- latest = max(
146
- existing["hits"]["hits"],
147
- key=lambda h: SimpleVersion(h["metadata"]["version"])
148
- )
149
- latest_link = latest["links"]["self"].replace("/api/", "/")
150
- latest_doi = latest["metadata"]["doi"]
151
-
152
- # Check to see if any files have changed since the latest version
153
- utime = datetime.datetime.fromisoformat(latest["updated"])
154
- files_to_upload = filter_files(files, utime)
155
- if not files:
156
- print(f"No changes to {latest_link}.")
157
- return latest_link
158
-
159
- # Update version, relatedIdentifiers, and updated date
160
- metadata = latest["metadata"]
161
- old_version = SimpleVersion(metadata["version"])
162
- metadata["version"] = str(old_version.increment())
163
- metadata["relatedIdentifiers"] =[
164
- {
165
- "relatedIdentifierRelation": "IsNewVersionOf",
166
- "relatedIdentifier": latest_doi,
167
- "relatedIdentifierScheme": "DOI"
168
- }
169
- ]
170
- metadata["relevantDates"] = [
171
- {
172
- "relevantDateType": "Updated",
173
- "relevantDateValue": datetime.datetime.utcnow().strftime("%Y-%m-%d"),
174
- }
175
- ]
176
-
177
- # Upload files
178
- raise NotImplementedError(
179
- "Adding new files to an existing record is not yet supported. We will need some logic like this:\n" +
180
- " https://github.com/caltechlibrary/caltechdata_api/blob/840a359a017d1257ac344b6af80038f591fd8a97/caltechdata_api/caltechdata_edit.py#L189-L233\n" +
181
- "More specifically, if we want to create a new version, we will probably need to essentially copy\n" +
182
- "the existing record, publish it, and then follow steps like the above to upload new files and\n" +
183
- "delete the old ones. It is not clear to me if the deletion would destroy files being used by the\n" +
184
- "older version, or just delete that entry in the new record."
185
- )
186
- existing_names = {
187
- f["electronic_name"][0]: f["uniform_resource_identifier"].split("/")[-2]
188
- for f in metadata["electronic_location_and_access"]
189
- }
190
- for f in files_to_upload:
191
- fileinfo = login.send_s3(f, str(f.relative_to(directory)), verbose=True)
192
- name = fileinfo["filename"]
193
- if name in existing_names:
194
- metadata["files"][existing_names.index(name)] = fileinfo
195
- else:
196
- metadata["files"].append(fileinfo)
197
-
198
- else:
199
- # Set up the metadata. Note that CaltechDATA internally uses an undocumented
200
- # customized schema similar to — but different from — the standard Invenio
201
- # schema. Fortunately, `caltechdata_api` includes a function to convert from
202
- # DataCite 4.0 or 4.3 to their schema. So, we set ours up as DataCite 4.0 and
203
- # convert it: <https://schema.datacite.org/meta/kernel-4.0/>
204
-
205
- # Generate a little table of info to append to the description
206
- highest_lev = sorted(set(f.relative_to(directory).parent for f in files))[-1]
207
- sim_metadata = Metadata.from_file(directory / highest_lev / "metadata")
208
- object_types = sim_metadata.get("object_types", "")
209
- mass_ratio = sim_metadata.get("reference_mass_ratio", "Unknown")
210
- if "reference_dimensionless_spin1" in sim_metadata:
211
- chi1 = sim_metadata.reference_dimensionless_spin1
212
- chi1 = f"[{chi1[0]:0.4f}, {chi1[1]:0.4f}, {chi1[2]:0.4f}]"
213
- else:
214
- chi1 = "N/A"
215
- if "reference_dimensionless_spin2" in sim_metadata:
216
- chi2 = sim_metadata.reference_dimensionless_spin2
217
- chi2 = f"[{chi2[0]:0.4f}, {chi2[1]:0.4f}, {chi2[2]:0.4f}]"
218
- else:
219
- chi2 = "N/A"
220
- n_orbits = sim_metadata.get("number_of_orbits", "Unknown")
221
- reference_eccentricity = sim_metadata.get("reference_eccentricity", "Unknown")
222
- description = f"""{description}
223
- <br/>
224
- <table>
225
- <tbody>
226
- <tr><td style="padding:0 15px;">Mass ratio</td><td>{mass_ratio:.4f}</td></tr>
227
- <tr><td style="padding:0 15px;">Spin 1</td><td>{chi1}</td></tr>
228
- <tr><td style="padding:0 15px;">Spin 2</td><td>{chi2}</td></tr>
229
- <tr><td style="padding:0 15px;">Number of orbits</td><td>{n_orbits:.4f}</td></tr>
230
- <tr><td style="padding:0 15px;">Eccentricity</td><td>{reference_eccentricity}</td></tr>
231
- </tbody>
232
- </table>
233
- <br/>
234
- Originally named "{original_name}"
235
- """
236
- files_to_upload = files
237
-
238
- metadata = {
239
- "resourceType": {
240
- "resourceTypeGeneral": "Dataset"
241
- },
242
- "titles": [
243
- {"title": title},
244
- ],
245
- "version": "2.0",
246
- "creators": [{"creatorName": "SXS Collaboration"}],
247
- "descriptions": [
248
- {
249
- "descriptionType": "Abstract",
250
- "description": description,
251
- }
252
- ],
253
- "subjects": [
254
- {"subject": "Astronomy"},
255
- {"subject": "Astrophysics"},
256
- {"subject": "Gravitational Waves"},
257
- {"subject": "Numerical Relativity"},
258
- ],
259
- "dates": [
260
- {
261
- "date": datetime.datetime.utcnow().strftime("%Y-%m-%d"),
262
- "dateType": "Updated"
263
- },
264
- ],
265
- "rightsList": [
266
- {
267
- "rights": "Creative Commons Attribution 4.0 International License",
268
- "rightsURI": "https://creativecommons.org/licenses/by/4.0/",
269
- }
270
- ],
271
- }
272
- if "bh" in object_types.lower():
273
- metadata["subjects"].append({"subject": "Black Holes"})
274
- if "ns" in object_types.lower():
275
- metadata["subjects"].append({"subject": "Neutron Stars"})
276
- metadata = customize_schema(metadata, schema="40")
277
- metadata["publicationDate"] = date or datetime.datetime.utcnow().strftime("%Y-%m-%d")
278
- metadata["files"] = [login.send_s3(f, str(f.relative_to(directory)), verbose=True) for f in files]
279
-
280
- metadata["titles"] = [
281
- {"title": title},
282
- {"title": f"{original_name}", "titleType": "AlternativeTitle"},
283
- ]
284
-
285
- # if doi is None:
286
- # # We want tind to generate the identifier
287
- # metadata["final_actions"] = [
288
- # {
289
- # "type": "create_doi",
290
- # "parameters": {"type": "records", "field": "doi"},
291
- # }
292
- # ]
293
- # else:
294
- metadata["doi"] = f"""{login.doi_prefix}/{sxs_system}v{metadata["version"]}"""
295
-
296
- # Now tell CaltechDATA about it
297
- recordurl = f"{login.base_url}submit/api/create/"
298
- response = login.session.post(recordurl, data=json.dumps({"record": metadata}))
299
- if response.status_code != 200:
300
- print(f"An error occurred when trying to create a new record for '{directory}'.")
301
- try:
302
- print(response.text)
303
- except:
304
- pass
305
- try:
306
- print(response.json())
307
- except:
308
- pass
309
- response.raise_for_status()
310
- raise RuntimeError() # Will only happen if the response was not strictly an error
311
- else:
312
- print(f""" Publishing as "{title}".""")
313
- print(f""" {response.text}""")
314
-
315
- url = response.text[response.text.find(login.base_url):].rstrip().rstrip(".")
316
- pid = url.rsplit("/", 1)[1]
317
-
318
- # Create the DOI
319
- print(" Creating/updating DOIs")
320
- api_url = f"{login.base_url}api/record/{pid}"
321
- for retry in range(20):
322
- r = login.session.get(api_url)
323
- if r.status_code == 200:
324
- break
325
- time.sleep(1.1) # Let things settle down at CaltechDATA, so we can get the metadata
326
- if r.status_code != 200:
327
- print(f"""An error occurred when trying to access "{api_url}".""")
328
- try:
329
- print(r.json())
330
- except:
331
- pass
332
- r.raise_for_status()
333
- raise RuntimeError() # Will only happen if the response was not strictly an error
334
- output_metadata = r.json()
335
- doi_metadata = decustomize_schema(output_metadata, schema="43")
336
- if not exists:
337
- doi = f"""{login.doi_prefix}/{sxs_system}"""
338
- login.datacite.public_doi(doi_metadata, url, doi)
339
- doi = f"""{login.doi_prefix}/{sxs_system}v{metadata["version"]}"""
340
- login.datacite.public_doi(doi_metadata, url, doi)
341
-
342
- return url