Repository navigation
MPI overlap modes fail to build Eqs with coordinate guards or non-local SubDimensions (ValueError: x_m is not in list) #3094
Description
Symptom
With DEVITO_MPI=overlap, overlap2 or full, Operator(...) raises at build time
ValueError: x_m is not in list
(or x_M / x_rtkn0; which symbol is named varies from run to run) for an Eq whose x iteration is narrowed by a coordinate guard or by a non-local one-sided SubDimension. The same Eqs build under DEVITO_MPI=1 (basic) and diag2. Nothing has to run: the failure is in code generation, and it happens with one rank as well as with four.
Reproducer
# repro.py -- mpirun -n 4 python repro.py {guard,subdim} (one rank fails the same way)
import sys
import sympy
from devito import (Grid, TimeFunction, Eq, Operator, ConditionalDimension,
SubDimension, switchconfig)
grid = Grid(shape=(24, 24))
x, y = grid.dimensions
u = TimeFunction(name='u', grid=grid, space_order=4)
if sys.argv[1] == 'guard':
# Update only 3 <= x < 10
cd = ConditionalDimension(name='cd', parent=x, condition=sympy.And(x >= 3, x < 10))
eqs = [Eq(u.forward, u.laplace + 1, implicit_dims=[cd])]
else:
# Update only the first 4 global x points
xl = SubDimension.left(name='xl', parent=x, thickness=4, local=False)
eqs = [Eq(u.forward, u.laplace + 1).subs(x, xl)]
for mode in ['basic', 'diag2', 'overlap', 'overlap2', 'full']:
try:
with switchconfig(mpi=mode):
Operator(eqs)
msg = 'built'
except Exception as e:
msg = f'{type(e).__name__}: {e}'
if grid.distributor.myrank == 0:
print(sys.argv[1], mode, msg)Run with DEVITO_MPI=1 DEVITO_LANGUAGE=openmp, 4 ranks, on ese-ponte:
guard basic built
guard diag2 built
guard overlap ValueError: x_m is not in list
guard overlap2 ValueError: x_m is not in list
guard full ValueError: x_m is not in list
subdim basic built
subdim diag2 built
subdim overlap ValueError: x_M is not in list
subdim overlap2 ValueError: x_rtkn0 is not in list
subdim full ValueError: x_rtkn0 is not in list
A ConditionalDimension whose condition refers to a SubDomain bound (x < sd.dimensions[0].symbolic_min, used to split x between a guarded Eq and a middle SubDomain Eq) fails the same way.
Evidence
- devito
mainat 61a2745 (2026-10-26): as above, both with 4 ranks undermpirunand with one rank. - Also at 6d2ad82, and with the candidate mpi: Honor MultiSubDomain thicknesses in overlap modes #3075 fix applied (ab62c88, not yet pushed). That fix neither causes nor cures this.
- How it was found: a z3-based check of the overlap iteration spaces (internal tool) could not build its guarded case. Separately, a reviewer of the mpi: Honor MultiSubDomain thicknesses in overlap modes #3075 fix hit it with the non-local
SubDimension.leftprobe.
What is known about the cause
The traceback ends in ElementalFunction.__init__, called for the compute Callable that the overlap builders create:
devito/mpi/routines.py:690 OverlapHaloExchangeBuilder._make_compute -> make_efunc(f'compute{key}', hs.body, hs.arguments, ...)
devito/ir/iet/efunc.py:119 self._mapper[i] = (parameters.index(i.symbolic_min),
devito/ir/iet/efunc.py:120 parameters.index(i.symbolic_max))
dynamic_parameters is hs.arguments, which is HaloScheme.arguments (devito/mpi/halo_scheme.py:498-499): self.dimensions | set(flatten(self.honored.values())). parameters is derived from the HaloSpot body. For these Eqs, hs.arguments names symbols that the body never uses, so parameters.index fails. Instrumenting ElementalFunction.__init__ on main shows:
guard: body parameters['u', 'x', 'h_x', 'h_y', 't0', 't1', 'y_M', 'y_m']; dynamic parametersy,x. Thexloop is outside the HaloSpot body, which only contains theyloop, sox_m/x_Mare absent.subdim: body parameters includex_mandx_ltkn0(the loop isxlover[x_m, x_m + x_ltkn0 - 1]); dynamic parameters arex_ltkn0,y,x,x_rtkn0, sox_Mandx_rtkn0are absent.
The symbol in the message changes between runs because HaloScheme.arguments is a set, so iteration order decides which missing symbol is reported first.
What is not known
- Whether the right fix is to filter
dynamic_parametersto what the body uses (in_make_computeor inElementalFunction), or whether the HaloSpot should not be overlappable in these shapes at all. In particular, it is not established that the CORE/OWNED split computed fromhs.omapperis correct when thexloop sits outside the HaloSpot (theguardcase). - Whether the other consumers of
hs.argumentsindevito/mpi/routines.py(:794MPIRegion, :915, andFullHaloExchangeBuilder._make_computeat :983) fail the same way once thecomputeCallable is fixed. Onlycomputehas been seen failing. - Whether any user code in the wild uses these shapes with overlap modes. The guard shape is a common way to restrict an update to a box.
🤖 Generated with Claude Code
Activity