Skip to content

Commit 895b0e0

Browse files
committed
Refactored MITNEBSet into a generic NEBSet. Added CINEBSet.
1 parent 5b997f7 commit 895b0e0

File tree

2 files changed

+137
-65
lines changed

2 files changed

+137
-65
lines changed

src/pymatgen/io/vasp/sets.py

Lines changed: 119 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -2599,23 +2599,91 @@ class MVLRelax52Set(VaspInputSet):
25992599
_valid_potcars: Sequence[str] | None = ("PBE_52", "PBE_54", "PBE_64")
26002600

26012601

2602-
class MITNEBSet(VaspInputSet):
2602+
@dataclass
2603+
class MITMDSet(VaspInputSet):
2604+
"""Write a VASP MD run. This DOES NOT do multiple stage runs.
2605+
2606+
Args:
2607+
structure (Structure): Input structure.
2608+
start_temp (float): Starting temperature.
2609+
end_temp (float): Final temperature.
2610+
nsteps (int): Number of time steps for simulations. NSW parameter.
2611+
time_step (float): The time step for the simulation. The POTIM
2612+
parameter. Defaults to 2fs.
2613+
spin_polarized (bool): Whether to do spin polarized calculations.
2614+
The ISPIN parameter. Defaults to False.
2615+
**kwargs: Other kwargs supported by VaspInputSet.
2616+
"""
2617+
2618+
structure: Structure | None = None
2619+
start_temp: float = 0.0
2620+
end_temp: float = 300.0
2621+
nsteps: int = 1000
2622+
time_step: float = 2
2623+
spin_polarized: bool = False
2624+
CONFIG = MITRelaxSet.CONFIG
2625+
2626+
@property
2627+
def incar_updates(self) -> dict[str, Any]:
2628+
"""Updates to the INCAR config for this calculation type."""
2629+
# MD default settings
2630+
return {
2631+
"TEBEG": self.start_temp,
2632+
"TEEND": self.end_temp,
2633+
"NSW": self.nsteps,
2634+
"EDIFF_PER_ATOM": 0.000001,
2635+
"LSCALU": False,
2636+
"LCHARG": False,
2637+
"LPLANE": False,
2638+
"LWAVE": True,
2639+
"ISMEAR": 0,
2640+
"NELMIN": 4,
2641+
"LREAL": True,
2642+
"BMIX": 1,
2643+
"MAXMIX": 20,
2644+
"NELM": 500,
2645+
"NSIM": 4,
2646+
"ISYM": 0,
2647+
"ISIF": 0,
2648+
"IBRION": 0,
2649+
"NBLOCK": 1,
2650+
"KBLOCK": 100,
2651+
"SMASS": 0,
2652+
"POTIM": self.time_step,
2653+
"PREC": "Low",
2654+
"ISPIN": 2 if self.spin_polarized else 1,
2655+
"LDAU": False,
2656+
"ENCUT": None,
2657+
}
2658+
2659+
@property
2660+
def kpoints_updates(self) -> Kpoints:
2661+
"""Updates to the kpoints configuration for this calculation type."""
2662+
return Kpoints.gamma_automatic()
2663+
2664+
2665+
class NEBSet(VaspInputSet):
26032666
"""Write NEB inputs.
26042667
26052668
Note that EDIFF is not on a per atom basis for this input set.
26062669
"""
26072670

2608-
def __init__(self, structures: list[Structure], unset_encut: bool = False, **kwargs) -> None:
2671+
def __init__(
2672+
self, structures: list[Structure], unset_encut: bool = False, parent_set="MPRelaxSet", **kwargs
2673+
) -> None:
26092674
"""
26102675
Args:
26112676
structures: List of Structure objects.
26122677
unset_encut (bool): Whether to unset ENCUT.
2678+
parent_set (str): The parent input set to inherit from. Defaults to MPRelaxSet. This should be a string
2679+
name to support MSONable.
26132680
**kwargs: Other kwargs supported by VaspInputSet.
26142681
"""
26152682
if len(structures) < 3:
26162683
raise ValueError(f"You need at least 3 structures for an NEB, got {len(structures)}")
26172684
kwargs["sort_structure"] = False
2618-
super().__init__(structures[0], MITRelaxSet.CONFIG, **kwargs)
2685+
self.parent = globals()[parent_set]
2686+
super().__init__(structures[0], self.parent.CONFIG, **kwargs)
26192687
self.structures = self._process_structures(structures)
26202688

