diff --git a/package/MDAnalysis/coordinates/timestep.pyx b/package/MDAnalysis/coordinates/timestep.pyx index 09e44c0f55..55c0d1e174 100644 --- a/package/MDAnalysis/coordinates/timestep.pyx +++ b/package/MDAnalysis/coordinates/timestep.pyx @@ -468,6 +468,18 @@ cdef class Timestep: if new_dimensions is None: self._unitcell[:] = 0 else: + new_dimensions = np.asarray(new_dimensions, dtype=np.float32) + if new_dimensions.ndim == 0: + if new_dimensions != 0: + raise ValueError("Unit cell dimensions must contain six values") + self._unitcell[:] = 0 + return + if new_dimensions.size == 6: + values = new_dimensions.reshape(-1) + if (values[:3] < 0).any(): + raise ValueError("Unit cell lengths must be non-negative") + if not np.isfinite(values[3:]).all() or not ((values[3:] > 0) & (values[3:] < 180)).all(): + raise ValueError("Unit cell angles must be finite and strictly between 0 and 180 degrees") self._unitcell[:] = new_dimensions @property diff --git a/testsuite/MDAnalysisTests/coordinates/test_timestep_api.py b/testsuite/MDAnalysisTests/coordinates/test_timestep_api.py index 1b2bc468b3..780f7451cd 100644 --- a/testsuite/MDAnalysisTests/coordinates/test_timestep_api.py +++ b/testsuite/MDAnalysisTests/coordinates/test_timestep_api.py @@ -170,6 +170,20 @@ def test_dimensions_set_box(self, ts, dtype): assert ts.dimensions.dtype == np.float32 assert_allclose(ts.dimensions, self.newbox) + def test_dimensions_reject_scalar_broadcast(self, ts): + with pytest.raises(ValueError, match="six values"): + ts.dimensions = 5 + ts.dimensions = 0 + assert ts.dimensions is None + + @pytest.mark.parametrize( + "angles", + ([0, 90, 90], [180, 90, 90], [-1, 90, 90], [np.nan, 90, 90], [np.inf, 90, 90]), + ) + def test_dimensions_reject_invalid_angles(self, ts, angles): + with pytest.raises(ValueError, match="angles"): + ts.dimensions = [10, 10, 10, *angles] + def test_volume(self, ts): ts.dimensions = self.newbox assert_equal(ts.volume, self.ref_volume)