Skip to content

Commit 8dc8b65

Browse files
committed
Helpers: to_numpy/cupy
1 parent 3689583 commit 8dc8b65

File tree

3 files changed

+134
-11
lines changed

3 files changed

+134
-11
lines changed

src/amrex/Array4.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
"""
2+
This file is part of pyAMReX
3+
4+
Copyright 2022 AMReX community
5+
Authors: Axel Huebl
6+
License: BSD-3-Clause-LBNL
7+
"""
8+
9+
def array4_to_numpy(self, copy=False, order="F"):
10+
"""
11+
Provide a Numpy view into an Array4.
12+
13+
Note on the order of indices:
14+
By default, this is as in AMReX in Fortran contiguous order, indexing as
15+
x,y,z. This has performance implications for use in external libraries such
16+
as cupy.
17+
The order="C" option will index as z,y,x and perform better with cupy.
18+
https://github.com/AMReX-Codes/pyamrex/issues/55#issuecomment-1579610074
19+
20+
Parameters
21+
----------
22+
self : amrex.Array4_*
23+
An Array4 class in pyAMReX
24+
copy : bool, optional
25+
Copy the data if true, otherwise create a view (default).
26+
order : string, optional
27+
F order (default) or C. C is faster with external libraries.
28+
29+
Returns
30+
-------
31+
np.array
32+
A numpy n-dimensional array.
33+
"""
34+
import numpy as np
35+
36+
if order == "F":
37+
return np.array(self, copy=copy).T
38+
elif order == "C":
39+
return np.array(self, copy=copy)
40+
else:
41+
raise ValueError("The order argument must be F or C.")
42+
43+
44+
def array4_to_cupy(self, copy=False, order="F"):
45+
"""
46+
Provide a Cupy view into an Array4.
47+
48+
Note on the order of indices:
49+
By default, this is as in AMReX in Fortran contiguous order, indexing as
50+
x,y,z. This has performance implications for use in external libraries such
51+
as cupy.
52+
The order="C" option will index as z,y,x and perform better with cupy.
53+
https://github.com/AMReX-Codes/pyamrex/issues/55#issuecomment-1579610074
54+
55+
Parameters
56+
----------
57+
self : amrex.Array4_*
58+
An Array4 class in pyAMReX
59+
copy : bool, optional
60+
Copy the data if true, otherwise create a view (default).
61+
order : string, optional
62+
F order (default) or C. C is faster with external libraries.
63+
64+
Returns
65+
-------
66+
cupy.array
67+
A numpy n-dimensional array.
68+
69+
Raises
70+
------
71+
ImportError
72+
Raises an exception if cupy is not installed
73+
"""
74+
import cupy as cp
75+
76+
if order == "F":
77+
return cp.array(self, copy=copy).T
78+
elif order == "C":
79+
return cp.array(self, copy=copy)
80+
else:
81+
raise ValueError("The order argument must be F or C.")

src/amrex/__init__.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,48 @@
55
"Axel Huebl, Ryan Sandberg, Shreyas Ananthan, Remi Lehe, " "Weiqun Zhang, et al."
66
)
77
__license__ = "BSD-3-Clause-LBNL"
8+
9+
import numpy as np
10+
11+
from . import amrex_pybind
12+
from .Array4 import array4_to_cupy, array4_to_numpy
13+
from .amrex_pybind import * # noqa
14+
15+
__version__ = amrex_pybind.__version__
16+
__doc__ = amrex_pybind.__doc__
17+
__license__ = amrex_pybind.__license__
18+
__author__ = amrex_pybind.__author__
19+
20+
# at this place we can enhance Python classes with additional methods written
21+
# in pure Python or add some other Python logic
22+
#
23+
24+
# Array4 helper methods
25+
#
26+
Array4_float.to_numpy = array4_to_numpy
27+
Array4_double.to_numpy = array4_to_numpy
28+
Array4_longdouble.to_numpy = array4_to_numpy
29+
30+
Array4_short.to_numpy = array4_to_numpy
31+
Array4_int.to_numpy = array4_to_numpy
32+
Array4_long.to_numpy = array4_to_numpy
33+
Array4_longlong.to_numpy = array4_to_numpy
34+
35+
Array4_ushort.to_numpy = array4_to_numpy
36+
Array4_uint.to_numpy = array4_to_numpy
37+
Array4_ulong.to_numpy = array4_to_numpy
38+
Array4_ulonglong.to_numpy = array4_to_numpy
39+
40+
Array4_float.to_cupy = array4_to_cupy
41+
Array4_double.to_cupy = array4_to_cupy
42+
Array4_longdouble.to_cupy = array4_to_cupy
43+
44+
Array4_short.to_cupy = array4_to_cupy
45+
Array4_int.to_cupy = array4_to_cupy
46+
Array4_long.to_cupy = array4_to_cupy
47+
Array4_longlong.to_cupy = array4_to_cupy
48+
49+
Array4_ushort.to_cupy = array4_to_cupy
50+
Array4_uint.to_cupy = array4_to_cupy
51+
Array4_ulong.to_cupy = array4_to_cupy
52+
Array4_ulonglong.to_cupy = array4_to_cupy

