3
3
import numpy as np
4
4
import numba as nb
5
5
6
+ from functools import partial
7
+
6
8
import dask .array as da
7
9
8
10
from numba import cuda
9
11
12
+ import xarray as xr
10
13
from xarray import DataArray
11
14
12
15
from xrspatial .utils import ngjit
13
16
from xrspatial .utils import has_cuda
14
17
from xrspatial .utils import cuda_args
18
+ from xrspatial .utils import is_cupy_backed
19
+
20
+
21
+ # 3rd-party
22
+ try :
23
+ import cupy
24
+ except ImportError :
25
+ class cupy (object ):
26
+ ndarray = False
15
27
16
28
RADIAN = 180 / np .pi
17
29
18
30
19
31
@ngjit
20
- def _horn_aspect (data ):
32
+ def _cpu (data ):
21
33
out = np .zeros_like (data , dtype = np .float64 )
22
34
out [:] = np .nan
23
35
rows , cols = data .shape
@@ -53,7 +65,7 @@ def _horn_aspect(data):
53
65
54
66
55
67
@cuda .jit (device = True )
56
- def _gpu_aspect (arr ):
68
+ def _gpu (arr ):
57
69
58
70
a = arr [0 , 0 ]
59
71
b = arr [0 , 1 ]
@@ -91,18 +103,66 @@ def _gpu_aspect(arr):
91
103
92
104
93
105
@cuda .jit
94
- def _horn_aspect_cuda (arr , out ):
95
- minus_one = nb .float32 (- 1. )
106
+ def _run_gpu (arr , out ):
96
107
i , j = cuda .grid (2 )
97
108
di = 1
98
109
dj = 1
99
110
if (i - di >= 1 and i + di < out .shape [0 ] - 1 and
100
111
j - dj >= 1 and j + dj < out .shape [1 ] - 1 ):
101
- out [i , j ] = _gpu_aspect (arr [i - di :i + di + 1 , j - dj :j + dj + 1 ])
112
+ out [i , j ] = _gpu (arr [i - di :i + di + 1 , j - dj :j + dj + 1 ])
113
+
114
+
115
+ def _run_cupy (data : cupy .ndarray ) -> cupy .ndarray :
116
+
117
+ pad_rows = 3 // 2
118
+ pad_cols = 3 // 2
119
+ pad_width = ((pad_rows , pad_rows ),
120
+ (pad_cols , pad_cols ))
121
+
122
+ _data = np .pad (data , pad_width = pad_width , mode = "reflect" )
123
+
124
+ griddim , blockdim = cuda_args (_data .shape )
125
+ agg = cupy .empty (_data .shape , dtype = 'f4' )
126
+ agg [:] = cupy .nan
127
+
128
+ _run_gpu [griddim , blockdim ](_data , agg )
129
+ out = agg [pad_rows :- pad_rows , pad_cols :- pad_cols ]
130
+ return out
131
+
132
+
133
+ def _run_dask_cupy (data :da .Array ) -> da .Array :
134
+
135
+ msg = 'Upstream bug in dask prevents cupy backed arrays'
136
+ raise NotImplementedError (msg )
137
+
138
+ # add any func args
139
+ # TODO: probably needs cellsize args
140
+ _func = partial (_run_cupy )
141
+
142
+ out = data .map_overlap (_func ,
143
+ depth = (1 , 1 ),
144
+ boundary = cupy .nan ,
145
+ dtype = cupy .float32 ,
146
+ meta = cupy .array (()))
147
+ return out
148
+
149
+
150
+ def _run_numpy (data :np .ndarray )-> np .ndarray :
151
+ out = _cpu (data )
152
+ return out
102
153
103
154
155
+ def _run_dask_numpy (data :da .Array ) -> da .Array :
156
+ _func = partial (_cpu )
104
157
105
- def aspect (agg , name = 'aspect' , use_cuda = True , pad = True , use_cupy = True ):
158
+ out = data .map_overlap (_func ,
159
+ depth = (1 , 1 ),
160
+ boundary = np .nan ,
161
+ meta = np .array (()))
162
+ return out
163
+
164
+
165
+ def aspect (agg : xr .DataArray , name :str = 'aspect' ):
106
166
"""Returns downward slope direction in compass degrees (0 - 360) with 0 at 12 o'clock.
107
167
108
168
Parameters
@@ -120,46 +180,27 @@ def aspect(agg, name='aspect', use_cuda=True, pad=True, use_cupy=True):
120
180
- Burrough, P. A., and McDonell, R. A., 1998. Principles of Geographical Information Systems (Oxford University Press, New York), pp 406
121
181
"""
122
182
123
- if not isinstance (agg , DataArray ):
124
- raise TypeError ("agg must be instance of DataArray" )
183
+ # numpy case
184
+ if isinstance (agg .data , np .ndarray ):
185
+ out = _run_numpy (agg .data )
125
186
126
- if has_cuda () and use_cuda :
127
-
128
- if pad :
129
- pad_rows = 3 // 2
130
- pad_cols = 3 // 2
131
- pad_width = ((pad_rows , pad_rows ),
132
- (pad_cols , pad_cols ))
133
- else :
134
- # If padding is not desired, set pads to 0
135
- pad_rows = 0
136
- pad_cols = 0
137
- pad_width = 0
138
-
139
- data = np .pad (agg .data , pad_width = pad_width , mode = "reflect" )
140
-
141
- griddim , blockdim = cuda_args (data .shape )
142
- out = np .empty (data .shape , dtype = 'f4' )
143
- out [:] = np .nan
144
-
145
- if use_cupy :
146
- import cupy
147
- out = cupy .asarray (out )
148
-
149
- _horn_aspect_cuda [griddim , blockdim ](data , out )
150
- if pad :
151
- out = out [pad_rows :- pad_rows , pad_cols :- pad_cols ]
187
+ # cupy case
188
+ elif has_cuda () and isinstance (agg .data , cupy .ndarray ):
189
+ out = _run_cupy (agg .data )
152
190
191
+ # dask + cupy case
192
+ elif has_cuda () and isinstance (agg .data , da .Array ) and is_cupy_backed (agg ):
193
+ out = _run_dask_cupy (agg .data )
194
+
195
+ # dask + numpy case
153
196
elif isinstance (agg .data , da .Array ):
154
- out = agg .data .map_overlap (_horn_aspect ,
155
- depth = (1 , 1 ),
156
- boundary = np .nan ,
157
- meta = np .array (()))
197
+ out = _run_dask_numpy (agg .data )
198
+
158
199
else :
159
- out = _horn_aspect ( agg .data )
200
+ raise TypeError ( 'Unsupported Array Type: {}' . format ( type ( agg .data )) )
160
201
161
- return DataArray (out ,
162
- name = name ,
163
- dims = agg .dims ,
164
- coords = agg .coords ,
165
- attrs = agg .attrs )
202
+ return xr . DataArray (out ,
203
+ name = name ,
204
+ coords = agg .coords ,
205
+ dims = agg .dims ,
206
+ attrs = agg .attrs )
0 commit comments