26212689
self.unset_encut = False
@@ -2634,6 +2702,7 @@ def __init__(self, structures: list[Structure], unset_encut: bool = False, **kwa
26342702
"LDAU": False,
26352703
}
26362704
self._config_dict["INCAR"].update(defaults)
2705+
self.parent_set = parent_set
26372706

26382707
@property
26392708
def poscar(self) -> Poscar:
@@ -2698,7 +2767,7 @@ def write_input(
26982767
if write_cif:
26992768
poscar.structure.to(filename=str(d / f"{idx}.cif"))
27002769
if write_endpoint_inputs:
2701-
end_point_param = MITRelaxSet(self.structures[0], user_incar_settings=self.user_incar_settings)
2770+
end_point_param = self.parent(self.structures[0], user_incar_settings=self.user_incar_settings)
27022771

27032772
for image in ("00", str(len(self.structures) - 1).zfill(2)):
27042773
end_point_param.incar.write_file(str(output_dir / image / "INCAR"))
@@ -2715,67 +2784,59 @@ def write_input(
27152784
neb_path.to(filename=f"{output_dir}/path.cif")
27162785

27172786

2718-
@dataclass
2719-
class MITMDSet(VaspInputSet):
2720-
"""Write a VASP MD run. This DOES NOT do multiple stage runs.
2721-
2722-
Args:
2723-
structure (Structure): Input structure.
2724-
start_temp (float): Starting temperature.
2725-
end_temp (float): Final temperature.
2726-
nsteps (int): Number of time steps for simulations. NSW parameter.
2727-
time_step (float): The time step for the simulation. The POTIM
2728-
parameter. Defaults to 2fs.
2729-
spin_polarized (bool): Whether to do spin polarized calculations.
2730-
The ISPIN parameter. Defaults to False.
2731-
**kwargs: Other kwargs supported by VaspInputSet.
2787+
class CINEBSet(NEBSet):
2788+
"""
2789+
MAVRL-tested settings for CI-NEB calculations. Note that these parameters
2790+
requires the VTST modification of VASP from the Henkelman group. See
2791+
http://theory.cm.utexas.edu/vtsttools/.
27322792
"""
27332793

2734-
structure: Structure | None = None
2735-
start_temp: float = 0.0
2736-
end_temp: float = 300.0
2737-
nsteps: int = 1000
2738-
time_step: float = 2
2739-
spin_polarized: bool = False
2740-
CONFIG = MITRelaxSet.CONFIG
2794+
def __init__(self, structures: list[Structure], **kwargs) -> None:
2795+
r"""
2796+
Args:
2797+
structures: Input structures.
2798+
**kwargs: Keyword args supported by VaspInputSets.
2799+
"""
2800+
user_incar_settings = kwargs.get("user_incar_settings", {})
27412801

2742-
@property
2743-
def incar_updates(self) -> dict[str, Any]:
2744-
"""Updates to the INCAR config for this calculation type."""
2745-
# MD default settings
2746-
return {
2747-
"TEBEG": self.start_temp,
2748-
"TEEND": self.end_temp,
2749-
"NSW": self.nsteps,
2750-
"EDIFF_PER_ATOM": 0.000001,
2751-
"LSCALU": False,
2752-
"LCHARG": False,
2753-
"LPLANE": False,
2754-
"LWAVE": True,
2802+
# CI-NEB settings
2803+
defaults = {
2804+
"EDIFF": 5e-5,
2805+
"EDIFFG": -0.02,
2806+
"IBRION": 3,
2807+
"ICHAIN": 0,
2808+
"IOPT": 1,
2809+
"ISIF": 2,
27552810
"ISMEAR": 0,
2756-
"NELMIN": 4,
2757-
"LREAL": True,
2758-
"BMIX": 1,
2759-
"MAXMIX": 20,
2760-
"NELM": 500,
2761-
"NSIM": 4,
2762-
"ISYM": 0,
2763-
"ISIF": 0,
2764-
"IBRION": 0,
2765-
"NBLOCK": 1,
2766-
"KBLOCK": 100,
2767-
"SMASS": 0,
2768-
"POTIM": self.time_step,
2769-
"PREC": "Low",
2770-
"ISPIN": 2 if self.spin_polarized else 1,
2811+
"ISPIN": 2,
2812+
"LCHARG": False,
2813+
"LCLIMB": True,
27712814
"LDAU": False,
2772-
"ENCUT": None,
2815+
"LORBIT": 0,
2816+
"NSW": 200,
2817+
"POTIM": 0,
2818+
"SPRING": -5,
27732819
}
2820+
if user_incar_settings != {}:
2821+
defaults.update(user_incar_settings)
27742822

2775-
@property
2776-
def kpoints_updates(self) -> Kpoints:
2777-
"""Updates to the kpoints configuration for this calculation type."""
2778-
return Kpoints.gamma_automatic()
2823+
kwargs["user_incar_settings"] = defaults
2824+
2825+
super().__init__(structures, **kwargs)
2826+
2827+
2828+
class MITNEBSet(NEBSet):
2829+
"""
2830+
NEBSet using MITRelaxSet as parent. Retained for compatibility.
2831+
"""
2832+
2833+
def __init__(self, structures: list[Structure], **kwargs) -> None:
2834+
"""
2835+
Args:
2836+
structures: List of Structure objects.
2837+
**kwargs: Other kwargs supported by VaspInputSet.
2838+
"""
2839+
super().__init__(structures, parent_set="MITRelaxSet", **kwargs)
27792840

27802841

27812842
@dataclass

tests/io/vasp/test_sets.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from pymatgen.io.vasp.sets import (
2323
MODULE_DIR,
2424
BadInputSetWarning,
25+
CINEBSet,
2526
DictSet,
2627
LobsterSet,
2728
MatPESStaticSet,
@@ -49,6 +50,7 @@
4950
MVLRelax52Set,
5051
MVLScanRelaxSet,
5152
MVLSlabSet,
53+
NEBSet,
5254
VaspInputGenerator,
5355
VaspInputSet,
5456
batch_write_input,
@@ -1311,40 +1313,49 @@ def test_as_from_dict(self):
13111313
assert v.incar["NSW"] == 1000
13121314

13131315

1314-
class TestMITNEBSet(PymatgenTest):
1316+
class TestNEBSet(PymatgenTest):
13151317
def setUp(self):
13161318
c1 = [[0.5] * 3, [0.9] * 3]
13171319
c2 = [[0.5] * 3, [0.9, 0.1, 0.1]]
13181320
s1 = Structure(Lattice.cubic(5), ["Si", "Si"], c1)
13191321
s2 = Structure(Lattice.cubic(5), ["Si", "Si"], c2)
13201322
self.structures = [Structure.from_sites(s.sites, to_unit_cell=True) for s in s1.interpolate(s2, 3, pbc=True)]
1321-
self.vis = MITNEBSet(self.structures)
1323+
self.vis = NEBSet(self.structures)
1324+
self.vis_MIT = MITNEBSet(self.structures)
1325+
self.vis_cineb = CINEBSet(self.structures)
13221326

13231327
def test_potcar_symbols(self):
13241328
syms = self.vis.potcar_symbols
13251329
assert syms == ["Si"]
13261330

13271331
def test_unset_encut(self):
1328-
vis = MITNEBSet(self.structures, unset_encut=True)
1332+
vis = NEBSet(self.structures, unset_encut=True)
13291333
assert "ENCUT" not in vis.incar
13301334
n_structs = 2
13311335
with pytest.raises(
13321336
ValueError,
13331337
match=f"You need at least 3 structures for an NEB, got {n_structs}",
13341338
):
1335-
_ = MITNEBSet(self.structures[:n_structs], unset_encut=True)
1339+
_ = NEBSet(self.structures[:n_structs], unset_encut=True)
13361340

13371341
def test_incar(self):
13381342
incar = self.vis.incar
13391343
assert "LDAUU" not in incar
1340-
assert incar["EDIFF"] == approx(0.00001)
1344+
assert incar["EDIFF"] == approx(0.00005)
1345+
assert self.vis_MIT.incar["EDIFF"] == approx(0.00001)
1346+
assert "LCLIMB" in self.vis_cineb.incar
13411347

13421348
def test_kpoints(self):
13431349
kpoints = self.vis.kpoints
1344-
assert kpoints.kpts == [(25,)]
1345-
assert kpoints.style == Kpoints.supported_modes.Automatic
1350+
assert kpoints.kpts == [(5, 5, 5)]
1351+
assert kpoints.style == Kpoints.supported_modes.Gamma
1352+
assert self.vis_MIT.kpoints.kpts == [(25,)]
1353+
assert self.vis_MIT.kpoints.style == Kpoints.supported_modes.Automatic
13461354

13471355
def test_as_from_dict(self):
1356+
dct = self.vis_MIT.as_dict()
1357+
v = MontyDecoder().process_decoded(dct)
1358+
assert v.incar["IMAGES"] == 2
13481359
dct = self.vis.as_dict()
13491360
v = MontyDecoder().process_decoded(dct)
13501361
assert v.incar["IMAGES"] == 2

0 commit comments

Comments
 (0)