Parallel Force Evaluation¶
eOn supports parallel force evaluation in NEB, Dimer/ImprovedDimer, and
ProcessSearchJob. The threading model uses std::thread with per-image
potential ownership.
Threading Model¶
Two virtual methods on Potential control the behavior:
isThreadSafe()¶
Returns whether the same potential instance can be called from multiple
threads concurrently. Most empirical potentials (LJ, Morse, SW, etc.)
return true (default). Python-based potentials (ASE, CatLearn) return
false because CPython has the GIL.
When true, NEB spawns one thread per image and all threads call
force() on the shared potential instance.
needsPerImageInstance()¶
Returns whether NEB should create a separate Potential instance per
image via makePotential(). This is needed for potentials where:
The same instance cannot be called concurrently (internal state, caches)
But separate instances CAN run in parallel (each has its own state)
Examples:
MetatomicPotential: PyTorch model has internal caches. Same instance needs a mutex; separate instances run truly in parallel. Returns
needsPerImageInstance() = true.XTBPot: Fortran library has per-instance state (
xtb_TEnvironment,xtb_TCalculator). Same instance is not thread-safe; separate instances are independent. ReturnsneedsPerImageInstance() = true.
When needsPerImageInstance() is true, NEB creates N+2 potential
instances (one per image) at construction time. The parallel force
evaluation then proceeds lock-free.
Decision Table¶
|
|
Behavior |
Examples |
|---|---|---|---|
|
|
Shared instance, parallel threads |
LJ, Morse, SW, EMT |
|
|
Per-image instances, parallel threads |
XTB, ASE, metatomic |
|
|
Per-image instances, parallel threads |
MetatomicPotential (mutex fallback) |
|
|
Sequential evaluation |
(none currently) |
The parallel check in NEB is:
bool canParallel = pot->isThreadSafe() || perImagePotentials_;
if (numImages > 1 && params.main_options.parallel && canParallel) { ... }
Affected Code Paths¶
Component |
Parallel Units |
Per-Image Potential |
|---|---|---|
NEB |
N images |
Each |
Dimer |
center + forward |
|
ImprovedDimer |
x0 + x1 |
|
ProcessSearchJob |
min1 + min2 |
|
Performance¶
With the Morse empirical potential (337-atom Pt, 5 NEB images), parallel force evaluation gives a 2.3x speedup over SVN sequential.
With PET-MAD-S ML potential (14-atom Claisen, 10 NEB images):
Mutex-serialized (shared instance): 192 seconds
Per-image instances (true parallel): 69 seconds (2.8x speedup)
Adding a New Potential¶
If your potential has internal state that prevents concurrent calls on the same instance but supports independent instances:
Override
isThreadSafe()to returntrue(with internal mutex as fallback) orfalseOverride
needsPerImageInstance()to returntrueEnsure the constructor (called by
makePotential()) creates an independent instance (no shared static state)
The [Main] parallel = true config option (default) enables threading.
Set parallel = false to force sequential evaluation.