This is an On-the-Fly Atomistic Calculator based on Gaussian Process Regression (GPR), designed as an ASE add-on calculator. It incorporates a hybrid approach by combining:
Base calculator
: A high-fidelity ab initio calculator (e.g., DFT) that serves as the reference (“gold standard”) for accurate but computationally expensive calculations.Surrogate GPR calculator
: A computationally inexpensive model trained on-the-fly to approximate the base calculator and accelerate simulations.
Many atomistic simulations—such as geometry optimization, barrier calculations, molecular dynamics (MD), and equation-of-state (EOS) simulations—require sampling a large number of atomic configurations in a compact phase space. The workhorse method for such calculations is often Density Functional Theory (DFT), which provides a balance between accuracy and computational cost. However, DFT can still be prohibitively expensive, particularly for large systems or simulations for many snapshots.
- Initially, the calculator invokes the base calculator (e.g., a DFT code) to compute energy, forces, and stress tensors for each atomic configuration.
- These computed data points are used to train an internal surrogate model based on Gaussian Process Regression (GPR).
- As the surrogate model accumulates training data, it gradually learns the energy and force landscape of the system.
- Once the model achieves sufficient accuracy, it begins predicting the mean and variance of energy, forces, and stress tensors, enabling efficient on-the-fly calculations.
- If the predicted uncertainty is low, the model replaces the expensive DFT evaluations. Otherwise, the base calculator is called again to refine the model.
Below illustrates an example to run NEB calculation with the hybrid calculator
from gpr_calc.GPRANEB import GP_NEB
from gpr_calc.calculator import GPR
from ase.optimize import BFGS
from ase.mep import NEB
from ase.calculators.emt import EMT
initial_state = 'database/initial.traj'
final_state = 'database/final.traj'
num_images = 5
print("\nInit the model")
neb_gp = GP_NEB(initial_state,
final_state,
num_images=num_images)
print("\nGet the initial images")
images = neb_gp.generate_images(IDPP = False)
# Set Base calculator and Run NEB
for image in images:
image.calc = EMT()
neb = NEB(images)
opt = BFGS(neb)
opt.run(fmax=0.01)
neb_gp.plot_neb_path(images, figname='Ref.png')
# Test gpr calculator
images = neb_gp.generate_images(IDPP = False)
for kernel in ['Dot', 'RBF']:
print("\nCreate the initial GPR model")
neb_gp.set_GPR(kernel=kernel, noise_e=0.002)
neb_gp.train_GPR(images)
print(neb_gp.model)
# Set hybrid calculator
for image in images:
image.calc = GPR(base_calculator=neb_gp.useCalc,
ff=neb_gp.model,
return_std=True)
print("\nRun actual NEB")
neb = NEB(images)
opt = BFGS(neb, trajectory='neb.traj')
opt.run(fmax=0.01)
# Plot results
neb_gp.plot_neb_path(images, figname=kernel+'.png')