--- myst: html_meta: "description": "Design rationale for NEB modularization using strategy patterns, parallel evaluation, and eigenmode variants." "keywords": "eOn NEB, strategy pattern, modularization, parallel, eigenmode, variant" --- # NEB modularization The Nudged Elastic Band implementation was decomposed from a monolithic 1,225-line file into modular, testable components using the Strategy pattern. ## Module structure | Module | Lines | Purpose | |---|---|---| | `NudgedElasticBand.cpp` | ~530 | Orchestration: image loop, convergence, CI activation | | `NEBTangent.cpp` | ~80 | Tangent estimation strategies | | `NEBProjection.cpp` | ~80 | Force projection strategies | | `NEBSpringForce.cpp` | ~140 | Spring force strategies (constant, energy-weighted, OM) | | `NEBForceProjection.cpp` | ~120 | Perpendicular force, CI force, zero-translation | | `NEBOcinebController.cpp` | ~180 | OCINEB hybrid dimer controller | | `NEBObjectiveFunction.cpp` | ~60 | Optimizer adapter | | `NEBSplineExtrema.cpp` | ~80 | Cubic spline peak detection | | `NEBInitialPaths.cpp` | ~200 | Path initialization (linear, IDPP, file) | ## Strategy pattern Three interchangeable strategies are built at NEB construction time via factory functions: ### Tangent strategies (`NEBTangent.cpp`) - **Improved tangent** (default): uses energy-weighted bisection at maxima/minima, standard interpolation elsewhere. Better behavior near transition states. - **Old tangent**: simple finite-difference `tau = R[i+1] - R[i-1]`. Retained for backward compatibility. ### Projection strategies (`NEBProjection.cpp`) - **Standard NEB**: projects out parallel spring force, keeps perpendicular true force. - **Elastic band**: no projection (spring + true force). - **Doubly-nudged elastic band (DNEB)**: adds perpendicular spring force component. - **Onsager-Machlup**: modifies spring constant per-image based on force magnitude. ### Spring force strategies (`NEBSpringForce.cpp`) - **Constant spring**: uniform `k` for all images. - **Energy-weighted**: adjusts `k` based on image energy relative to endpoints. Higher `k` near the barrier, lower in basins. - **Onsager-Machlup**: adaptive `k` based on local curvature for minimum-action paths. ## Climbing image and OCINEB The climbing image (CI-NEB) modification activates after initial convergence: the highest-energy image climbs along the band to the exact saddle point. OCINEB (Off-Path Climbing Image NEB) {cite:t}`neb-goswamiEnhancedClimbingImage2026` extends CI-NEB with Min-Mode Following (MMF) and hessian eigenmode alignment: once the climbing image stabilizes, a dimer search refines the saddle point. This is controlled by `NEBOcinebController` which monitors CI stability and triggers a `MinModeSaddleSearch` when the angular tolerance is met. ## Eigenmode methods The eigenmode estimation (for dimer/Lanczos) uses `std::variant` instead of an abstract base class: ```cpp using EigenmodeStrategy = std::variant; ``` This eliminates virtual dispatch overhead and enables value semantics. The variant is constructed by `buildEigenmodeStrategy()` based on configuration. ## Parallel force evaluation When built with `-Deon_parallel_neb=true` (requires TBB), NEB evaluates image forces in parallel using `std::for_each` with `std::execution::par`. The `Potential::isThreadSafe()` virtual method gates parallelism: Python-based potentials (ASE, CatLearn) return `false` and fall back to serial evaluation.