Skip to content

Commit f42028a

Browse files
authored
JDFTXStructure - partial fix for special case lattice tags (#4401)
* Adding `NotImplementedError` for Structure <-> JDFTXInfile conversion for JDFTXInfile with special case lattices (aka lattices not provided as a 3x3 matrix) * Adding partial implementation of JDFTXStructure <-> JDFTXInfile special case lattice
1 parent 2283493 commit f42028a

File tree

2 files changed

+91
-11
lines changed

2 files changed

+91
-11
lines changed

src/pymatgen/io/jdftx/inputs.py

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class is written.
2020
import scipy.constants as const
2121
from monty.json import MSONable
2222

23-
from pymatgen.core import Structure
23+
from pymatgen.core import Lattice, Structure
2424
from pymatgen.core.periodic_table import Element
2525
from pymatgen.core.units import bohr_to_ang
2626
from pymatgen.io.jdftx.generic_tags import AbstractTag, BoolTagContainer, DumpTagContainer, MultiformatTag, TagContainer
@@ -788,17 +788,8 @@ def from_jdftxinfile(cls, jdftxinfile: JDFTXInfile, sort_structure: bool = False
788788
Returns:
789789
JDFTXStructure: The created JDFTXStructure object.
790790
"""
791-
jl = jdftxinfile["lattice"]
792-
lattice = np.zeros([3, 3])
793-
for i in range(3):
794-
for j in range(3):
795-
lattice[i][j] += float(jl[f"R{i}{j}"])
796-
if "latt-scale" in jdftxinfile:
797-
latt_scale = np.array([jdftxinfile["latt-scale"][x] for x in ["s0", "s1", "s2"]])
798-
lattice *= latt_scale
799-
lattice = lattice.T # convert to row vector format
800-
lattice *= const.value("Bohr radius") * 10**10 # Bohr radius in Ang; convert to Ang
801791

792+
lattice = _infile_to_pmg_lattice(jdftxinfile)
802793
atomic_symbols = [x["species-id"] for x in jdftxinfile["ion"]]
803794
coords = np.array([[x["x0"], x["x1"], x["x2"]] for x in jdftxinfile["ion"]])
804795
coords *= const.value("Bohr radius") * 10**10 # Bohr radius in Ang; convert to Ang
@@ -1016,3 +1007,58 @@ def _multi_getattr(varbase: Any, varname: str):
10161007
for var in varlist:
10171008
varbase = getattr(varbase, var)
10181009
return varbase
1010+
1011+
1012+
def _infile_to_pmg_lattice(jdftxinfile: JDFTXInfile) -> Lattice:
1013+
jl = jdftxinfile["lattice"]
1014+
if "R00" not in jl:
1015+
return _infile_special_format_to_pmg_lattice(jdftxinfile)
1016+
return _infile_matrix_format_to_pmg_lattice(jdftxinfile)
1017+
1018+
1019+
def _infile_special_format_to_pmg_lattice(jdftxinfile: JDFTXInfile) -> Lattice:
1020+
jl = jdftxinfile["lattice"]
1021+
if "modification" in jl:
1022+
raise NotImplementedError("Special case lattices with modification not implemented yet")
1023+
# Second boolean to check if latt-scale was passed but does nothing
1024+
if ("latt-scale" in jl) and (not all(np.isclose(float(x), 1) in jl for x in ["s0", "s1", "s2"])):
1025+
raise NotImplementedError("latt-scale for special case lattices not implemented yet")
1026+
if "Monoclinic" in jl:
1027+
lattice = Lattice.monoclinic(jl["a"], jl["b"], jl["c"], jl["beta"])
1028+
elif "Orthorhombic" in jl:
1029+
lattice = Lattice.orthorhombic(jl["a"], jl["b"], jl["c"])
1030+
elif "Cubic" in jl:
1031+
lattice = Lattice.cubic(jl["a"])
1032+
elif "Hexagonal" in jl:
1033+
lattice = Lattice.hexagonal(jl["a"], jl["c"])
1034+
elif "Rhombohedral" in jl:
1035+
lattice = Lattice.rhombohedral(jl["a"], jl["alpha"])
1036+
elif "Tetragonal" in jl:
1037+
lattice = Lattice.tetragonal(jl["a"], jl["c"])
1038+
elif "Triclinic" in jl:
1039+
lattice = Lattice.from_parameters(jl["a"], jl["b"], jl["c"], jl["alpha"], jl["beta"], jl["gamma"])
1040+
else:
1041+
raise ValueError(f"Unable to convert JDFTX lattice tag {jl} to pymatgen Lattice object")
1042+
return lattice
1043+
1044+
1045+
def _infile_matrix_format_to_pmg_lattice(jdftxinfile: JDFTXInfile) -> Lattice:
1046+
"""Convert JDFTX lattice tag to pymatgen Lattice object.
1047+
1048+
Args:
1049+
jl (dict[str, Any]): JDFTX lattice tag.
1050+
1051+
Returns:
1052+
Lattice: Pymatgen Lattice object.
1053+
"""
1054+
_lattice = np.zeros([3, 3])
1055+
jl = jdftxinfile["lattice"]
1056+
for i in range(3):
1057+
for j in range(3):
1058+
_lattice[i][j] += float(jl[f"R{i}{j}"])
1059+
if "latt-scale" in jdftxinfile:
1060+
latt_scale = np.array([jdftxinfile["latt-scale"][x] for x in ["s0", "s1", "s2"]])
1061+
_lattice *= latt_scale
1062+
_lattice = _lattice.T # convert to row vector format
1063+
_lattice *= const.value("Bohr radius") * 10**10 # Bohr radius in Ang; convert to Ang
1064+
return Lattice(_lattice)

tests/io/jdftx/test_jdftxinfile.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,40 @@ def test_lattice_writing(value_str: str):
487487
)
488488

489489

490+
@pytest.mark.parametrize(
491+
("value_str"),
492+
[
493+
("Rhombohedral 1.0 1.0"),
494+
("Triclinic 1.0 1.0 1.0 1.0 1.0 1.0"),
495+
("Hexagonal 1.0 1.0"),
496+
("Body-Centered Cubic 1.0"),
497+
("Cubic 1.0"),
498+
("Orthorhombic 1.0 1.0 1.0"),
499+
("Base-Centered Orthorhombic 1.0 1.0 1.0"),
500+
("Monoclinic 1.0 1.0 1.0 1.0"),
501+
("Base-Centered Monoclinic 1.0 1.0 1.0 1.0"),
502+
("Tetragonal 1.0 1.0"),
503+
("Body-Centered Tetragonal 1.0 1.0"),
504+
],
505+
)
506+
def test_jdftxstructure_lattice_conversion(value_str: str):
507+
test_vars = ["a", "b", "c", "alpha", "beta", "gamma"]
508+
mft_lattice_tag = get_tag_object("lattice")
509+
assert mft_lattice_tag is not None
510+
i = mft_lattice_tag.get_format_index_for_str_value("lattice", value_str)
511+
tag_object = mft_lattice_tag.format_options[i]
512+
parsed_tag = tag_object.read("lattice", value_str)
513+
infile = JDFTXInfile.from_str("lattice " + value_str + "\n ion H 0.0 0.0 0.0 0", dont_require_structure=True)
514+
if "modification" in parsed_tag:
515+
with pytest.raises(NotImplementedError):
516+
_ = infile.to_pmg_structure(infile)
517+
else:
518+
structure = infile.to_pmg_structure(infile)
519+
for var in test_vars:
520+
if var in parsed_tag:
521+
assert_same_value(float(getattr(structure.lattice, var)), float(parsed_tag[var]))
522+
523+
490524
# This fails, but I don't think we need to support this
491525
# @pytest.mark.parametrize(
492526
# ("unordered_dict", "expected_out"),

0 commit comments

Comments
 (0)