Skip to content

Commit 3c0f3ea

Browse files
committed
mpi: Add raise_mpi utility for collective exceptions
1 parent 4f386a6 commit 3c0f3ea

2 files changed

Lines changed: 44 additions & 1 deletion

File tree

‎devito/mpi/distributed.py‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ def __getattr__(self, name):
7070
'SubDistributor',
7171
'devito_mpi_finalize',
7272
'devito_mpi_init',
73+
'mpi_raise',
7374
]
7475

7576

@@ -101,6 +102,21 @@ def devito_mpi_finalize():
101102
MPI.Finalize()
102103

103104

105+
def mpi_raise(error, exception=ValueError, comm=None):
106+
"""
107+
Raise `exception` with the first non-None error message in rank order.
108+
109+
All ranks in `comm` must call this routine, including those with no local
110+
error (`error=None`). This prevents a rank-local exception from stranding
111+
peers in subsequent MPI calls. With no communicator or `MPI.COMM_NULL`,
112+
only the local error is checked.
113+
"""
114+
if comm is not None and comm is not MPI.COMM_NULL:
115+
error = next((i for i in comm.allgather(error) if i is not None), None)
116+
if error is not None:
117+
raise exception(error)
118+
119+
104120
class AbstractDistributor(ABC):
105121

106122
"""

‎tests/test_mpi.py‎

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@
1313
)
1414
from devito.arch.compiler import OneapiCompiler
1515
from devito.data import LEFT, RIGHT
16+
from devito.exceptions import InvalidArgument
1617
from devito.ir import Cluster, Interval, IterationSpace
1718
from devito.ir.clusters.algorithms import check_halo_writes
1819
from devito.ir.iet import (
1920
Call, Conditional, FindNodes, FindSymbols, Iteration, retrieve_iteration_tree
2021
)
2122
from devito.ir.support.space import Backward, Forward
22-
from devito.mpi import MPI
23+
from devito.mpi import MPI, mpi_raise
2324
from devito.mpi.distributed import CustomTopology
2425
from devito.mpi.routines import ComputeCall, HaloUpdateCall, HaloUpdateList, MPICall
2526
from devito.tools import Bunch
@@ -28,6 +29,32 @@
2829
from examples.seismic.acoustic import acoustic_setup
2930

3031

32+
class TestMPIUtils:
33+
34+
@pytest.mark.parametrize('comm', [None, MPI.COMM_NULL], ids=['none', 'null'])
35+
@pytest.mark.parametrize('error', [None, '', 'invalid argument'])
36+
@pytest.mark.parametrize('exception', [ValueError, InvalidArgument])
37+
def test_raise_serial(self, comm, error, exception):
38+
if error is None:
39+
mpi_raise(error, exception, comm=comm)
40+
else:
41+
with pytest.raises(exception, match=f'^{error}$'):
42+
mpi_raise(error, exception, comm=comm)
43+
44+
@pytest.mark.parametrize('failing_ranks', [(), (0,), (1,), (0, 1)])
45+
@pytest.mark.parallel(mode=[(2, 'basic')])
46+
def test_raise_collective(self, failing_ranks, mode):
47+
grid = Grid(shape=(16, 16))
48+
rank = grid.distributor.myrank
49+
error = f'rank {rank}' if rank in failing_ranks else None
50+
51+
if failing_ranks:
52+
with pytest.raises(InvalidArgument, match=f'^rank {failing_ranks[0]}$'):
53+
mpi_raise(error, InvalidArgument, comm=grid.comm)
54+
else:
55+
mpi_raise(error, InvalidArgument, comm=grid.comm)
56+
57+
3158
class TestDistributor:
3259

3360
@pytest.mark.parallel(mode=[2, 4])

0 commit comments

Comments
 (0)