diff --git a/tests/test_mpi.py b/tests/test_mpi.py index 24e46074fb..b454eb9e03 100644 --- a/tests/test_mpi.py +++ b/tests/test_mpi.py @@ -1,3 +1,4 @@ +import os from functools import cached_property import numpy as np @@ -3453,10 +3454,16 @@ def gen_serial_norms(shape, so): """ Computes the norms of the outputs in serial mode to compare with """ + fname = f"norms{len(shape)}.npy" day = np.datetime64('today') try: - l = np.load(f"norms{len(shape)}.npy", allow_pickle=True) - assert l[-1] == day + l = np.load(fname, allow_pickle=True) + # The ranks spawned by the `parallel` marker import this module too, but + # must only ever read the norms generated by the parent pytest process. + # If the day changed since then, regenerating here would run the "serial" + # solver under MPI, with all ranks racing on the same file + spawned = int(os.environ.get('DEVITO_PYTEST_FLAG', 0)) + assert spawned or l[-1] == day except: tn = 500. # Final time nrec = 130 # Number of receivers @@ -3475,7 +3482,12 @@ def gen_serial_norms(shape, so): Ev = norm(v) Esrca = norm(srca) - np.save(f"norms{len(shape)}.npy", (Eu, Erec, Ev, Esrca, day), allow_pickle=True) + # Write to a private file and rename it into place, so that a concurrent + # reader (e.g., another xdist worker) never sees a partial file + tmpname = f"{fname}.{os.getpid()}.tmp" + with open(tmpname, 'wb') as f: + np.save(f, (Eu, Erec, Ev, Esrca, day), allow_pickle=True) + os.replace(tmpname, fname) class TestIsotropicAcoustic: