Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add keyword bandgap_tol: float = 1e-4 to MPScanRelaxSet #3287

Merged
merged 3 commits into from
Aug 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pymatgen/core/structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -1271,7 +1271,7 @@ def __eq__(self, other: object) -> bool:
# return NotImplemented as in https://docs.python.org/3/library/functools.html#functools.total_ordering
return NotImplemented

other = cast(Structure, other) # to make mypy happy
other = cast(Structure, other) # make mypy happy

if other is self:
return True
Expand Down
2 changes: 1 addition & 1 deletion pymatgen/electronic_structure/plotter.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ def get_plot(
# Remove duplicate labels with a dictionary
handles, labels = ax.get_legend_handles_labels()
label_dict = dict(zip(labels, handles))
ax.legend(label_dict.values(), label_dict.keys())
ax.legend(label_dict.values(), label_dict)
legend_text = ax.get_legend().get_texts() # all the text.Text instance in the legend
plt.setp(legend_text, fontsize=30)
plt.tight_layout()
Expand Down
2 changes: 1 addition & 1 deletion pymatgen/io/lammps/inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,7 @@ def _initialize_stage(self, stage_name: str | None = None, stage_index: int | No

@staticmethod
def _check_stage_format(stage: dict):
if list(stage.keys()) != ["stage_name", "commands"]:
if list(stage) != ["stage_name", "commands"]:
raise KeyError(
"The provided stage does not have the correct keys. It should be 'stage_name' and 'commands'."
)
Expand Down
62 changes: 26 additions & 36 deletions pymatgen/io/vasp/sets.py
Original file line number Diff line number Diff line change
Expand Up @@ -932,28 +932,30 @@ class MPScanRelaxSet(DictSet):
CONFIG = _load_yaml_config("MPSCANRelaxSet")
_valid_potcars = ("PBE_52", "PBE_54")

def __init__(self, structure: Structure, bandgap=0, **kwargs):
def __init__(self, structure: Structure, bandgap: float = 0, bandgap_tol: float = 1e-4, **kwargs) -> None:
"""
Args:
structure (Structure): Input structure.
bandgap (int): Bandgap of the structure in eV. The bandgap is used to
compute the appropriate k-point density and determine the
smearing settings.

Metallic systems (default, bandgap = 0) use a KSPACING value of 0.22
and Methfessel-Paxton order 2 smearing (ISMEAR=2, SIGMA=0.2).

Non-metallic systems (bandgap > 0) use the tetrahedron smearing
method (ISMEAR=-5, SIGMA=0.05). The KSPACING value is
calculated from the bandgap via Eqs. 25 and 29 of Wisesa, McGill,
and Mueller [1] (see References). Note that if 'user_incar_settings'
or 'user_kpoints_settings' override KSPACING, the calculation from
bandgap is not performed.

bandgap (float): Bandgap of the structure in eV. The bandgap is used to
compute the appropriate k-point density and determine the
smearing settings.

Metallic systems (default, bandgap = 0) use a KSPACING value of 0.22
and Methfessel-Paxton order 2 smearing (ISMEAR=2, SIGMA=0.2).

Non-metallic systems (bandgap > 0) use the tetrahedron smearing
method (ISMEAR=-5, SIGMA=0.05). The KSPACING value is
calculated from the bandgap via Eqs. 25 and 29 of Wisesa, McGill,
and Mueller [1] (see References). Note that if 'user_incar_settings'
or 'user_kpoints_settings' override KSPACING, the calculation from
bandgap is not performed.
bandgap_tol (float): Tolerance for determining if a system is metallic.
If the bandgap is less than this value, the system is considered
metallic. Defaults to 1e-4 (eV).
vdw (str): set "rVV10" to enable SCAN+rVV10, which is a versatile
van der Waals density functional by combing the SCAN functional
with the rVV10 non-local correlation functional. rvv10 is the only
dispersion correction available for SCAN at this time.
van der Waals density functional by combing the SCAN functional
with the rVV10 non-local correlation functional. rvv10 is the only
dispersion correction available for SCAN at this time.
**kwargs: Same as those supported by DictSet.

References:
Expand All @@ -966,33 +968,21 @@ def __init__(self, structure: Structure, bandgap=0, **kwargs):
super().__init__(structure, MPScanRelaxSet.CONFIG, **kwargs)
self.bandgap = bandgap
self.kwargs = kwargs
self.bandgap_tol = bandgap_tol

updates = {}
updates: dict[str, float] = {}
# select the KSPACING and smearing parameters based on the bandgap
if self.bandgap < 1e-4:
updates["KSPACING"] = 0.22
updates["SIGMA"] = 0.2
updates["ISMEAR"] = 2
updates.update(KSPACING=0.22, SIGMA=0.2, ISMEAR=2)
else:
rmin = 25.22 - 2.87 * bandgap # Eq. 25
kspacing = 2 * np.pi * 1.0265 / (rmin - 1.0183) # Eq. 29
# cap the KSPACING at a max of 0.44, per internal benchmarking
if 0.22 < kspacing < 0.44:
updates["KSPACING"] = kspacing
else:
updates["KSPACING"] = 0.44
updates["ISMEAR"] = -5
updates["SIGMA"] = 0.05
updates.update(KSPACING=kspacing if 0.22 < kspacing < 0.44 else 0.44, SIGMA=0.05, ISMEAR=-5)

# Don't overwrite things the user has supplied
if self.user_incar_settings.get("KSPACING"):
del updates["KSPACING"]

if self.user_incar_settings.get("ISMEAR"):
del updates["ISMEAR"]

if self.user_incar_settings.get("SIGMA"):
del updates["SIGMA"]
for key in self.user_incar_settings:
updates.pop(key, None)

if self.vdw and self.vdw != "rvv10":
warnings.warn("Use of van der waals functionals other than rVV10 with SCAN is not supported at this time. ")
Expand Down
3 changes: 1 addition & 2 deletions tests/electronic_structure/test_plotter.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import matplotlib.pyplot as plt
import numpy as np
import scipy
from matplotlib import rc
from numpy.testing import assert_allclose
from pytest import approx

Expand Down Expand Up @@ -54,8 +55,6 @@ def test_get_dos_dict(self):
# it can actually execute.
def test_get_plot(self):
# Disabling latex is needed for this test to work.
from matplotlib import rc

rc("text", usetex=False)
self.plotter.add_dos_dict(self.dos.get_element_dos(), key_sort_func=lambda x: x.X)
ax = self.plotter.get_plot()
Expand Down
2 changes: 1 addition & 1 deletion tests/io/qchem/test_outputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ def test_cmirs_water(self):

def test_NBO_hyperbonds(self):
data = QCOutput(f"{TEST_FILES_DIR}/molecules/new_qchem_files/hyper.qout").data
assert len(data["nbo_data"]["hyperbonds"][0]["hyperbond index"].keys()) == 2
assert len(data["nbo_data"]["hyperbonds"][0]["hyperbond index"]) == 2
assert data["nbo_data"]["hyperbonds"][0]["BD(A-B)"][1] == 106
assert data["nbo_data"]["hyperbonds"][0]["bond atom 2 symbol"][0] == "C"
assert data["nbo_data"]["hyperbonds"][0]["occ"][1] == 3.0802
Expand Down
31 changes: 15 additions & 16 deletions tests/io/vasp/test_sets.py
Original file line number Diff line number Diff line change
Expand Up @@ -1484,25 +1484,24 @@ def test_scan_substitute(self):
incar = mp_scan_sub.incar
assert incar["METAGGA"] == "Scan"

def test_nonmetal(self):
# Test that KSPACING and ISMEAR change with a nonmetal structure
file_path = f"{TEST_FILES_DIR}/POSCAR.O2"
struct = Poscar.from_file(file_path, check_for_POTCAR=False).structure
scan_nonmetal_set = MPScanRelaxSet(struct, bandgap=1.1)
incar = scan_nonmetal_set.incar
assert incar["KSPACING"] == approx(0.3064757, abs=1e-5)
assert incar["ISMEAR"] == -5
assert incar["SIGMA"] == 0.05

def test_kspacing_cap(self):
def test_bandgap_tol(self):
# Test that the bandgap tolerance is applied correctly
bandgap = 0.01
for bandgap_tol, expected_kspacing in ((0.001, 0.2668137888), (0.02, 0.26681378884)):
incar = MPScanRelaxSet(self.struct, bandgap=0.01, bandgap_tol=bandgap_tol).incar
assert incar["KSPACING"] == approx(expected_kspacing, abs=1e-5), f"{bandgap_tol=}, {bandgap=}"
assert incar["ISMEAR"] == -5
assert incar["SIGMA"] == 0.05

def test_kspacing(self):
# Test that KSPACING is capped at 0.44 for insulators
file_path = f"{TEST_FILES_DIR}/POSCAR.O2"
struct = Poscar.from_file(file_path, check_for_POTCAR=False).structure
scan_nonmetal_set = MPScanRelaxSet(struct, bandgap=10)
incar = scan_nonmetal_set.incar
assert incar["KSPACING"] == approx(0.44, abs=1e-5)
assert incar["ISMEAR"] == -5
assert incar["SIGMA"] == 0.05
for bandgap, expected in ((10, 0.44), (3, 0.4136617), (1.1, 0.3064757)):
incar = MPScanRelaxSet(struct, bandgap=bandgap).incar
assert incar["KSPACING"] == approx(expected, abs=1e-5)
assert incar["ISMEAR"] == -5
assert incar["SIGMA"] == 0.05

def test_incar_overrides(self):
# use 'user_incar_settings' to override the KSPACING, ISMEAR, and SIGMA
Expand Down