Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,12 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
pip install flake8 pytest 'sxs==2025.0.9' romspline pycbc requests bs4
pip install -r requirements.txt
pip install flake8 pytest
pip install -e .
- name: Apply pycbc numpy 2.x compatibility patch
run: |
python scripts/patch_pycbc_numpy2.py
#- name: Lint with flake8
# run: |
# stop the build if there are Python syntax errors or undefined names
Expand Down
3 changes: 0 additions & 3 deletions PyART/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
#!/usr/bin/env python
from __future__ import absolute_import

__import__("pkg_resources").declare_namespace(__name__)

from . import analysis
from . import analytic
Expand Down
2 changes: 0 additions & 2 deletions PyART/analysis/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
from __future__ import absolute_import

__import__("pkg_resources").declare_namespace(__name__)
2 changes: 0 additions & 2 deletions PyART/analytic/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
from __future__ import absolute_import

__import__("pkg_resources").declare_namespace(__name__)
2 changes: 0 additions & 2 deletions PyART/catalogs/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
from __future__ import absolute_import

__import__("pkg_resources").declare_namespace(__name__)
2 changes: 0 additions & 2 deletions PyART/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
from __future__ import absolute_import

__import__("pkg_resources").declare_namespace(__name__)
2 changes: 0 additions & 2 deletions PyART/numerical/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
from __future__ import absolute_import

__import__("pkg_resources").declare_namespace(__name__)
2 changes: 0 additions & 2 deletions PyART/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
from __future__ import absolute_import