tests/test_multifab.py

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -46,25 +46,25 @@ def test_mfab_loop(make_mfab):
4646
# numpy representation: non-copying view, including the
4747
# guard/ghost region
4848
# note: in numpy, indices are in C-order!
49-
marr_np = np.array(marr, copy=False)
49+
marr_np = marr.to_numpy()
5050

5151
# check the values at start/end are the same: first component
5252
assert marr_np[0, 0, 0, 0] == marr[bx.small_end]
53-
assert marr_np[0, -1, -1, -1] == marr[bx.big_end]
53+
assert marr_np[-1, -1, -1, 0] == marr[bx.big_end]
5454
# same check, but for all components
5555
for n in range(mfab.num_comp):
5656
small_end_comp = list(bx.small_end) + [n]
5757
big_end_comp = list(bx.big_end) + [n]
58-
assert marr_np[n, 0, 0, 0] == marr[small_end_comp]
59-
assert marr_np[n, -1, -1, -1] == marr[big_end_comp]
58+
assert marr_np[0, 0, 0, n] == marr[small_end_comp]
59+
assert marr_np[-1, -1, -1, n] == marr[big_end_comp]
6060

6161
# now we do some faster assignments, using range based access
6262
# this should fail as out-of-bounds, but does not
6363
# does Numpy not check array access for non-owned views?
6464
# marr_np[24:200, :, :, :] = 42.
6565

6666
# all components and all indices set at once to 42
67-
marr_np[:, :, :, :] = 42.0
67+
marr_np[()] = 42.0
6868

6969
# values in start & end still match?
7070
assert marr_np[0, 0, 0, 0] == marr[bx.small_end]
@@ -210,8 +210,7 @@ def test_mfab_ops_cuda_cupy(make_mfab_device):
210210
with cupy.profiler.time_range("assign 3 [()]", color_id=0):
211211
for mfi in mfab_device:
212212
bx = mfi.tilebox().grow(ngv)
213-
marr = mfab_device.array(mfi)
214-
marr_cupy = cp.array(marr, copy=False)
213+
marr_cupy = mfab_device.array(mfi).to_cupy(order="C")
215214
# print(marr_cupy.shape) # 1, 32, 32, 32
216215
# print(marr_cupy.dtype) # float64
217216

@@ -244,8 +243,7 @@ def set_to_five(mm):
244243

245244
for mfi in mfab_device:
246245
bx = mfi.tilebox().grow(ngv)
247-
marr = mfab_device.array(mfi)
248-
marr_cupy = cp.array(marr, copy=False)
246+
marr_cupy = mfab_device.array(mfi).to_cupy(order="C")
249247

250248
# write and read into the marr_cupy
251249
fives_cp = set_to_five(marr_cupy)
@@ -266,8 +264,7 @@ def set_to_seven(x):
266264

267265
for mfi in mfab_device:
268266
bx = mfi.tilebox().grow(ngv)
269-
marr = mfab_device.array(mfi)
270-
marr_cupy = cp.array(marr, copy=False)
267+
marr_cupy = mfab_device.array(mfi).to_cupy(order="C")
271268

272269
# write and read into the marr_cupy
273270
set_to_seven(marr_cupy)

0 commit comments

Comments
 (0)