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,122 @@
1
+ # coding: utf-8
2
+
3
+ from feectools.ddm.mpi import mpi as MPI
4
+
5
+ from .cart import InterfaceCartDecomposition, find_mpi_type
6
+
7
+ __all__ = ('InterfaceCartDataExchanger',)
8
+
9
+ class InterfaceCartDataExchanger:
10
+ """
11
+ This takes care of updating the ghost regions between two sides of an interface for a
12
+ multi-dimensional array distributed according to the given Cartesian
13
+ decomposition of a tensor-product grid of coefficients.
14
+
15
+ Parameters
16
+ ----------
17
+ cart : feectools.ddm.InterfaceCartDecomposition
18
+ Object that contains all information about the Cartesian decomposition
19
+ of a tensor-product grid of coefficients.
20
+
21
+ dtype : [type | str | numpy.dtype | mpi4py.MPI.Datatype]
22
+ Datatype of single coefficient (if scalar) or of each of its
23
+ components (if vector).
24
+
25
+ coeff_shape : [tuple(int) | list(int)]
26
+ Shape of a single coefficient, if this is multi-dimensional
27
+ (optional: by default, we assume scalar coefficients).
28
+
29
+ """
30
+ def __init__(self, cart, dtype, *, coeff_shape=()):
31
+
32
+ assert isinstance(cart, InterfaceCartDecomposition)
33
+
34
+ send_types, recv_types = self._create_buffer_types( cart, dtype , coeff_shape=coeff_shape)
35
+
36
+ self._cart = cart
37
+ self._dtype = dtype
38
+ self._send_types = send_types
39
+ self._recv_types = recv_types
40
+ self._dest_ranks = cart.get_interface_communication_infos( cart.axis )['dest_ranks']
41
+ self._source_ranks = cart.get_interface_communication_infos( cart.axis )['source_ranks']
42
+
43
+
44
+ # ...
45
+ def update_ghost_regions( self, array_minus=None, array_plus=None ):
46
+ req = self.start_update_ghost_regions(array_minus, array_plus)
47
+ self.end_update_ghost_regions(req)
48
+
49
+ # ...
50
+ def start_update_ghost_regions( self, array_minus=None, array_plus=None ):
51
+ send_req = []
52
+ recv_req = []
53
+ cart = self._cart
54
+ intercomm = cart.intercomm
55
+
56
+ for i,(st,rank) in enumerate(zip(self._send_types, self._dest_ranks)):
57
+
58
+ if cart._local_rank_minus is not None and array_minus is not None:
59
+ send_buf = (array_minus, 1, st)
60
+ send_req.append(intercomm.Isend( send_buf, rank ))
61
+ elif cart._local_rank_plus is not None and array_plus is not None:
62
+ send_buf = (array_plus, 1, st)
63
+ send_req.append(intercomm.Isend( send_buf, rank ))
64
+
65
+ for i,(rt,rank) in enumerate(zip(self._recv_types, self._source_ranks)):
66
+
67
+ if cart._local_rank_minus is not None and array_plus is not None:
68
+ recv_buf = (array_plus, 1, rt)
69
+ recv_req.append(intercomm.Irecv( recv_buf, rank ))
70
+ elif cart._local_rank_plus is not None and array_minus is not None:
71
+ recv_buf = (array_minus, 1, rt)
72
+ recv_req.append(intercomm.Irecv( recv_buf, rank ))
73
+
74
+ return send_req + recv_req
75
+
76
+ def end_update_ghost_regions(self, req):
77
+ MPI.Request.Waitall(req)
78
+
79
+ @staticmethod
80
+ def _create_buffer_types( cart, dtype , *, coeff_shape=()):
81
+
82
+ assert isinstance( cart, InterfaceCartDecomposition )
83
+
84
+ mpi_type = find_mpi_type( dtype )
85
+ info = cart.get_interface_communication_infos( cart.axis )
86
+
87
+ # Possibly, each coefficient could have multiple components
88
+ coeff_shape = list( coeff_shape )
89
+ coeff_start = [0] * len( coeff_shape )
90
+
91
+ send_types = [None]*len(info['dest_ranks'])
92
+ axis = cart.axis
93
+ for i in range(len(info['dest_ranks'])):
94
+
95
+ gbuf_shape = list(info['gbuf_send_shape'][i]) + coeff_shape
96
+ buf_shape = list(info['buf_send_shape'][i]) + coeff_shape
97
+ send_starts = list(info['gbuf_send_starts'][i]) + coeff_start
98
+
99
+ if coeff_shape:
100
+ gbuf_shape[axis] = info['gbuf_recv_shape'][0][axis]
101
+ buf_shape[axis] = info['buf_recv_shape'][0][axis]
102
+ send_starts[axis] = info['gbuf_recv_starts'][0][axis]
103
+
104
+ send_types[i] = mpi_type.Create_subarray(
105
+ sizes = gbuf_shape,
106
+ subsizes = buf_shape,
107
+ starts = send_starts).Commit()
108
+
109
+ recv_types = [None]*len(info['source_ranks'])
110
+
111
+ for i in range(len(info['source_ranks'])):
112
+
113
+ gbuf_shape = list(info['gbuf_recv_shape'][i]) + coeff_shape
114
+ buf_shape = list(info['buf_recv_shape'][i]) + coeff_shape
115
+ recv_starts = list(info['gbuf_recv_starts'][i]) + coeff_start
116
+
117
+ recv_types[i] = mpi_type.Create_subarray(
118
+ sizes = gbuf_shape,
119
+ subsizes = buf_shape,
120
+ starts = recv_starts).Commit()
121
+
122
+ return send_types, recv_types
feectools/ddm/mpi.py ADDED
@@ -0,0 +1,109 @@
1
+ from dataclasses import dataclass
2
+ from time import time
3
+ from typing import TYPE_CHECKING
4
+
5
+
6
+ # Might not be needed
7
+ class MPICommWrapper:
8
+ def __init__(self, use_mpi=True):
9
+ self.use_mpi = use_mpi
10
+ if use_mpi:
11
+ from mpi4py import MPI
12
+
13
+ self.comm = MPI.COMM_WORLD
14
+ else:
15
+ self.comm = MockComm()
16
+
17
+ def __getattr__(self, name):
18
+ return getattr(self.comm, name)
19
+
20
+
21
+ class MockComm:
22
+ def __getattr__(self, name):
23
+ # Return a function that does nothing and returns None
24
+ def dummy(*args, **kwargs):
25
+ return None
26
+
27
+ return dummy
28
+
29
+ # Override some functions
30
+ def Get_rank(self):
31
+ return 0
32
+
33
+ def Get_size(self):
34
+ return 1
35
+
36
+ def Barrier(self):
37
+ return
38
+
39
+
40
+ class MPIwrapper:
41
+ def __init__(
42
+ self,
43
+ use_mpi: bool = False,
44
+ verbose: bool = False,
45
+ ):
46
+ self.use_mpi = use_mpi
47
+ if use_mpi:
48
+ from mpi4py import MPI
49
+
50
+ self._MPI = MPI
51
+ if verbose:
52
+ print("MPI is enabled")
53
+ else:
54
+ self._MPI = MockMPI()
55
+ if verbose:
56
+ print("MPI is NOT enabled")
57
+
58
+ @property
59
+ def MPI(self):
60
+ return self._MPI
61
+
62
+
63
+ class MockMPI:
64
+ def __getattr__(self, name):
65
+ # Return a function that does nothing and returns None
66
+ def dummy(*args, **kwargs):
67
+ return None
68
+
69
+ return dummy
70
+
71
+ # Override some functions
72
+ @property
73
+ def COMM_WORLD(self):
74
+ return MockComm()
75
+
76
+ # def comm_Get_rank(self):
77
+ # return 0
78
+
79
+ # def comm_Get_size(self):
80
+ # return 1
81
+
82
+
83
+ try:
84
+ from mpi4py import MPI
85
+
86
+ _comm = MPI.COMM_WORLD
87
+ rank = _comm.Get_rank()
88
+ size = _comm.Get_size()
89
+ mpi_enabled = True
90
+ except ImportError:
91
+ # mpi4py not installed
92
+ mpi_enabled = False
93
+ except Exception:
94
+ # mpi4py installed but not running under mpirun
95
+ mpi_enabled = False
96
+
97
+ # TODO: add environment variable for mpi use
98
+ mpi_wrapper = MPIwrapper(
99
+ use_mpi=mpi_enabled,
100
+ verbose=False,
101
+ )
102
+
103
+ # TYPE_CHECKING is True when type checking (e.g., mypy), but False at runtime.
104
+ if TYPE_CHECKING:
105
+ from mpi4py import MPI
106
+
107
+ mpi = MPI
108
+ else:
109
+ mpi = mpi_wrapper.MPI
@@ -0,0 +1,331 @@
1
+ # coding: utf-8
2
+
3
+ import numpy as np
4
+ from itertools import product
5
+
6
+ from feectools.ddm.mpi import mpi as MPI
7
+ from .cart import CartDecomposition, find_mpi_type
8
+ from .basic import CartDataExchanger
9
+
10
+ __all__ = ('NonBlockingCartDataExchanger',)
11
+
12
+ class NonBlockingCartDataExchanger(CartDataExchanger):
13
+ """
14
+ Type that takes care of updating the ghost regions (padding) of a
15
+ multi-dimensional array distributed according to the given Cartesian
16
+ decomposition of a tensor-product grid of coefficients.
17
+
18
+ Each coefficient in the decomposed grid may have multiple components,
19
+ contiguous in memory.
20
+
21
+ Parameters
22
+ ----------
23
+ cart : feectools.ddm.CartDecomposition
24
+ Object that contains all information about the Cartesian decomposition
25
+ of a tensor-product grid of coefficients.
26
+
27
+ dtype : [type | str | numpy.dtype | mpi4py.MPI.Datatype]
28
+ Datatype of single coefficient (if scalar) or of each of its
29
+ components (if vector).
30
+
31
+ coeff_shape : [tuple(int) | list(int)]
32
+ Shape of a single coefficient, if this is multi-dimensional
33
+ (optional: by default, we assume scalar coefficients).
34
+
35
+ """
36
+ def __init__( self, cart, dtype, *, coeff_shape=(), assembly=False, axis=None, shape=None ):
37
+
38
+ self._send_types, self._recv_types = self._create_buffer_types(
39
+ cart, dtype, coeff_shape=coeff_shape )
40
+
41
+ self._cart = cart
42
+ self._comm = cart.comm_cart
43
+ self._axis = axis
44
+
45
+ if assembly:
46
+ self._assembly_send_types, self._assembly_recv_types = self._create_assembly_buffer_types(
47
+ cart, dtype, coeff_shape=coeff_shape, axis=axis, shape=shape)
48
+ #---------------------------------------------------------------------------
49
+ # Public interface
50
+ #---------------------------------------------------------------------------
51
+ def get_send_type( self, *args ):
52
+ shift = args[0]
53
+ return self._send_types[shift]
54
+
55
+ # ...
56
+ def get_recv_type( self, *args ):
57
+ shift = args[0]
58
+ return self._recv_types[shift]
59
+
60
+ # ...
61
+ def get_assembly_send_type( self, *args ):
62
+ direction = args[0]
63
+ disp = args[1]
64
+ return self._assembly_send_types[direction, disp]
65
+
66
+ # ...
67
+ def get_assembly_recv_type( self, *args ):
68
+ direction = args[0]
69
+ disp = args[1]
70
+ return self._assembly_recv_types[direction, disp]
71
+
72
+ # ...
73
+ def prepare_communications(self, u):
74
+
75
+ # Requests' handles
76
+ requests = []
77
+ cart = self._cart
78
+ for shift in product( [-1,0,1], repeat=cart._ndims ):
79
+ if all(s==0 for s in shift):
80
+ continue
81
+
82
+ info = cart.get_shift_info_non_blocking( shift )
83
+ comm = cart._comm_cart
84
+
85
+ recv_typ = self.get_recv_type( shift )
86
+ if recv_typ != MPI.DATATYPE_NULL:
87
+ recv_buf = (u, 1, recv_typ)
88
+ recv_req = comm.Recv_init( recv_buf, info['rank_source'], info['tag'] )
89
+ requests.append( recv_req )
90
+
91
+ send_typ = self.get_send_type( shift )
92
+ if send_typ != MPI.DATATYPE_NULL:
93
+ send_buf = (u, 1, send_typ)
94
+ send_req = comm.Send_init( send_buf, info['rank_dest'], info['tag'] )
95
+ requests.append( send_req )
96
+
97
+ return tuple(requests)
98
+
99
+ def start_update_ghost_regions(self, array, requests ):
100
+ MPI.Prequest.Startall( requests )
101
+
102
+ def end_update_ghost_regions(self, array, requests):
103
+ MPI.Prequest.Waitall ( requests )
104
+
105
+ # ...
106
+ def start_exchange_assembly_data( self, array ):
107
+
108
+ assert isinstance( array, np.ndarray )
109
+
110
+ # Shortcuts
111
+ cart = self._cart
112
+ comm = self._comm
113
+ gcomm = comm
114
+ ndim = cart.ndim
115
+
116
+ # Choose non-negative invertible function tag(disp) >= 0
117
+ # NOTES:
118
+ # . different values of disp must return different tags!
119
+ # . tag at receiver must match message tag at sender
120
+ tag = lambda disp: 42+disp
121
+
122
+ # Requests' handles
123
+
124
+ for direction in range( ndim ):
125
+ if direction == self._axis: continue
126
+ if self._axis is not None: comm = cart.subcomm[direction]
127
+
128
+ # Start receiving data (MPI_IRECV)
129
+ disp = 1
130
+ info = cart.get_shift_info( direction, disp )
131
+ recv_typ = self.get_assembly_recv_type ( direction, disp )
132
+ rank_source = info['rank_source']
133
+
134
+ if self._axis is not None:
135
+ rank_source = gcomm.group.Translate_ranks(np.array([rank_source]), comm.group)[0]
136
+
137
+ recv_buf = (array, 1, recv_typ)
138
+ recv_req = comm.Irecv( recv_buf, rank_source, tag(disp) )
139
+
140
+ # Start sending data (MPI_ISEND)
141
+ send_typ = self.get_assembly_send_type ( direction, disp )
142
+ rank_dest = info['rank_dest']
143
+
144
+ if self._axis is not None:
145
+ rank_dest = gcomm.group.Translate_ranks(np.array([rank_dest]), comm.group)[0]
146
+
147
+ send_buf = (array, 1, send_typ)
148
+ send_req = comm.Isend( send_buf, rank_dest, tag(disp) )
149
+
150
+ # Wait for end of data exchange (MPI_WAITALL)
151
+ MPI.Request.Waitall( [recv_req, send_req] )
152
+
153
+ if disp == 1:
154
+ info = cart.get_shift_info( direction, disp )
155
+ pads = [0]*ndim
156
+ pads[direction] = cart._pads[direction]*cart._shifts[direction]
157
+ idx_from = tuple(slice(s,s+b) for s,b in zip(info['recv_starts'],info['buf_shape']))
158
+ idx_to = tuple(slice(s+p,s+b+p) for s,b,p in zip(info['recv_starts'],info['buf_shape'],pads))
159
+ array[idx_to] += array[idx_from]
160
+ else:
161
+ info = cart.get_shift_info( direction, disp )
162
+ pads = [0]*ndim
163
+ pads[direction] = cart._pads[direction]*cart._shifts[direction]
164
+ idx_from = tuple(slice(s,s+b) for s,b in zip(info['recv_starts'],info['buf_shape']))
165
+ idx_to = tuple(slice(s-p,s+b-p) for s,b,p in zip(info['recv_starts'],info['buf_shape'],pads))
166
+ array[idx_to] += array[idx_from]
167
+
168
+ def end_exchange_assembly_data( self, array ):
169
+ pass
170
+
171
+ #---------------------------------------------------------------------------
172
+ # Private methods
173
+ #---------------------------------------------------------------------------
174
+ @staticmethod
175
+ def _create_buffer_types( cart, dtype, *, coeff_shape=() ):
176
+ """
177
+ Create MPI subarray datatypes for updating the ghost regions (padding)
178
+ of a multi-dimensional array distributed according to the given Cartesian
179
+ decomposition of a tensor-product grid of coefficients.
180
+
181
+ MPI requires a subarray datatype for accessing non-contiguous slices of
182
+ a multi-dimensional array; this is a typical situation when updating the
183
+ ghost regions.
184
+
185
+ Each coefficient in the decomposed grid may have multiple components,
186
+ contiguous in memory.
187
+
188
+ Parameters
189
+ ----------
190
+ cart : feectools.ddm.CartDecomposition
191
+ Object that contains all information about the Cartesian decomposition
192
+ of a tensor-product grid of coefficients.
193
+
194
+ dtype : [type | str | numpy.dtype | mpi4py.MPI.Datatype]
195
+ Datatype of single coefficient (if scalar) or of each of its
196
+ components (if vector).
197
+
198
+ coeff_shape : [tuple(int) | list(int)]
199
+ Shape of a single coefficient, if this is multidimensional
200
+ (optional: by default, we assume scalar coefficients).
201
+
202
+ Returns
203
+ -------
204
+ send_types : dict
205
+ Dictionary of MPI subarray datatypes for SEND BUFFERS, accessed
206
+ through the integer pair (direction, displacement) as key;
207
+ 'direction' takes values from 0 to ndim, 'disp' is -1 or +1.
208
+
209
+ recv_types : dict
210
+ Dictionary of MPI subarray datatypes for RECEIVE BUFFERS, accessed
211
+ through the integer pair (direction, displacement) as key;
212
+ 'direction' takes values from 0 to ndim, 'disp' is -1 or +1.
213
+
214
+ """
215
+ assert isinstance( cart, CartDecomposition )
216
+
217
+ mpi_type = find_mpi_type( dtype )
218
+
219
+ # Possibly, each coefficient could have multiple components
220
+ coeff_shape = list( coeff_shape )
221
+ coeff_start = [0] * len( coeff_shape )
222
+
223
+ data_shape = list( cart.shape ) + coeff_shape
224
+
225
+ send_types = {}
226
+ recv_types = {}
227
+ for shift in product( [-1,0,1], repeat=cart._ndims ):
228
+ if all(s == 0 for s in shift):
229
+ continue
230
+ info = cart.get_shift_info_non_blocking( shift )
231
+
232
+ buf_shape = list( info[ 'buf_shape' ] ) + coeff_shape
233
+ send_starts = list( info['send_starts'] ) + coeff_start
234
+ recv_starts = list( info['recv_starts'] ) + coeff_start
235
+
236
+ if info['rank_dest']>=0:
237
+ send_types[shift] = mpi_type.Create_subarray(
238
+ sizes = data_shape,
239
+ subsizes = buf_shape,
240
+ starts = send_starts,
241
+ ).Commit()
242
+ else:
243
+ send_types[shift] = MPI.DATATYPE_NULL
244
+
245
+ if info['rank_source']>=0:
246
+ recv_types[shift] = mpi_type.Create_subarray(
247
+ sizes = data_shape,
248
+ subsizes = buf_shape,
249
+ starts = recv_starts,
250
+ ).Commit()
251
+ else:
252
+ recv_types[shift] = MPI.DATATYPE_NULL
253
+
254
+ return send_types, recv_types
255
+
256
+
257
+ # ...
258
+ @staticmethod
259
+ def _create_assembly_buffer_types( cart, dtype, *, coeff_shape=(), axis=None, shape=None ):
260
+ """
261
+ Create MPI subarray datatypes for updating the ghost regions (padding)
262
+ of a multi-dimensional array distributed according to the given Cartesian
263
+ decomposition of a tensor-product grid of coefficients.
264
+ MPI requires a subarray datatype for accessing non-contiguous slices of
265
+ a multi-dimensional array; this is a typical situation when updating the
266
+ ghost regions.
267
+ Each coefficient in the decomposed grid may have multiple components,
268
+ contiguous in memory.
269
+ Parameters
270
+ ----------
271
+ cart : feectools.ddm.CartDecomposition
272
+ Object that contains all information about the Cartesian decomposition
273
+ of a tensor-product grid of coefficients.
274
+ dtype : [type | str | numpy.dtype | mpi4py.MPI.Datatype]
275
+ Datatype of single coefficient (if scalar) or of each of its
276
+ components (if vector).
277
+ coeff_shape : [tuple(int) | list(int)]
278
+ Shape of a single coefficient, if this is multidimensional
279
+ (optional: by default, we assume scalar coefficients).
280
+ Returns
281
+ -------
282
+ send_types : dict
283
+ Dictionary of MPI subarray datatypes for SEND BUFFERS, accessed
284
+ through the integer pair (direction, displacement) as key;
285
+ 'direction' takes values from 0 to ndim, 'disp' is -1 or +1.
286
+ recv_types : dict
287
+ Dictionary of MPI subarray datatypes for RECEIVE BUFFERS, accessed
288
+ through the integer pair (direction, displacement) as key;
289
+ 'direction' takes values from 0 to ndim, 'disp' is -1 or +1.
290
+ """
291
+ assert isinstance( cart, CartDecomposition )
292
+
293
+ mpi_type = find_mpi_type( dtype )
294
+
295
+ # Possibly, each coefficient could have multiple components
296
+ coeff_shape = list( coeff_shape )
297
+ coeff_start = [0] * len( coeff_shape )
298
+
299
+ data_shape = list( cart.shape ) + coeff_shape
300
+ send_types = {}
301
+ recv_types = {}
302
+
303
+ if axis is not None:
304
+ data_shape[axis] = shape[axis]
305
+
306
+ for direction in range( cart.ndim ):
307
+ for disp in [-1, 1]:
308
+ info = cart.get_shift_info( direction, disp )
309
+
310
+ buf_shape = list( info[ 'buf_shape' ] ) + coeff_shape
311
+ send_starts = list( info['send_assembly_starts'] ) + coeff_start
312
+ recv_starts = list( info['recv_assembly_starts'] ) + coeff_start
313
+ if direction == axis:continue
314
+ if axis is not None:
315
+ buf_shape[axis] = shape[axis]
316
+ send_starts[axis] = 0
317
+ recv_starts[axis] = 0
318
+
319
+ send_types[direction,disp] = mpi_type.Create_subarray(
320
+ sizes = data_shape ,
321
+ subsizes = buf_shape ,
322
+ starts = send_starts,
323
+ ).Commit()
324
+
325
+ recv_types[direction,disp] = mpi_type.Create_subarray(
326
+ sizes = data_shape ,
327
+ subsizes = buf_shape ,
328
+ starts = recv_starts,
329
+ ).Commit()
330
+
331
+ return send_types, recv_types