Skip to content

Commit e51072b

Browse files
committed
API: forbid use of non-finite boxes
1 parent 73db531 commit e51072b

File tree

4 files changed

+59
-28
lines changed

4 files changed

+59
-28
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ ds = gpgi.load(
178178

179179
With CIC and TSC deposition, particles contribute to cells neighbouring the one
180180
that contains them. For particles that live in the outermost layer of the
181-
domain, this means some of their contribution is lost. This behaviour
181+
domain, this means some of their contribution is lost. This behavior
182182
corresponds to the default `'open'` boundary condition, but `gpgi` has builtin
183183
support for more conservative boundary conditions.
184184

src/gpgi/types.py

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from __future__ import annotations
44

55
import enum
6+
import math
67
import sys
78
import warnings
89
from abc import ABC, abstractmethod
@@ -229,15 +230,23 @@ def _validate_coordinates(self) -> None:
229230
coord_dtype = self._get_safe_datatype(coord)
230231
dt = coord_dtype.type
231232
xmin, xmax = (dt(_) for _ in _AXES_LIMITS[axis])
232-
if (cmin := dt(np.min(coord))) < xmin:
233+
if (cmin := dt(np.min(coord))) < xmin or not math.isfinite(cmin):
234+
if math.isfinite(xmin):
235+
hint = f"minimal value allowed is {xmin}"
236+
else:
237+
assert xmin == -float("inf")
238+
hint = "value must be finite"
233239
raise ValueError(
234-
f"Invalid coordinate data for axis {axis!r} {cmin} "
235-
f"(minimal allowed value is {xmin})"
240+
f"Invalid coordinate data for axis {axis!r} {cmin} ({hint})"
236241
)
237-
if (cmax := dt(np.max(coord))) > xmax:
242+
if (cmax := dt(np.max(coord))) > xmax or not math.isfinite(cmax):
243+
if math.isfinite(xmax):
244+
hint = f"maximal value allowed is {xmax}"
245+
else:
246+
assert xmax == float("inf")
247+
hint = "value must be finite"
238248
raise ValueError(
239-
f"Invalid coordinate data for axis {axis!r} {cmax} "
240-
f"(maximal allowed value is {xmax})"
249+
f"Invalid coordinate data for axis {axis!r} {cmax} ({hint})"
241250
)
242251

243252
self.coordinates[axis] = coord.astype(coord_dtype, copy=False)
@@ -680,7 +689,7 @@ def deposit(
680689

681690
if self.grid.size == 1:
682691
warnings.warn(
683-
"Depositing on a single-cell grid is undefined behaviour",
692+
"Depositing on a single-cell grid is undefined behavior",
684693
stacklevel=2,
685694
)
686695
if self.particles.count == 0:

tests/test_api.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,41 @@ def test_unsorted_cell_edges():
8484
gpgi.load(geometry="cartesian", grid={"cell_edges": {"x": np.array([1, 0])}})
8585

8686

87+
@pytest.mark.parametrize(
88+
"side, out_of_bounds_value",
89+
[
90+
("min", -float("inf")),
91+
("max", float("inf")),
92+
],
93+
)
94+
def test_infinite_box_edges(side, out_of_bounds_value):
95+
if side == "min":
96+
xlim = np.array([-np.inf, 1.0])
97+
elif side == "max":
98+
xlim = np.array([-0.1, +np.inf])
99+
with pytest.raises(
100+
ValueError,
101+
match=(
102+
f"Invalid coordinate data for axis 'x' {out_of_bounds_value} "
103+
rf"\(value must be finite\)"
104+
),
105+
):
106+
gpgi.load(geometry="cartesian", grid={"cell_edges": {"x": xlim}})
107+
108+
109+
def test_missing_grid():
110+
with pytest.raises(
111+
TypeError, match=r"load\(\) missing 1 required keyword-only argument: 'grid'"
112+
):
113+
gpgi.load(
114+
geometry="cartesian",
115+
particles={
116+
"coordinates": {"x": np.arange(10, dtype="float64")},
117+
"fields": {"mass": np.ones(10, dtype="float64")},
118+
},
119+
)
120+
121+
87122
@pytest.mark.parametrize(
88123
"geometry, cell_edges, coords, axis, side, limit",
89124
[
@@ -237,7 +272,7 @@ def test_load_invalid_particles_coordinates(
237272
ValueError,
238273
match=(
239274
f"Invalid coordinate data for axis {axis!r} {c} "
240-
rf"\({side}imal allowed value is {limit}\)"
275+
rf"\({side}imal value allowed is {limit}\)"
241276
),
242277
):
243278
gpgi.load(

tests/test_deposit.py

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -47,37 +47,24 @@ def sample_2D_dataset():
4747
)
4848

4949

50-
def test_single_cell_grid():
51-
# TODO: drop support for this use case (infinite boundaries)
50+
@pytest.mark.parametrize("method", ["ngp", "cic", "tsc"])
51+
def test_single_cell_grid(method):
5252
ds = gpgi.load(
5353
geometry="cartesian",
5454
grid={
5555
"cell_edges": {
56-
"x": np.array([-np.inf, np.inf]),
56+
"x": np.array([-1.0, 1.0]),
5757
},
5858
},
5959
particles={
60-
"coordinates": {"x": np.arange(10, dtype="float64")},
60+
"coordinates": {"x": 0.1 * np.arange(10, dtype="float64") - 0.5},
6161
"fields": {"mass": np.ones(10, dtype="float64")},
6262
},
6363
)
6464
with pytest.warns(
65-
UserWarning, match="Depositing on a single-cell grid is undefined behaviour"
66-
):
67-
ds.deposit("mass", method="ngp")
68-
69-
70-
def test_missing_grid():
71-
with pytest.raises(
72-
TypeError, match=r"load\(\) missing 1 required keyword-only argument: 'grid'"
65+
UserWarning, match="Depositing on a single-cell grid is undefined behavior"
7366
):
74-
gpgi.load(
75-
geometry="cartesian",
76-
particles={
77-
"coordinates": {"x": np.arange(10, dtype="float64")},
78-
"fields": {"mass": np.ones(10, dtype="float64")},
79-
},
80-
)
67+
ds.deposit("mass", method=method)
8168

8269

8370
def test_missing_particles():

0 commit comments

Comments
 (0)