Created
November 20, 2024 19:22
-
-
Save lukasmki/3e5322dd221f71e6ce1abdb52b484c2c to your computer and use it in GitHub Desktop.
Nanoreactor + Langevin Integrator for the Atomic Simulation Environment (ASE)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """ Nanoreactor + Langevin Integrator | |
| Wang, LP., Titov, A., McGibbon, R. et al. Discovering chemistry with an ab initio nanoreactor. Nature Chem 6, 1044-1048 (2014). https://doi.org/10.1038/nchem.2099 | |
| """ | |
| import numpy as np | |
| from ase import Atoms, units | |
| from ase.md.langevin import Langevin | |
| class Nanoreactor(Langevin): | |
| def __init__( | |
| self, | |
| atoms, | |
| timestep, | |
| k1=1.0, | |
| k2=0.5, | |
| r1=14, | |
| r2=8.0, | |
| tau_ps=1.5, | |
| T_ps=2.0, | |
| **kwargs, | |
| ): | |
| Langevin.__init__(self, atoms, timestep, **kwargs) | |
| self.k1 = k1 * units.kcal / units.mol / units.Angstrom**2 | |
| self.k2 = k2 * units.kcal / units.mol / units.Angstrom**2 | |
| self.r1 = r1 * units.Angstrom | |
| self.r2 = r2 * units.Angstrom | |
| self.period = T_ps * 1000.0 * units.fs | |
| self.tau = tau_ps * 1000.0 * units.fs | |
| def boundary(self) -> tuple[np.ndarray, np.ndarray]: | |
| box_center = self.atoms.get_cell() @ np.array([0.5, 0.5, 0.5]) | |
| V = self.atoms.positions - box_center | |
| D = np.sqrt(np.sum(V * V, -1)) | |
| Vhat = V / (D[:, None] + 1e-8) | |
| # Boundary potential | |
| t = self.get_time() | |
| ft = np.heaviside( | |
| np.floor(t / self.period) - (t / self.period) + (self.tau / self.period), 1 | |
| ) | |
| U1 = 0.5 * self.k1 * np.square(D - self.r1) * np.heaviside(D - self.r1, 1) | |
| U2 = 0.5 * self.k2 * np.square(D - self.r2) * np.heaviside(D - self.r2, 1) | |
| U = ft * U1 + (1 - ft) * U2 | |
| # Force | |
| F1 = ( | |
| -Vhat | |
| * self.k1 | |
| * (D - self.r1)[:, None] | |
| * np.heaviside(D - self.r1, 1)[:, None] | |
| ) | |
| F2 = ( | |
| -Vhat | |
| * self.k2 | |
| * (D - self.r2)[:, None] | |
| * np.heaviside(D - self.r2, 1)[:, None] | |
| ) | |
| F = ft * F1 + (1 - ft) * F2 | |
| return U, F | |
| def step(self, forces=None): | |
| atoms: Atoms = self.atoms | |
| if forces is None: | |
| forces = atoms.get_forces() | |
| # Nanoreactor boundary potential | |
| U, F = self.boundary() | |
| forces += F | |
| # MD step | |
| Langevin.step(self, forces) | |
| return forces |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment