feectools 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. feectools/__init__.py +0 -0
  2. feectools/accelerate/__init__.py +0 -0
  3. feectools/accelerate/accelerate.py +220 -0
  4. feectools/accelerate/compile_psydac.mk +52 -0
  5. feectools/api/__init__.py +0 -0
  6. feectools/api/essential_bc.py +122 -0
  7. feectools/api/fem_bilinear_form.py +2226 -0
  8. feectools/api/fem_common.py +286 -0
  9. feectools/api/fem_sum_form.py +123 -0
  10. feectools/api/settings.py +82 -0
  11. feectools/core/__init__.py +11 -0
  12. feectools/core/bsplines.py +1107 -0
  13. feectools/core/bsplines_kernels.py +1349 -0
  14. feectools/core/field_evaluation_kernels.py +5015 -0
  15. feectools/core/tests/__init__.py +0 -0
  16. feectools/core/tests/test_bsplines.py +263 -0
  17. feectools/core/tests/test_bsplines_kernel.py +40 -0
  18. feectools/core/tests/test_bsplines_pyccel.py +752 -0
  19. feectools/ddm/__init__.py +3 -0
  20. feectools/ddm/basic.py +78 -0
  21. feectools/ddm/blocking_data_exchanger.py +348 -0
  22. feectools/ddm/cart.py +1835 -0
  23. feectools/ddm/interface_data_exchanger.py +122 -0
  24. feectools/ddm/mpi.py +109 -0
  25. feectools/ddm/nonblocking_data_exchanger.py +331 -0
  26. feectools/ddm/partition.py +207 -0
  27. feectools/ddm/petsc.py +112 -0
  28. feectools/ddm/tests/__init__.py +0 -0
  29. feectools/ddm/tests/test_cart_1d.py +138 -0
  30. feectools/ddm/tests/test_cart_2d.py +164 -0
  31. feectools/ddm/tests/test_cart_3d.py +158 -0
  32. feectools/ddm/tests/test_multicart_2d.py +173 -0
  33. feectools/ddm/tests/test_partition.py +124 -0
  34. feectools/ddm/utilities.py +24 -0
  35. feectools/feec/__init__.py +0 -0
  36. feectools/feec/derivatives.py +780 -0
  37. feectools/feec/dof_kernels.py +210 -0
  38. feectools/feec/global_geometric_projectors.py +1073 -0
  39. feectools/feec/hodge.py +148 -0
  40. feectools/fem/__init__.py +0 -0
  41. feectools/fem/basic.py +465 -0
  42. feectools/fem/grid.py +181 -0
  43. feectools/fem/partitioning.py +344 -0
  44. feectools/fem/projectors.py +160 -0
  45. feectools/fem/splines.py +559 -0
  46. feectools/fem/tensor.py +1393 -0
  47. feectools/fem/tests/__init__.py +0 -0
  48. feectools/fem/tests/analytical_profiles_1d.py +100 -0
  49. feectools/fem/tests/analytical_profiles_base.py +34 -0
  50. feectools/fem/tests/splines_error_bounds.py +155 -0
  51. feectools/fem/tests/test_spline_histopolation.py +120 -0
  52. feectools/fem/tests/test_spline_interpolation.py +182 -0
  53. feectools/fem/tests/test_splines.py +184 -0
  54. feectools/fem/tests/test_splines_par.py +46 -0
  55. feectools/fem/tests/test_vector_spaces.py +150 -0
  56. feectools/fem/tests/utilities.py +47 -0
  57. feectools/fem/vector.py +729 -0
  58. feectools/linalg/__init__.py +0 -0
  59. feectools/linalg/basic.py +1386 -0
  60. feectools/linalg/block.py +1451 -0
  61. feectools/linalg/direct_solvers.py +201 -0
  62. feectools/linalg/fft.py +258 -0
  63. feectools/linalg/kernels/__init__.py +0 -0
  64. feectools/linalg/kernels/axpy_kernels.py +57 -0
  65. feectools/linalg/kernels/inner_kernels.py +100 -0
  66. feectools/linalg/kernels/matvec_kernels.py +206 -0
  67. feectools/linalg/kernels/stencil2IJV_kernels.py +227 -0
  68. feectools/linalg/kernels/stencil2coo_kernels.py +179 -0
  69. feectools/linalg/kernels/transpose_kernels.py +263 -0
  70. feectools/linalg/kron.py +911 -0
  71. feectools/linalg/solvers.py +1914 -0
  72. feectools/linalg/sparse.py +114 -0
  73. feectools/linalg/stencil.py +2923 -0
  74. feectools/linalg/stencil_dot_kernels.py +317 -0
  75. feectools/linalg/stencil_transpose_kernels.py +372 -0
  76. feectools/linalg/tests/__init__.py +0 -0
  77. feectools/linalg/tests/test_block.py +1588 -0
  78. feectools/linalg/tests/test_fft.py +106 -0
  79. feectools/linalg/tests/test_kron_stencil_matrix.py +114 -0
  80. feectools/linalg/tests/test_linalg.py +1065 -0
  81. feectools/linalg/tests/test_matrix_free.py +128 -0
  82. feectools/linalg/tests/test_solvers.py +213 -0
  83. feectools/linalg/tests/test_stencil_interface_matrix.py +379 -0
  84. feectools/linalg/tests/test_stencil_vector.py +1036 -0
  85. feectools/linalg/tests/test_stencil_vector_space.py +440 -0
  86. feectools/linalg/topetsc.py +522 -0
  87. feectools/linalg/utilities.py +200 -0
  88. feectools/utilities/__init__.py +0 -0
  89. feectools/utilities/quadratures.py +113 -0
  90. feectools/utilities/utils.py +166 -0
  91. feectools/version.py +1 -0
  92. feectools-0.1.0.dist-info/METADATA +66 -0
  93. feectools-0.1.0.dist-info/RECORD +98 -0
  94. feectools-0.1.0.dist-info/WHEEL +5 -0
  95. feectools-0.1.0.dist-info/entry_points.txt +3 -0
  96. feectools-0.1.0.dist-info/licenses/AUTHORS +22 -0
  97. feectools-0.1.0.dist-info/licenses/LICENSE +21 -0
  98. feectools-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,3 @@
