Parallel execution and resuming#

Run the sequential tutorial first, then enlarge the calculation. Parallel workers distribute independent backend jobs; they do not change the physical resolution or compute a single backend solve across multiple Python cores.

Single-node multiprocessing#

The preprocessors store processes_num and write task groups. Corresponding create_grnlib_*_parallel functions use a process pool to execute those jobs. Select a worker count that fits both CPU and memory: each Fortran process has its own working arrays and can be memory-intensive.

Memory#

QSEIS06, QSEIS2025 and EDCMP2 use static Fortran arrays, so every process needs a fixed amount of memory whatever the grid size: about 0.7, 1.3 and 0.95 GiB. Before starting, their create_grnlib_*_parallel functions issue a RuntimeWarning when min(processes_num, number of jobs) processes of memory_per_job_gb (default 0.75, 1.4 and 1.0 GiB) exceed the currently available memory; on Linux this includes a Slurm or container cgroup limit. The warning gives the largest worker count that fits. The run goes on: jobs that run out of memory fail and are computed again after the others (see below). To avoid those retries, reduce processes_num in the preprocessor. memory_per_job_gb=None skips the warning. EDGRN2 needs about 10 MiB per process. QSSP2020, SPGRN2020 and SPGRN2012 allocate their arrays from the input, so their warning is off by default; pass the peak memory of one job as memory_per_job_gb to enable it.

Failed jobs, retries and Ctrl+C#

A failed job does not stop the others, for example a job the operating system killed or refused to start for lack of memory. After all jobs ran, the builders compute the jobs that did not complete again, with the same worker count, up to max_retries=2 times. They then check the library and raise RuntimeError listing the jobs that still fail, with their logs, and the incomplete files. Ctrl+C stops the run at once: no new job starts and the running backend processes are killed.

Check an existing library with the matching check_grnlib_* function:

from pygrnwang.create_qseis2025_bulk import check_grnlib_qseis2025

problems = check_grnlib_qseis2025(path_green)  # [] when complete

Each check requires every file the readers use, for every prepared job, in binary form with the expected size or as complete ASCII output, and the travel-time tables where the backend has them. check_values=True also reads the binary files and reports NaN or infinite values.

Every backend reads and writes its paths through 160-character variables. The preprocessors raise ValueError when a job path would be longer, and QSEIS preprocessors also when N_each_group exceeds 101 distances.

Keep the calculation driver under if __name__ == "__main__":. This is required for Windows spawning and makes scripts portable. Use absolute model and output paths because low-level calls can change working directories. Do not share one job directory between simultaneous independent runs.

A single source/receiver pair offers little parallelism for SPGRN. QSEIS also distributes distance groups; QSSP distributes source/receiver pairs for spectra and six tensor-source jobs for time-domain outputs. More workers than available tasks provide no speedup.

Multi-node MPI#

MPI is optional. Install mpi4py and an MPI runtime compatible with the cluster, and launch a dedicated driver with the site’s launcher, for example mpiexec -n 4 python run_mpi.py. A scheduler may require srun or other site-specific allocation flags.

Use the create_grnlib_*_parallel_multi_nodes API, not the multiprocessing API inside every MPI rank. Preprocess once before launching ranks, on a filesystem visible with the same absolute paths on all nodes. Every node needs the package, backend binaries and their runtime libraries.

The MPI routines associate ranks with the precomputed task groups. Match the number of ranks to the size of the first group, which may be smaller than requested processes_num when there are few jobs. Do not independently rerun preprocessing on every rank.

QSSP exposes separate spectral and time-domain MPI functions. Complete the spectral launch successfully before launching the time-domain driver. Run format conversion once after all ranks finish. SPGRN2012 additionally needs Python travel-time tables after its MPI run, and EDCMP’s MPI path needs explicit conversion if binary output is desired.

Every rank runs one backend process at a time. Before any job starts, one rank per node warns when the ranks of its node may need more than the available memory (memory_per_job_gb); the run goes on. A failed job does not stop the others; after all ranks finish, the jobs that did not complete are shared among the ranks and computed again, up to max_retries times. Rank 0 then records SPGRN metadata, writes SPGRN2012 travel-time tables, checks the library and raises RuntimeError if it is incomplete. QSSP MPI functions accept more ranks than the group width; the extra ranks help with retries. Multi-node MPI is not validated by the local tutorial runs or ordinary documentation builds.

Resuming a calculation#

check_finished=True reuses backend directories that have a .finished marker and complete output files; every other job is computed again. This is an existence-based restart mechanism, not a parameter checksum. A marker records a prior successful executable run: it is written only when the executable exits with code 0 and without a Fortran STOP error message; failed runs leave their log in .failed instead. A job that runs again first deletes its earlier output, so a failure cannot leave old files that pass the library check. Inspect the output and the numerical files before treating a marker as scientific validation.

After a builder raised RuntimeError, fix the cause (for example reduce processes_num if jobs ran out of memory) and rerun with check_finished=True to compute only the unfinished jobs; an MPI build can also be completed with the local parallel builder on one node.

Reuse only with identical model, grids, time window, sampling, source function, enabled observables and solver settings. After changing any of these, use a new output directory or deliberately rebuild the affected stages. Do not preserve stale metadata with newly generated Green’s files.

QSSP cal_spec=False is valid only when compatible spectral files already exist. The first calculation must use cal_spec=True. SPGRN cal_gf=1 computes/updates spectra, while 0 requests compatible existing spectra. Changes to spectral model/sampling settings require rebuilding spectra.

Travel-time tables have a separate check_finished_tpts_table setting where exposed, and create_tpts_table(check_finished=True) only checks whether its two files exist. New model or grid means new tables.

The tutorial scripts’ --reuse mode reuses a completed tutorial library and regenerates its saved results through the documented reader path, writing summary-reuse.json while preserving summary.json. Use the backend completion flags in your own driver for interrupted large calculations; keep the original generated inputs and inspect incomplete jobs.

Record a useful run#

Save the host/platform, Python/package/dependency versions, worker count, backend elapsed time, resulting bytes, input model and exact generated files. The tutorials write summary.json with reproducibility and array checks. For a production grid, also retain backend logs and review completed file counts before deleting intermediate ASCII results.