Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

numpy 2.0 compatibility #3461

Merged
merged 21 commits into from
Jun 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
2 changes: 1 addition & 1 deletion .github/test_conda_env.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ dependencies:
- decorator
- lxml
- matplotlib
- numpy
- numpy < 2
- scipy
- requests
- setuptools
Expand Down
2 changes: 1 addition & 1 deletion .github/test_mindep_conda_env.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ dependencies:
- decorator
- lxml
- matplotlib
- numpy
- numpy < 2
- scipy
- requests
- setuptools
Expand Down
27 changes: 27 additions & 0 deletions .github/test_np2_conda_env.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: test
channels:
- conda-forge
- defaults
dependencies:
# obspy
- decorator
- lxml
- matplotlib
- numpy >= 2.0.0rc2
# dummy package needed to install the np2 rc via conda
- conda-forge/label/numpy_rc::_numpy_rc
- scipy
- requests
- setuptools
# see #3258
- sqlalchemy < 2.0
# soft dependencies
- cartopy
- geographiclib
- pyshp
# tests
- packaging
- pyproj
- pytest
- pytest-cov
- pytest-json-report >= 1.4
12 changes: 11 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,16 @@ jobs:
- os: ubuntu-latest
python-version: '3.12'
options: default warnings
# numpy 2 specific builds
- os: ubuntu-latest
python-version: '3.12'
options: default warnings np2
- os: macos-latest
python-version: '3.12'
options: default warnings np2
- os: windows-latest
python-version: '3.12'
options: default warnings np2
- os: ubuntu-latest
python-version: '3.12'
options: ${{ (github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'test_network')) && 'network warnings' || 'default' }}
Expand All @@ -136,7 +146,7 @@ jobs:
# set individual cache key
cache_key: ${{ matrix.os }}-py${{ matrix.python-version }}-${{ contains(matrix.options, 'mindep') }}-${{ contains(matrix.options, 'mindepversion') }}
# set conda environment file with dependencies
env_file: .github/test${{ contains(matrix.options, 'mindepversion') && '_mindepversion' || contains(matrix.options, 'mindep') && '_mindep' || '' }}_conda_env.yml
env_file: .github/test${{ contains(matrix.options, 'np2') && '_np2' || contains(matrix.options, 'mindepversion') && '_mindepversion' || contains(matrix.options, 'mindep') && '_mindep' || '' }}_conda_env.yml
ThomasLecocq marked this conversation as resolved.
Show resolved Hide resolved
# set additional runtest options (--keep-images, --network, -W error)
runtest_options: ${{ github.event_name == 'pull_request' && '--keep-images' || '' }} ${{ contains(matrix.options, 'network') && '--network' || '' }} ${{ contains(matrix.options, 'warnings') && '-W error' || '' }}
steps:
Expand Down
13 changes: 12 additions & 1 deletion obspy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,20 @@
(https://www.gnu.org/copyleft/lesser.html)
"""
import sys
import warnings

# don't change order
from obspy.core.utcdatetime import UTCDateTime # NOQA
# ignore pkg resources deprecation warning for now
# we really only need this here for one single reason: this file here gets
# imported during the pytest startup phase before ignore rules in pytest.ini
# take effect. otherwise we could just add this warning to the ignore list in
# pytest.ini (see #3333)
# The warning capture can be removed after #3333 gets finalized and merged
with warnings.catch_warnings(record=True) as w:
msg = ('pkg_resources is deprecated as an API')
warnings.filterwarnings(
'ignore', message=msg, category=DeprecationWarning, module='obspy')
from obspy.core.utcdatetime import UTCDateTime # NOQA
from obspy.core.util import _get_version_string
__version__ = _get_version_string(abbrev=10)
from obspy.core.trace import Trace # NOQA
Expand Down
3 changes: 2 additions & 1 deletion obspy/core/preview.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from obspy.core.stream import Stream
from obspy.core.trace import Trace
from obspy.core.utcdatetime import UTCDateTime
from obspy.core.util.misc import ptp


def create_preview(trace, delta=60):
Expand Down Expand Up @@ -66,7 +67,7 @@ def create_preview(trace, delta=60):
# reshape matrix
data = trace.data[start:end].reshape([number_of_slices, samples_per_slice])
# get minimum and maximum for each row
diff = data.ptp(axis=1)
diff = ptp(data, axis=1)
# fill masked values with -1 -> means missing data
if isinstance(diff, np.ma.masked_array):
diff = np.ma.filled(diff, -1)
Expand Down
9 changes: 5 additions & 4 deletions obspy/core/stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
_read_from_plugin, _generic_reader)
from obspy.core.util.decorator import (map_example_filename,
raise_if_masked, uncompress_file)
from obspy.core.util.misc import get_window_times, buffered_load_entry_point
from obspy.core.util.misc import (
get_window_times, buffered_load_entry_point, ptp)
from obspy.core.util.obspy_types import ObsPyException


Expand Down Expand Up @@ -3353,13 +3354,13 @@ def stack(self, group_by='all', stack_type='linear', npts_tol=0,
raise ValueError(msg)
if 'starttime' not in header and time_tol > 0:
times = [tr.stats.starttime for tr in traces]
if np.ptp(times) <= time_tol:
if ptp(times) <= time_tol:
# use high median as starttime
header['starttime'] = sorted(times)[len(times) // 2]
header['stack'] = AttribDict(group=groupid, count=len(traces),
type=stack_type)
npts_all = [len(tr) for tr in traces]
npts_dif = np.ptp(npts_all)
npts_dif = ptp(npts_all)
npts = min(npts_all)
if npts_dif > npts_tol:
msg = ('Difference of number of points of the traces is higher'
Expand Down Expand Up @@ -3407,7 +3408,7 @@ def _dummy_stream_from_string(s):
sampling_rate = float(items[6])
npts = int(items[8])
tr = Trace()
tr.data = np.ones(npts, dtype=np.float_)
tr.data = np.ones(npts, dtype=np.float64)
tr.stats.station = sta
tr.stats.network = net
tr.stats.location = loc
Expand Down
2 changes: 1 addition & 1 deletion obspy/core/tests/test_trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -1427,7 +1427,7 @@ def test_times(self):
tr.data = np.ma.ones(100)
tr.data[30:40] = np.ma.masked
tm = tr.times()
assert np.alltrue(tr.data.mask == tm.mask)
assert np.all(tr.data.mask == tm.mask)
# test relative with reftime
tr.data = np.ones(100)
shift = 9.5
Expand Down
12 changes: 6 additions & 6 deletions obspy/core/tests/test_utcdatetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,19 @@ def test_from_numpy_string(self):
Tests importing from NumPy strings.
"""
# some strange patterns
dt = UTC(np.string_("1970-01-01 12:23:34"))
dt = UTC(np.bytes_("1970-01-01 12:23:34"))
assert dt == UTC(1970, 1, 1, 12, 23, 34)
dt = UTC(np.string_("1970,01,01,12:23:34"))
dt = UTC(np.bytes_("1970,01,01,12:23:34"))
assert dt == UTC(1970, 1, 1, 12, 23, 34)
dt = UTC(np.string_("1970,001,12:23:34"))
dt = UTC(np.bytes_("1970,001,12:23:34"))
assert dt == UTC(1970, 1, 1, 12, 23, 34)
dt = UTC(np.string_("20090701121212"))
dt = UTC(np.bytes_("20090701121212"))
assert dt == UTC(2009, 7, 1, 12, 12, 12)
dt = UTC(np.string_("19700101"))
dt = UTC(np.bytes_("19700101"))
assert dt == UTC(1970, 1, 1, 0, 0)
# non ISO8601 strings should raise an exception
with pytest.raises(Exception):
UTC(np.string_("1970,001,12:23:34"), iso8601=True)
UTC(np.bytes_("1970,001,12:23:34"), iso8601=True)

def test_from_python_date_time(self):
"""
Expand Down
2 changes: 1 addition & 1 deletion obspy/core/tests/test_util_testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def test_nan(self):
Ensure NaNs eval equal if equal_nan is used, else they do not.
"""
tr1, tr2 = read()[0], read()[0]
tr1.data[0], tr2.data[0] = np.NaN, np.NaN
tr1.data[0], tr2.data[0] = np.nan, np.nan
assert traces_almost_equal(tr1, tr2, equal_nan=True)
assert not traces_almost_equal(tr1, tr2, equal_nan=False)

Expand Down
2 changes: 1 addition & 1 deletion obspy/core/trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -1735,7 +1735,7 @@ def resample(self, sampling_rate, window='hann', no_filter=True,
self.filter('lowpass_cheby_2', freq=freq, maxorder=12)

# resample in the frequency domain. Make sure the byteorder is native.
x = rfft(self.data.newbyteorder("="))
x = rfft(self.data.view(self.data.dtype.newbyteorder("=")))
# Cast the value to be inserted to the same dtype as the array to avoid
# issues with numpy rule 'safe'.
x = np.insert(x, 1, x.dtype.type(0))
Expand Down
6 changes: 6 additions & 0 deletions obspy/core/utcdatetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -995,6 +995,12 @@ def __add__(self, value):
msg = ("unsupported operand type(s) for +: 'UTCDateTime' and "
"'UTCDateTime'")
raise TypeError(msg)
# need to make sure we don't get e.g. np.float32 singl precision input
# or worse, because then numpy is in charge of the calculations and
# numpy 2.0 is not automatically upcasting to avoid precision loss
# which means we can't keep full precision when converting input
# seconds to nanoseconds
value = float(value)
return UTCDateTime(ns=self._ns + int(round(value * 1e9)))

def __sub__(self, value):
Expand Down
14 changes: 14 additions & 0 deletions obspy/core/util/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,20 @@ def func(obj, parent=None, attr=None):
return func(obj)


def ptp(a, *args, **kwargs):
"""
Replacement for :meth:`numpy.ndarray.ptp()` and the corresponding method on
`MaskedArray` objects which are being removed in numpy 2.0
Basically just makes sure we call the correct replacement function numpy
put in place for regular and masked arrays.

:type a: :class:`numpy.ndarray` or :class:`numpy.ma.MaskedArray`
"""
if isinstance(a, np.ma.MaskedArray):
return np.ma.ptp(a, *args, **kwargs)
return np.ptp(a, *args, **kwargs)


if __name__ == '__main__':
import doctest
doctest.testmod(exclude_empty=True)
7 changes: 5 additions & 2 deletions obspy/io/alsep/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@ def _read_header(file):
# np.fromfile accepts file path on-disk file-like
# objects. In all other cases, use np.frombuffer:
try:
return np.fromfile(file, dtype='u1', count=16)
header = np.fromfile(file, dtype='u1', count=16)
except UnsupportedOperation:
# stream does not support fileno
buffer = io_stream.read(16)
return np.frombuffer(buffer, dtype='u1')
header = np.frombuffer(buffer, dtype='u1')
# manual upcast for later bitshifting etc
header = np.require(header, dtype=np.int64)
return header


def _is_pse(file):
Expand Down
2 changes: 1 addition & 1 deletion obspy/io/alsep/pse/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

class PseFrame(object):
def __init__(self, frame, _is_old_format):
self.data = frame
self.data = np.require(frame, dtype=np.int64)
self._is_old_format = _is_old_format
# Frame header parameters
self.software_time_flag = None
Expand Down
2 changes: 1 addition & 1 deletion obspy/io/alsep/wt/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
class _WtFrame(object):

def __init__(self, frame):
self.data = frame
self.data = np.require(frame, dtype=np.int64)
# Frame header parameters
self.flag_bit = None
self.msec_of_year = None
Expand Down
3 changes: 1 addition & 2 deletions obspy/io/ascii/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,8 +349,7 @@ def _write_slist(stream, filename, custom_fmt=None,
else:
data = trace.data
data = data.reshape((-1, 6))
np.savetxt(fh, data, delimiter=b'\t',
fmt=fmt.encode('ascii', 'strict'))
np.savetxt(fh, data, delimiter='\t', fmt=fmt)
if rest:
fh.write(('\t'.join([fmt % d for d in trace.data[-rest:]]) +
'\n').encode('ascii', 'strict'))
Expand Down
2 changes: 1 addition & 1 deletion obspy/io/mseed/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -1609,7 +1609,7 @@ def shift_time_of_file(input_file, output_file, timeshift):
# This should rarely be the case.
if current_time_shift == 0 and is_time_correction_applied:
# This sets bit 2 of the activity flags to 0.
current_record[36] = current_record[36] & (~2)
current_record[36] = np.int64(current_record[36]) & (~2)
is_time_correction_applied = False
# This is the case if the time correction has been applied. This
# requires some more work by changing both, the actual time and the
Expand Down
4 changes: 3 additions & 1 deletion obspy/io/nordic/ellipse.py
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,9 @@ def subtended_angle(self, pt=(0, 0)):
temp.y -= pt[1]
t0, t1 = temp._get_tangents((0, 0))
cosang = np.dot(t0, t1)
sinang = np.linalg.norm(np.cross(t0, t1))
# avoid np.cross() complaining about 2-d vectors
# see https://github.com/obspy/obspy/pull/3461#issuecomment-2149488336
sinang = abs(t0[0] * t1[1] - t0[1] * t1[0])
return np.degrees(np.arctan2(sinang, cosang))

def plot(self, linewidth=2, color='k',
Expand Down
2 changes: 1 addition & 1 deletion obspy/io/reftek/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ def drop_not_implemented_packet_types(self):
Checks if there are packets of a type that is currently not implemented
and drop them showing a warning message.
"""
is_implemented = np.in1d(
is_implemented = np.isin(
self._data['packet_type'],
[x.encode() for x in PACKET_TYPES_IMPLEMENTED])
# if all packets are of a type that is implemented, the nothing to do..
Expand Down
Loading
Loading