1
+ __all__ = ['cart']
2
+
3
+ from feectools.ddm import cart
feectools/ddm/basic.py ADDED
@@ -0,0 +1,78 @@
1
+ # coding: utf-8
2
+
3
+ from abc import ABC, abstractmethod
4
+
5
+ __all__ = ('CartDataExchanger',)
6
+ #===============================================================================
7
+ class CartDataExchanger(ABC):
8
+ """
9
+ Type that takes care of updating the ghost regions (padding) of a
10
+ multi-dimensional array distributed according to the given Cartesian
11
+ decomposition of a tensor-product grid of coefficients.
12
+
13
+ Each coefficient in the decomposed grid may have multiple components,
14
+ contiguous in memory.
15
+
16
+ Parameters
17
+ ----------
18
+ cart : feectools.ddm.CartDecomposition
19
+ Object that contains all information about the Cartesian decomposition
20
+ of a tensor-product grid of coefficients.
21
+
22
+ dtype : [type | str | numpy.dtype | mpi4py.MPI.Datatype]
23
+ Datatype of single coefficient (if scalar) or of each of its
24
+ components (if vector).
25
+
26
+ coeff_shape : [tuple(int) | list(int)]
27
+ Shape of a single coefficient, if this is multi-dimensional
28
+ (optional: by default, we assume scalar coefficients).
29
+
30
+ """
31
+
32
+ #---------------------------------------------------------------------------
33
+ # Public interface
34
+ #---------------------------------------------------------------------------
35
+
36
+ @abstractmethod
37
+ def prepare_communications(self, u):
38
+ pass
39
+
40
+ @abstractmethod
41
+ def start_update_ghost_regions( self, array, requests ):
42
+ """
43
+ Update ghost regions in a numpy array with dimensions compatible with
44
+ CartDecomposition (and coeff_shape) provided at initialization.
45
+
46
+ Parameters
47
+ ----------
48
+ array : numpy.ndarray
49
+ Multidimensional array corresponding to local subdomain in
50
+ decomposed tensor grid, including padding.
51
+
52
+ requests : tuple|None
53
+ The requests of the communications.
54
+
55
+ """
56
+
57
+ @abstractmethod
58
+ def end_update_ghost_regions( self, array, requests ):
59
+ pass
60
+
61
+ @abstractmethod
62
+ def start_exchange_assembly_data( self, array ):
63
+ """
64
+ Update ghost regions after the assembly algorithm in a numpy array
65
+ with dimensions compatible with CartDecomposition (and coeff_shape)
66
+ provided at initialization.
67
+
68
+ Parameters
69
+ ----------
70
+ array : numpy.ndarray
71
+ Multidimensional array corresponding to local subdomain in
72
+ decomposed tensor grid, including padding.
73
+ """
74
+
75
+ @abstractmethod
76
+ def end_exchange_assembly_data( self, array ):
77
+ pass
78
+
@@ -0,0 +1,348 @@
1
+ # coding: utf-8
2
+
3
+ import numpy as np
4
+ from feectools.ddm.mpi import mpi as MPI
5
+
6
+ from .cart import CartDecomposition, find_mpi_type
7
+ from .basic import CartDataExchanger
8
+
9
+ __all__ = ('BlockingCartDataExchanger',)
10
+
11
+ class BlockingCartDataExchanger(CartDataExchanger):
12
+ """
13
+ Type that takes care of updating the ghost regions (padding) of a
14
+ multi-dimensional array distributed according to the given Cartesian
15
+ decomposition of a tensor-product grid of coefficients.
16
+
17
+ Each coefficient in the decomposed grid may have multiple components,
18
+ contiguous in memory.
19
+
20
+ Parameters
21
+ ----------
22
+ cart : feectools.ddm.CartDecomposition
23
+ Object that contains all information about the Cartesian decomposition
24
+ of a tensor-product grid of coefficients.
25
+
26
+ dtype : [type | str | numpy.dtype | mpi4py.MPI.Datatype]
27
+ Datatype of single coefficient (if scalar) or of each of its
28
+ components (if vector).
29
+
30
+ coeff_shape : [tuple(int) | list(int)]
31
+ Shape of a single coefficient, if this is multi-dimensional
32
+ (optional: by default, we assume scalar coefficients).
33
+
34
+ """
35
+ def __init__( self, cart, dtype, *, coeff_shape=(), assembly=False, axis=None, shape=None ):
36
+
37
+ self._send_types, self._recv_types = self._create_buffer_types(
38
+ cart, dtype, coeff_shape=coeff_shape )
39
+
40
+ self._cart = cart
41
+ self._comm = cart.comm_cart
42
+ self._axis = axis
43
+
44
+ if assembly:
45
+ self._assembly_send_types, self._assembly_recv_types = self._create_assembly_buffer_types(
46
+ cart, dtype, coeff_shape=coeff_shape, axis=axis, shape=shape)
47
+
48
+ #---------------------------------------------------------------------------
49
+ # Public interface
50
+ #---------------------------------------------------------------------------
51
+ def get_send_type( self, *args ):
52
+ direction = args[0]
53
+ disp = args[1]
54
+ return self._send_types[direction, disp]
55
+
56
+ # ...
57
+ def get_recv_type( self, *args ):
58
+ direction = args[0]
59
+ disp = args[1]
60
+ return self._recv_types[direction, disp]
61
+
62
+ # ...
63
+ def get_assembly_send_type( self,*args ):
64
+ direction = args[0]
65
+ disp = args[1]
66
+ return self._assembly_send_types[direction, disp]
67
+
68
+ # ...
69
+ def get_assembly_recv_type( self, *args ):
70
+ direction = args[0]
71
+ disp = args[1]
72
+ return self._assembly_recv_types[direction, disp]
73
+
74
+ # ...
75
+ def prepare_communications(self, u):
76
+ pass
77
+
78
+ # ...
79
+ def start_update_ghost_regions( self, array, requests ):
80
+
81
+ assert isinstance( array, np.ndarray )
82
+
83
+ # Shortcuts
84
+ cart = self._cart
85
+ comm = self._comm
86
+
87
+ # Choose non-negative invertible function tag(disp) >= 0
88
+ # NOTES:
89
+ # . different values of disp must return different tags!
90
+ # . tag at receiver must match message tag at sender
91
+ tag = lambda disp: 42+disp
92
+
93
+ for direction in range( self._cart.ndim ):
94
+ # Requests' handles
95
+ requests = []
96
+
97
+ # Start receiving data (MPI_IRECV)
98
+ for disp in [-1,1]:
99
+ info = cart.get_shift_info( direction, disp )
100
+ recv_typ = self.get_recv_type ( direction, disp )
101
+ recv_buf = (array, 1, recv_typ)
102
+ recv_req = comm.Irecv( recv_buf, info['rank_source'], tag(disp) )
103
+ requests.append( recv_req )
104
+
105
+ # Start sending data (MPI_ISEND)
106
+ for disp in [-1,1]:
107
+ info = cart.get_shift_info( direction, disp )
108
+ send_typ = self.get_send_type ( direction, disp )
109
+ send_buf = (array, 1, send_typ)
110
+ send_req = comm.Isend( send_buf, info['rank_dest'], tag(disp) )
111
+ requests.append( send_req )
112
+
113
+ # Wait for end of data exchange (MPI_WAITALL)
114
+ MPI.Request.Waitall( requests )
115
+
116
+ def end_update_ghost_regions(self, array, requests ):
117
+ pass
118
+
119
+ # ...
120
+ def start_exchange_assembly_data( self, array ):
121
+
122
+ assert isinstance( array, np.ndarray )
123
+
124
+ # Shortcuts
125
+ cart = self._cart
126
+ comm = self._comm
127
+ gcomm = comm
128
+ ndim = cart.ndim
129
+
130
+ # Choose non-negative invertible function tag(disp) >= 0
131
+ # NOTES:
132
+ # . different values of disp must return different tags!
133
+ # . tag at receiver must match message tag at sender
134
+ tag = lambda disp: 42+disp
135
+
136
+ # Requests' handles
137
+
138
+ for direction in range( ndim ):
139
+ if direction == self._axis: continue
140
+ if self._axis is not None: comm = cart.subcomm[direction]
141
+
142
+ # Start receiving data (MPI_IRECV)
143
+ disp = 1
144
+ info = cart.get_shift_info( direction, disp )
145
+ recv_typ = self.get_assembly_recv_type ( direction, disp )
146
+ rank_source = info['rank_source']
147
+
148
+ if self._axis is not None:
149
+ rank_source = gcomm.group.Translate_ranks(np.array([rank_source]), comm.group)[0]
150
+
151
+ recv_buf = (array, 1, recv_typ)
152
+ recv_req = comm.Irecv( recv_buf, rank_source, tag(disp) )
153
+
154
+ # Start sending data (MPI_ISEND)
155
+ send_typ = self.get_assembly_send_type ( direction, disp )
156
+ rank_dest = info['rank_dest']
157
+
158
+ if self._axis is not None:
159
+ rank_dest = gcomm.group.Translate_ranks(np.array([rank_dest]), comm.group)[0]
160
+
161
+ send_buf = (array, 1, send_typ)
162
+ send_req = comm.Isend( send_buf, rank_dest, tag(disp) )
163
+
164
+ # Wait for end of data exchange (MPI_WAITALL)
165
+ MPI.Request.Waitall( [recv_req, send_req] )
166
+
167
+ if disp == 1:
168
+ info = cart.get_shift_info( direction, disp )
169
+ pads = [0]*ndim
170
+ pads[direction] = cart._pads[direction]*cart._shifts[direction]
171
+ idx_from = tuple(slice(s,s+b) for s,b in zip(info['recv_starts'],info['buf_shape']))
172
+ idx_to = tuple(slice(s+p,s+b+p) for s,b,p in zip(info['recv_starts'],info['buf_shape'],pads))
173
+ array[idx_to] += array[idx_from]
174
+ else:
175
+ info = cart.get_shift_info( direction, disp )
176
+ pads = [0]*ndim
177
+ pads[direction] = cart._pads[direction]*cart._shifts[direction]
178
+ idx_from = tuple(slice(s,s+b) for s,b in zip(info['recv_starts'],info['buf_shape']))
179
+ idx_to = tuple(slice(s-p,s+b-p) for s,b,p in zip(info['recv_starts'],info['buf_shape'],pads))
180
+ array[idx_to] += array[idx_from]
181
+
182
+ def end_exchange_assembly_data( self, array ):
183
+ pass
184
+
185
+ #---------------------------------------------------------------------------
186
+ # Private methods
187
+ #---------------------------------------------------------------------------
188
+ @staticmethod
189
+ def _create_buffer_types( cart, dtype, *, coeff_shape=() ):
190
+ """
191
+ Create MPI subarray datatypes for updating the ghost regions (padding)
192
+ of a multi-dimensional array distributed according to the given Cartesian
193
+ decomposition of a tensor-product grid of coefficients.
194
+
195
+ MPI requires a subarray datatype for accessing non-contiguous slices of
196
+ a multi-dimensional array; this is a typical situation when updating the
197
+ ghost regions.
198
+
199
+ Each coefficient in the decomposed grid may have multiple components,
200
+ contiguous in memory.
201
+
202
+ Parameters
203
+ ----------
204
+ cart : feectools.ddm.CartDecomposition
205
+ Object that contains all information about the Cartesian decomposition
206
+ of a tensor-product grid of coefficients.
207
+
208
+ dtype : [type | str | numpy.dtype | mpi4py.MPI.Datatype]
209
+ Datatype of single coefficient (if scalar) or of each of its
210
+ components (if vector).
211
+
212
+ coeff_shape : [tuple(int) | list(int)]
213
+ Shape of a single coefficient, if this is multidimensional
214
+ (optional: by default, we assume scalar coefficients).
215
+
216
+ Returns
217
+ -------
218
+ send_types : dict
219
+ Dictionary of MPI subarray datatypes for SEND BUFFERS, accessed
220
+ through the integer pair (direction, displacement) as key;
221
+ 'direction' takes values from 0 to ndim, 'disp' is -1 or +1.
222
+
223
+ recv_types : dict
224
+ Dictionary of MPI subarray datatypes for RECEIVE BUFFERS, accessed
225
+ through the integer pair (direction, displacement) as key;
226
+ 'direction' takes values from 0 to ndim, 'disp' is -1 or +1.
227
+
228
+ """
229
+ assert isinstance( cart, CartDecomposition )
230
+
231
+ mpi_type = find_mpi_type( dtype )
232
+
233
+ # Possibly, each coefficient could have multiple components
234
+ coeff_shape = list( coeff_shape )
235
+ coeff_start = [0] * len( coeff_shape )
236
+
237
+ data_shape = list( cart.shape ) + coeff_shape
238
+ send_types = {}
239
+ recv_types = {}
240
+
241
+ for direction in range( cart.ndim ):
242
+ for disp in [-1, 1]:
243
+ info = cart.get_shift_info( direction, disp )
244
+
245
+ buf_shape = list( info[ 'buf_shape' ] ) + coeff_shape
246
+ send_starts = list( info['send_starts'] ) + coeff_start
247
+ recv_starts = list( info['recv_starts'] ) + coeff_start
248
+
249
+ send_types[direction,disp] = mpi_type.Create_subarray(
250
+ sizes = data_shape ,
251
+ subsizes = buf_shape ,
252
+ starts = send_starts,
253
+ ).Commit()
254
+
255
+ recv_types[direction,disp] = mpi_type.Create_subarray(
256
+ sizes = data_shape ,
257
+ subsizes = buf_shape ,
258
+ starts = recv_starts,
259
+ ).Commit()
260
+
261
+ return send_types, recv_types
262
+
263
+ # ...
264
+ @staticmethod
265
+ def _create_assembly_buffer_types( cart, dtype, *, coeff_shape=(), axis=None, shape=None ):
266
+ """
267
+ Create MPI subarray datatypes for updating the ghost regions (padding)
268
+ of a multi-dimensional array distributed according to the given Cartesian
269
+ decomposition of a tensor-product grid of coefficients.
270
+ MPI requires a subarray datatype for accessing non-contiguous slices of
271
+ a multi-dimensional array; this is a typical situation when updating the
272
+ ghost regions.
273
+ Each coefficient in the decomposed grid may have multiple components,
274
+ contiguous in memory.
275
+
276
+ Parameters
277
+ ----------
278
+ cart : feectools.ddm.CartDecomposition
279
+ Object that contains all information about the Cartesian decomposition
280
+ of a tensor-product grid of coefficients.
281
+
282
+ dtype : [type | str | numpy.dtype | mpi4py.MPI.Datatype]
283
+ Datatype of single coefficient (if scalar) or of each of its
284
+ components (if vector).
285
+
286
+ coeff_shape : [tuple(int) | list(int)]
287
+ Shape of a single coefficient, if this is multidimensional
288
+ (optional: by default, we assume scalar coefficients).
289
+
290
+ axis: int,optional
291
+ The axis of which we don't update the ghost regions.
292
+
293
+ shape:
294
+ the shape of data when axis is not None
295
+
296
+ Returns
297
+ -------
298
+ send_types : dict
299
+ Dictionary of MPI subarray datatypes for SEND BUFFERS, accessed
300
+ through the integer pair (direction, displacement) as key;
301
+ 'direction' takes values from 0 to ndim, 'disp' is -1 or +1.
302
+
303
+ recv_types : dict
304
+ Dictionary of MPI subarray datatypes for RECEIVE BUFFERS, accessed
305
+ through the integer pair (direction, displacement) as key;
306
+ 'direction' takes values from 0 to ndim, 'disp' is -1 or +1.
307
+ """
308
+ assert isinstance( cart, CartDecomposition )
309
+
310
+ mpi_type = find_mpi_type( dtype )
311
+
312
+ # Possibly, each coefficient could have multiple components
313
+ coeff_shape = list( coeff_shape )
314
+ coeff_start = [0] * len( coeff_shape )
315
+
316
+ data_shape = list( cart.shape ) + coeff_shape
317
+ send_types = {}
318
+ recv_types = {}
319
+
320
+ if axis is not None:
321
+ data_shape[axis] = shape[axis]
322
+
323
+ for direction in range( cart.ndim ):
324
+ for disp in [-1, 1]:
325
+ info = cart.get_shift_info( direction, disp )
326
+
327
+ buf_shape = list( info[ 'buf_shape' ] ) + coeff_shape
328
+ send_starts = list( info['send_assembly_starts'] ) + coeff_start
329
+ recv_starts = list( info['recv_assembly_starts'] ) + coeff_start
330
+ if direction == axis:continue
331
+ if axis is not None:
332
+ buf_shape[axis] = shape[axis]
333
+ send_starts[axis] = 0
334
+ recv_starts[axis] = 0
335
+
336
+ send_types[direction,disp] = mpi_type.Create_subarray(
337
+ sizes = data_shape ,
338
+ subsizes = buf_shape ,
339
+ starts = send_starts,
340
+ ).Commit()
341
+
342
+ recv_types[direction,disp] = mpi_type.Create_subarray(
343
+ sizes = data_shape ,
344
+ subsizes = buf_shape ,
345
+ starts = recv_starts,
346
+ ).Commit()
347
+
348
+ return send_types, recv_types