__import__("pkg_resources").declare_namespace(__name__)
2 changes: 1 addition & 1 deletion PyART/utils/convert_sxs_to_lvc.py
Original file line number Diff line number Diff line change
Expand Up @@ -731,7 +731,7 @@ def convert_simulation(
The option resolution is an integer labeling the resolution of the
converted waveform. Modes is an array of the format
[[l1, m1], [l2, m2], ...] listing the l,m modes to convert. This function
outputs a file in LVC format named SXS_BBH_\#\#\#\#_Res\#.h5
outputs a file in LVC format named SXS_BBH_####_Res#.h5
in out_path."""
horizons = h5py.File(sxs_data_path + "/Horizons.h5", "r")
rhOverM = h5py.File(sxs_data_path + "/rhOverM_Asymptotic_GeometricUnits_CoM.h5")
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ Install with
pip install .
```

> **Note:** PyART depends on `pycbc`, which currently has two incompatibilities with numpy 2.x.
> After installing, apply the one-time patch:
> ```
> python scripts/patch_pycbc_numpy2.py
> ```
> This patches `pycbc`'s `events/threshold_cpu.py` and `filter/matchedfilter.py`
> in-place. It is idempotent and safe to re-run after pycbc upgrades.

## For developers

If you are a developer:
Expand Down
11 changes: 10 additions & 1 deletion examples/match_sxs.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
from PyART.models import teob
from PyART.analysis.match import Matcher
from PyART.utils import utils as ut
from PyART.logging_config import setup_logging

setup_logging(level="INFO")

matplotlib.rc("text", usetex=True)

Expand Down Expand Up @@ -39,7 +42,13 @@
# load (or download) SXS data
sxs_id = f"{args.sxs_id:04}" # e.g.0180
nr = sxs.Waveform_SXS(
path=sxs_path, download=True, ID=sxs_id, order="Extrapolated_N3.dir", ellmax=7
path=sxs_path,
download=True,
ID=sxs_id,
ellmax=7,
ignore_deprecation=True,
downloads=["hlm", "metadata", "horizons"],
load=["hlm", "metadata", "horizons"],
)
nr.cut(300)
# nr.compute_hphc()
Expand Down
13 changes: 10 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Requirements for most basic library
numpy>=1.16.0,<2.0,!=1.19.0
# Note: pycbc>=2.4.0 uses np.array(..., copy=False) which changed semantics in
# numpy 2.x. After installing, apply the one-time patch:
# python scripts/patch_pycbc_numpy2.py
numpy>=2.0
scipy>=1.13.0
rich>=13.7.0
matplotlib>=3.7.3
Expand All @@ -8,11 +11,15 @@ astropy>=5.0.0
setuptools

# GW package dependencies
# pycbc>=2.4.0
pycbc>=2.4.0
# teobresums>=4.4.0
# gw-eccentricity>=1.0.4
# mayawaves>=2024.4.25
# lalsuite>=7.22
lalsuite>=7.22

# SXS catalog support (optional; needed for downloading SXS simulations)
sxs>=2025.0
romspline

# Documentation
sphinx>=4.2.0
Expand Down
96 changes: 96 additions & 0 deletions scripts/patch_pycbc_numpy2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
"""
Patch pycbc for numpy 2.x compatibility.

pycbc <= 2.10.0 has two numpy 2.x incompatibilities:

1. events/threshold_cpu.py: np.array(..., copy=False) raises ValueError in
numpy 2.x when a copy is required; numpy.asarray() preserves the original
numpy 1.x semantics (copy only if necessary, silently).

2. filter/matchedfilter.py: numpy.real(x) / numpy.imag(x) dispatch via
x.real / x.imag. On pycbc Array objects these are plain methods, not
properties, so numpy.real() returns the bound method instead of the data.
Replacing with numpy.asarray(x).real / .imag forces conversion via
Array.__array__ first, which is safe and localized to the affected calls.

Run once after installing pycbc:
python scripts/patch_pycbc_numpy2.py
"""

import inspect
import re
import shutil
import sys
from pathlib import Path


def _clear_cache(path):
cache_dir = path.parent / "__pycache__"
if cache_dir.exists():
shutil.rmtree(cache_dir)
print(f" Cleared __pycache__ in {cache_dir.parent.name}/")


def patch_threshold_cpu():
try:
import pycbc.events.threshold_cpu as mod
except ImportError:
print("pycbc not found – nothing to patch.", file=sys.stderr)
sys.exit(1)

path = Path(inspect.getfile(mod))
src = path.read_text()
original = src

src = src.replace(
"numpy.array(series.data, copy=False, dtype=numpy.complex64)",
"numpy.asarray(series.data, dtype=numpy.complex64)",
)
src = re.sub(
r"numpy\.array\(series\.data,\s*copy=False,\s*\n(\s*)dtype=numpy\.complex64\)",
r"numpy.asarray(series.data,\n\1dtype=numpy.complex64)",
src,
)

if src == original:
print("threshold_cpu.py: already patched or pattern not found, skipping.")
else:
path.write_text(src)
_clear_cache(path)
print("threshold_cpu.py: patched (copy=False → asarray).")


def patch_matchedfilter():
try:
import pycbc.filter.matchedfilter as mod
except ImportError:
print("pycbc not found – nothing to patch.", file=sys.stderr)
sys.exit(1)

path = Path(inspect.getfile(mod))
src = path.read_text()
original = src

replacements = [
("numpy.real(hplus)", "numpy.asarray(hplus).real"),
("numpy.real(hcross)", "numpy.asarray(hcross).real"),
("numpy.imag(hplus)", "numpy.asarray(hplus).imag"),
("numpy.imag(hcross)", "numpy.asarray(hcross).imag"),
("numpy.real(hphccorr)", "numpy.asarray(hphccorr).real"),
("numpy.real(hplus_cross_corr)", "numpy.asarray(hplus_cross_corr).real"),
]
for old, new in replacements:
src = src.replace(old, new)

if src == original:
print("matchedfilter.py: already patched or pattern not found, skipping.")
else:
path.write_text(src)
_clear_cache(path)
n = sum(1 for a, b in zip(original.splitlines(), src.splitlines()) if a != b)
print(f"matchedfilter.py: patched ({n} lines changed).")


if __name__ == "__main__":
patch_threshold_cpu()
patch_matchedfilter()
3 changes: 2 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ classifiers =
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Programming Language :: Python :: 3.11
Programming Language :: Python :: 3.12
Topic :: Scientific/Engineering :: Astronomy
Topic :: Scientific/Engineering :: Physics
Natural Language :: English
Expand All @@ -24,4 +25,4 @@ classifiers =
[options]
packages = find:
install_requires = file: requirements.txt
python_requires = >=3.9
python_requires = >=3.9,<3.13
31 changes: 30 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,34 @@
setup.py file for GWforge package
"""

from setuptools import setup, find_packages
from setuptools import setup, find_packages, Command
from setuptools.command.install import install
from setuptools.command.develop import develop
import os

PATCH_MESSAGE = """
============================================================
PyART post-install step required
------------------------------------------------------------
pycbc <= 2.10.0 is incompatible with numpy 2.x.
Run the following command once to patch pycbc in-place:

python scripts/patch_pycbc_numpy2.py

The patch is idempotent and safe to re-run after pycbc
upgrades.
============================================================
"""


def print_patch_notice(command_cls):
class Patched(command_cls):
def run(self):
super().run()
print(PATCH_MESSAGE)

return Patched


def find_files(
dirname,
Expand Down Expand Up @@ -52,6 +77,10 @@ def find_paths(directory):
ignore_files = [".DS_Store", "*.pyc", "Thumbs.db", "*.sqlite", "*.swp", "*.env", ".env"]

setup(
cmdclass={
"install": print_patch_notice(install),
"develop": print_patch_notice(develop),
},
scripts=find_files(
"bin/", relpath="./", ignore_dirs=ignore_dirs, ignore_files=ignore_files
),
Expand Down
1 change: 1 addition & 0 deletions tests/test_gra.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ def test_gra():
res="128",
downloads=["hlm", "metadata"],
ext="CCE",
r_ext="50.00",
)
# check attributes
assert wf.ID == "0001"
Expand Down
Loading