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.
- feectools/__init__.py +0 -0
- feectools/accelerate/__init__.py +0 -0
- feectools/accelerate/accelerate.py +220 -0
- feectools/accelerate/compile_psydac.mk +52 -0
- feectools/api/__init__.py +0 -0
- feectools/api/essential_bc.py +122 -0
- feectools/api/fem_bilinear_form.py +2226 -0
- feectools/api/fem_common.py +286 -0
- feectools/api/fem_sum_form.py +123 -0
- feectools/api/settings.py +82 -0
- feectools/core/__init__.py +11 -0
- feectools/core/bsplines.py +1107 -0
- feectools/core/bsplines_kernels.py +1349 -0
- feectools/core/field_evaluation_kernels.py +5015 -0
- feectools/core/tests/__init__.py +0 -0
- feectools/core/tests/test_bsplines.py +263 -0
- feectools/core/tests/test_bsplines_kernel.py +40 -0
- feectools/core/tests/test_bsplines_pyccel.py +752 -0
- feectools/ddm/__init__.py +3 -0
- feectools/ddm/basic.py +78 -0
- feectools/ddm/blocking_data_exchanger.py +348 -0
- feectools/ddm/cart.py +1835 -0
- feectools/ddm/interface_data_exchanger.py +122 -0
- feectools/ddm/mpi.py +109 -0
- feectools/ddm/nonblocking_data_exchanger.py +331 -0
- feectools/ddm/partition.py +207 -0
- feectools/ddm/petsc.py +112 -0
- feectools/ddm/tests/__init__.py +0 -0
- feectools/ddm/tests/test_cart_1d.py +138 -0
- feectools/ddm/tests/test_cart_2d.py +164 -0
- feectools/ddm/tests/test_cart_3d.py +158 -0
- feectools/ddm/tests/test_multicart_2d.py +173 -0
- feectools/ddm/tests/test_partition.py +124 -0
- feectools/ddm/utilities.py +24 -0
- feectools/feec/__init__.py +0 -0
- feectools/feec/derivatives.py +780 -0
- feectools/feec/dof_kernels.py +210 -0
- feectools/feec/global_geometric_projectors.py +1073 -0
- feectools/feec/hodge.py +148 -0
- feectools/fem/__init__.py +0 -0
- feectools/fem/basic.py +465 -0
- feectools/fem/grid.py +181 -0
- feectools/fem/partitioning.py +344 -0
- feectools/fem/projectors.py +160 -0
- feectools/fem/splines.py +559 -0
- feectools/fem/tensor.py +1393 -0
- feectools/fem/tests/__init__.py +0 -0
- feectools/fem/tests/analytical_profiles_1d.py +100 -0
- feectools/fem/tests/analytical_profiles_base.py +34 -0
- feectools/fem/tests/splines_error_bounds.py +155 -0
- feectools/fem/tests/test_spline_histopolation.py +120 -0
- feectools/fem/tests/test_spline_interpolation.py +182 -0
- feectools/fem/tests/test_splines.py +184 -0
- feectools/fem/tests/test_splines_par.py +46 -0
- feectools/fem/tests/test_vector_spaces.py +150 -0
- feectools/fem/tests/utilities.py +47 -0
- feectools/fem/vector.py +729 -0
- feectools/linalg/__init__.py +0 -0
- feectools/linalg/basic.py +1386 -0
- feectools/linalg/block.py +1451 -0
- feectools/linalg/direct_solvers.py +201 -0
- feectools/linalg/fft.py +258 -0
- feectools/linalg/kernels/__init__.py +0 -0
- feectools/linalg/kernels/axpy_kernels.py +57 -0
- feectools/linalg/kernels/inner_kernels.py +100 -0
- feectools/linalg/kernels/matvec_kernels.py +206 -0
- feectools/linalg/kernels/stencil2IJV_kernels.py +227 -0
- feectools/linalg/kernels/stencil2coo_kernels.py +179 -0
- feectools/linalg/kernels/transpose_kernels.py +263 -0
- feectools/linalg/kron.py +911 -0
- feectools/linalg/solvers.py +1914 -0
- feectools/linalg/sparse.py +114 -0
- feectools/linalg/stencil.py +2923 -0
- feectools/linalg/stencil_dot_kernels.py +317 -0
- feectools/linalg/stencil_transpose_kernels.py +372 -0
- feectools/linalg/tests/__init__.py +0 -0
- feectools/linalg/tests/test_block.py +1588 -0
- feectools/linalg/tests/test_fft.py +106 -0
- feectools/linalg/tests/test_kron_stencil_matrix.py +114 -0
- feectools/linalg/tests/test_linalg.py +1065 -0
- feectools/linalg/tests/test_matrix_free.py +128 -0
- feectools/linalg/tests/test_solvers.py +213 -0
- feectools/linalg/tests/test_stencil_interface_matrix.py +379 -0
- feectools/linalg/tests/test_stencil_vector.py +1036 -0
- feectools/linalg/tests/test_stencil_vector_space.py +440 -0
- feectools/linalg/topetsc.py +522 -0
- feectools/linalg/utilities.py +200 -0
- feectools/utilities/__init__.py +0 -0
- feectools/utilities/quadratures.py +113 -0
- feectools/utilities/utils.py +166 -0
- feectools/version.py +1 -0
- feectools-0.1.0.dist-info/METADATA +66 -0
- feectools-0.1.0.dist-info/RECORD +98 -0
- feectools-0.1.0.dist-info/WHEEL +5 -0
- feectools-0.1.0.dist-info/entry_points.txt +3 -0
- feectools-0.1.0.dist-info/licenses/AUTHORS +22 -0
- feectools-0.1.0.dist-info/licenses/LICENSE +21 -0
- 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
|