From 0b21552642b28077f16ab50d8a8cfd5fb7dfa6eb Mon Sep 17 00:00:00 2001 From: Marcin Wojdyr Date: Tue, 30 Jul 2024 15:25:43 +0200 Subject: [PATCH] Python: remove buffer protocol from docs, tests and examples because nanobind doesn't support it --- docs/grid.rst | 20 +++----------------- docs/hkl.rst | 29 ++++++++--------------------- examples/maskcheck.py | 4 ++-- examples/maskdiff.py | 4 ++-- examples/merge_mtz_mmcif.py | 3 +-- examples/patterson_slice.py | 2 +- tests/test_grid.py | 2 +- tests/test_hkl.py | 18 ++++++++---------- 8 files changed, 26 insertions(+), 56 deletions(-) diff --git a/docs/grid.rst b/docs/grid.rst index fb427220f..847647a90 100644 --- a/docs/grid.rst +++ b/docs/grid.rst @@ -121,18 +121,14 @@ Values are accessed with functions get_value() and set_value(): >>> grid.get_value(-11, 13, 25) 7.0 -.. _buffer_protocol: - -The data can be also accessed through the -`buffer protocol `_. -It means that you can use it as a NumPy array (Fortran-style contiguous) -without copying the data: +The data can be also accessed as a NumPy array (Fortran-style contiguous), +without copying the data, through the `array` property: .. doctest:: :skipif: numpy is None or sys.platform == 'win32' >>> import numpy - >>> array = numpy.array(grid, copy=False) + >>> array = grid.array >>> array.dtype dtype('float32') >>> array.shape @@ -140,16 +136,6 @@ without copying the data: >>> numpy.argwhere(array == 7.0) array([[1, 1, 1]]) -The buffer protocol is not specific to NumPy -- any other Python library -that supports this protocol can directly access the grid data. -Alternatively, the grid can be viewed as a NumPy array through -the `array` property: - -.. doctest:: - :skipif: numpy is None or sys.platform == 'win32' - - >>> grid.array.shape - (12, 12, 12) Symmetry -------- diff --git a/docs/hkl.rst b/docs/hkl.rst index 6727bc52d..61619a17f 100644 --- a/docs/hkl.rst +++ b/docs/hkl.rst @@ -392,15 +392,6 @@ It provides access to all the MTZ data as 2D NumPy array >>> mtz.array.shape (441, 8) -Another way to access MTZ data in Python is through the -`buffer protocol `_. -For example, here is equivalent of `mtz.array`: - -.. doctest:: - :skipif: numpy is None - - >>> all_data = numpy.array(mtz, copy=False) - It helps to have labels on the columns. A good data structure for this is Pandas DataFrame: @@ -408,7 +399,7 @@ is Pandas DataFrame: :skipif: pandas is None >>> import pandas - >>> df = pandas.DataFrame(data=all_data, columns=mtz.column_labels()) + >>> df = pandas.DataFrame(data=mtz.array, columns=mtz.column_labels()) >>> # now we can handle columns using their labels: >>> I_over_sigma = df['I'] / df['SIGI'] @@ -965,7 +956,7 @@ into a single DataFrame: .. literalinclude:: ../examples/merge_mtz_mmcif.py :language: python - :lines: 4-28 + :lines: 4-27 We want to compare the FP column from the MTZ file and the F_meas_au column from mmCIF. We start with plotting one against the other: @@ -976,7 +967,7 @@ from mmCIF. We start with plotting one against the other: .. literalinclude:: ../examples/merge_mtz_mmcif.py :language: python - :lines: 31-38 + :lines: 30-37 The numbers are similar, but not exactly equal. Let us check how the difference between the two values depends on the @@ -990,7 +981,7 @@ so we can use it for coloring. .. literalinclude:: ../examples/merge_mtz_mmcif.py :language: python - :lines: 41- + :lines: 40- Apparently, some scaling has been applied. The scaling is anisotropic and is the strongest along the *k* axis. @@ -1457,8 +1448,7 @@ To check if the size is big enough you can call: >>> rblock.data_fits_into([52,6,18]) False -To access the data you can use either the buffer protocol -(:ref:`in the same way ` as in the Grid class), +To access the data you can use either the NumPy interface (`grid.array`), or getter and setter: .. doctest:: @@ -1572,8 +1562,7 @@ tool. >>> size = rblock.get_size_for_hkl(sample_rate=2.6) >>> full = rblock.get_f_phi_on_grid('pdbx_FWT', 'pdbx_PHWT', size) - >>> array = numpy.array(full, copy=False) - >>> complex_map = numpy.fft.ifftn(array.conj()) + >>> complex_map = numpy.fft.ifftn(full.array.conj()) >>> scale_factor = complex_map.size / full.unit_cell.volume >>> real_map = numpy.real(complex_map) * scale_factor >>> round(real_map[1][2][3], 5) @@ -1591,8 +1580,7 @@ using complex-to-real FFT on a half of the data: :skipif: numpy is None >>> half = rblock.get_f_phi_on_grid('pdbx_FWT', 'pdbx_PHWT', size, half_l=True) - >>> array = numpy.array(half, copy=False) - >>> real_map = numpy.fft.irfftn(array.conj()) * scale_factor + >>> real_map = numpy.fft.irfftn(half.array.conj()) * scale_factor >>> round(real_map[1][2][3], 5) -0.40554 >>> round(real_map.std(), 5) @@ -1646,8 +1634,7 @@ The grid can be accessed as NumPy 3D array: .. doctest:: :skipif: numpy is None - >>> array = numpy.array(_, copy=False) - >>> round(array.std(), 5) + >>> round(_.array.std(), 5) 0.66338 .. _map_from_rblock: diff --git a/examples/maskcheck.py b/examples/maskcheck.py index 4a3dbfb77..d1e290078 100644 --- a/examples/maskcheck.py +++ b/examples/maskcheck.py @@ -51,8 +51,8 @@ def maskcheck(mask_path, coor_path, output_diff_map=None, verbose=False): def compare_mask_arrays(grid1, grid2, verbose): - arr1 = numpy.array(grid1, copy=False) - arr2 = numpy.array(grid2, copy=False) + arr1 = grid1.array + arr2 = grid2.array if arr1.shape != arr2.shape: sys.exit('Different grid sizes %s and %s. Exiting.' % (arr1.shape, arr2.shape)) diff --git a/examples/maskdiff.py b/examples/maskdiff.py index 544620d59..43896d49e 100644 --- a/examples/maskdiff.py +++ b/examples/maskdiff.py @@ -6,9 +6,9 @@ def maskdiff(path1, path2): mask1 = gemmi.read_ccp4_mask(path1, setup=True) - arr1 = numpy.array(mask1.grid, copy=False) + arr1 = mask1.grid.array mask2 = gemmi.read_ccp4_mask(path2, setup=True) - arr2 = numpy.array(mask2.grid, copy=False) + arr2 = mask2.grid.array print("Size: %d x %d x %d and %d x %d x %d" % (arr1.shape + arr2.shape)) if arr1.shape != arr2.shape: sys.exit("Different sizes. Exiting.") diff --git a/examples/merge_mtz_mmcif.py b/examples/merge_mtz_mmcif.py index 33bae68aa..536191625 100755 --- a/examples/merge_mtz_mmcif.py +++ b/examples/merge_mtz_mmcif.py @@ -11,8 +11,7 @@ # make DataFrame from MTZ file mtz = gemmi.read_mtz_file(MTZ_PATH) -mtz_data = numpy.array(mtz, copy=False) -mtz_df = pandas.DataFrame(data=mtz_data, columns=mtz.column_labels()) +mtz_df = pandas.DataFrame(data=mtz.array, columns=mtz.column_labels()) # (optional) store Miller indices as integers mtz_df = mtz_df.astype({label: 'int32' for label in 'HKL'}) diff --git a/examples/patterson_slice.py b/examples/patterson_slice.py index edce25712..258e544c7 100644 --- a/examples/patterson_slice.py +++ b/examples/patterson_slice.py @@ -6,7 +6,7 @@ # toxd_aupatt.map is generated by $CCP4/examples/unix/runnable/patterson ccp4 = gemmi.read_ccp4_map('/tmp/wojdyr/toxd_aupatt.map', setup=True) -arr = numpy.array(ccp4.grid, copy=False) +arr = ccp4.grid.array x = numpy.linspace(0, ccp4.grid.unit_cell.a, num=arr.shape[0], endpoint=False) y = numpy.linspace(0, ccp4.grid.unit_cell.b, num=arr.shape[1], endpoint=False) X, Y = numpy.meshgrid(x, y, indexing='ij') diff --git a/tests/test_grid.py b/tests/test_grid.py index d90d15575..262213e59 100644 --- a/tests/test_grid.py +++ b/tests/test_grid.py @@ -57,7 +57,7 @@ def test_reading(self): m.grid.symmetrize_max() self.assertEqual(m.grid.get_value(60-3, 24//2+4, 60-5), 90) if numpy: - arr = numpy.array(m.grid, copy=False) + arr = m.grid.array self.assertEqual(arr.shape, (60, 24, 60)) self.assertEqual(arr[3][4][5], 90) grid2 = gemmi.FloatGrid(arr) diff --git a/tests/test_hkl.py b/tests/test_hkl.py index 1e9b6fa10..c957f6a32 100755 --- a/tests/test_hkl.py +++ b/tests/test_hkl.py @@ -31,7 +31,7 @@ def fft_test(self, data, f, phi, size, order=gemmi.AxisOrder.XYZ): self.assertTrue(data.data_fits_into(size)) grid_full = data.get_f_phi_on_grid(f, phi, size, half_l=False, order=order) self.assertEqual(grid_full.axis_order, order) - array_full = numpy.array(grid_full, copy=False) + array_full = grid_full.array map1 = gemmi.transform_f_phi_grid_to_map(grid_full) self.assertEqual(map1.axis_order, order) map2 = numpy.fft.ifftn(array_full.conj()) @@ -84,7 +84,6 @@ def test_read_write(self): os.remove(out_name) self.assertEqual(mtz2.spacegroup.hm, 'P 1 21 1') if numpy is not None: - assert_numpy_equal(self, numpy.array(mtz, copy=False), mtz.array) assert_numpy_equal(self, mtz.array, mtz2.array) def test_remove_and_add_column(self): @@ -96,17 +95,16 @@ def test_remove_and_add_column(self): ncol = len(mtz.columns) if numpy is None: return - assert_numpy_equal(self, col.array, numpy.array(col, copy=False)) arr = col.array.copy() - mtz_data = numpy.array(mtz, copy=True) + mtz_data = mtz.array.copy() self.assertEqual(mtz_data.shape, (mtz.nreflections, ncol)) mtz.remove_column(col_idx) self.assertEqual(len(mtz.columns), ncol-1) - self.assertEqual(numpy.array(mtz, copy=False).shape, + self.assertEqual(mtz.array.shape, (mtz.nreflections, ncol-1)) col = mtz.add_column(col_name, 'I', dataset_id=0, pos=col_idx) - numpy.array(col, copy=False)[:] = arr - assert_numpy_equal(self, mtz_data, numpy.array(mtz, copy=False)) + col.array[:] = arr + assert_numpy_equal(self, mtz_data, mtz.array) def asu_data_test(self, grid): asu = grid.prepare_asu_data() @@ -130,8 +128,8 @@ def test_f_phi_grid(self): order=gemmi.AxisOrder.ZYX) if numpy is None: continue - array1 = numpy.array(grid1, copy=False) - array2 = numpy.array(grid2, copy=False) + array1 = grid1.array + array2 = grid2.array self.assertTrue((array2 == array1.transpose(2,1,0)).all()) self.asu_data_test(grid1) @@ -146,7 +144,7 @@ def test_value_grid(self): if numpy is None: return asu = gemmi.ReciprocalAsu(mtz.spacegroup) - mtz_data = numpy.array(mtz, copy=False) + mtz_data = mtz.array fp_idx = mtz.column_labels().index('FP') fp_map = {} for row in mtz_data: