-
Notifications
You must be signed in to change notification settings - Fork 867
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
Make Incar
keys case insensitive, fix init Incar
from dict val processing for str/float/int
#4122
Make Incar
keys case insensitive, fix init Incar
from dict val processing for str/float/int
#4122
Conversation
ping the true expert @esoteric-ephemera in case you have any comments (perhaps deserve a separate PR):
Thanks in advance :) |
f94d43e
to
37ac9f5
Compare
This reverts commit adcdba7.
3ce90e8
to
82f732c
Compare
82f732c
to
966bb91
Compare
88b5eab
to
a24ac8e
Compare
Why do this exactly? FORTRAN (unless you use specific compiler options) is case insensitive, and most FORTRAN code, VASP included, is case insensitive.
I would just throw a user warning. Because of how VASP parses an INCAR, I'm pretty sure you can write the same tag multiple times, and the bottom-most value is the one that's accepted
Probably just convention, again because FORTRAN is case insensitive Also for the |
I btw recently had issues with updating the incar. I would have expected that sigma and SIGMA would simply modify SIGMA but they did not. Instead, i had two different sigmas in the incar. I did not have time to investigate but it was a very recent pymatgen version |
@JaGeo did VASP parse them as different variables or just take the one defined furthest down the INCAR? |
Yes, all tags are case insensitive. I don't think the Incar class actually checks. So if you set |
Thanks that makes sense, and I don't think we need to change this as it wouldn't have any side effect.
I believe VASP parses the first occurrence (can you confirm it's the bottom one? @esoteric-ephemera ), but I could be wrong here. I tested with INCAR:
And |
@DanielYang59 @esoteric-ephemera Yes, it took the first one as far as I remember. @shyuep i might be wrong but I thought i modelled the lobsterin class after the incar at the time and i think it checked at some point. In any case, it would be great if this would be checked again (e.g., via Camelcase). |
I proposed that we cast Incar keys to upper case internally in order to prevent such duplicate and make other internal checking/handling easier. Also the reason I personally prefer upper case over others (capitalize/lower) being:
|
@JaGeo I just fix the upper case keys problem and added a test. It should no longer be a problem. As far as I can tell, the values are already properly capitalize and there is a test. |
d43bb1a
to
3b231be
Compare
) | ||
key = key.strip().upper() | ||
# Cast float/int to str and feed into proc_val to clean up their types | ||
val = self.proc_val(key, str(val)) if isinstance(val, str | float | int) else val |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change:
- val = self.proc_val(key, str(val)) if isinstance(val, str) else val
+ val = self.proc_val(key, str(val)) if isinstance(val, str | float | int) else val
should have a healthy dose of stupidity as I believe casting int/float
to str
is necessary such that the val
would go through proc_val
to sanitize its type, otherwise it would bypass the proc_val
check, leading to inconsistent behaviour when init from dict vs from str/file.
27ad437
to
96ad945
Compare
ac2a40a
to
fa58d0d
Compare
@@ -345,7 +345,7 @@ def __init__( | |||
self.update_potcar_spec(parse_potcar_file) | |||
self.update_charge_from_potcar(parse_potcar_file) | |||
|
|||
if self.incar.get("ALGO") not in {"CHI", "BSE"} and not self.converged and self.parameters.get("IBRION") != 0: | |||
if self.incar.get("ALGO") not in {"Chi", "Bse"} and not self.converged and self.parameters.get("IBRION") != 0: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before migrating to subclassing UserDict
, the setter method is not triggered through update
method when init from a dict, therefore these values remain as is (BSE
) when it should be capitalized (Bse
).
pymatgen/src/pymatgen/io/vasp/inputs.py
Lines 718 to 737 in ed8542d
def __init__(self, params: dict[str, Any] | None = None) -> None: | |
""" | |
Create an Incar object. | |
Args: | |
params (dict): Input parameters as a dictionary. | |
""" | |
super().__init__() | |
if params is not None: | |
# If INCAR contains vector-like MAGMOMS given as a list | |
# of floats, convert to a list of lists | |
if (params.get("MAGMOM") and isinstance(params["MAGMOM"][0], int | float)) and ( | |
params.get("LSORBIT") or params.get("LNONCOLLINEAR") | |
): | |
val = [] | |
for idx in range(len(params["MAGMOM"]) // 3): | |
val.append(params["MAGMOM"][idx * 3 : (idx + 1) * 3]) | |
params["MAGMOM"] = val | |
self.update(params) |
Incar.check_params
handling of case sensitivity, make Incar
keys case insensitiveIncar
keys case insensitive, fix init Incar
from dict val processing for str/float/int
Thanks. |
No problem, thanks for the helpful discussion everyone |
I think that one special key is SYSTEM - it is usually free text and capitalizing it can be awkward. |
That's a good point to me. Update: I released comparing two INCARs (especially to check if they produce the same result) through directly
|
Summary
Incar.check_params
handling of case sensitivity, to fix VaspInput setter and Incar.check_params() are inconsistent #4119, also add unit testsIncar
Make more operations (get/update/setdefault) in
Incar
case insensitiveIncar
from dict (also thefrom_dict
method) doesn't seem to use setter method, might be better off subclass UserDictInit
Incar
from dict doesn't call setter method, leading to unprocessed str/float/int valueproc_val
when val is string, leading to inconsistent behaviour when init from dict for int/floatupdate
method when subclassing builtindict
, leading to val not passing throughproc_val
when init from dict