From 730d0c4e19e3bf0ed13e20951e40a84396f5ed91 Mon Sep 17 00:00:00 2001 From: cote3804 Date: Wed, 13 Aug 2025 13:35:49 -0400 Subject: [PATCH 1/2] Addition of JDFTx set to pymatgen from atomate2. --- src/pymatgen/io/jdftx/BaseJdftxSet.yaml | 67 ++++++++++++++++ src/pymatgen/io/jdftx/sets.py | 100 ++++++++++++++++++++++++ tests/io/jdftx/test_sets.py | 29 +++++++ 3 files changed, 196 insertions(+) create mode 100644 src/pymatgen/io/jdftx/BaseJdftxSet.yaml create mode 100644 src/pymatgen/io/jdftx/sets.py create mode 100644 tests/io/jdftx/test_sets.py diff --git a/src/pymatgen/io/jdftx/BaseJdftxSet.yaml b/src/pymatgen/io/jdftx/BaseJdftxSet.yaml new file mode 100644 index 00000000000..f6aa8f6bf6c --- /dev/null +++ b/src/pymatgen/io/jdftx/BaseJdftxSet.yaml @@ -0,0 +1,67 @@ +# Default JDFTx settings for atomate2 calculations. +### Functional ### +elec-ex-corr: gga +van-der-waals: D3 + +### Electronic Parameters ### +elec-cutoff: + Ecut: 20 + EcutRho: 100 +electronic-minimize: + nIterations: 100 + energyDiffThreshold: 1.0e-07 +elec-smearing: + smearingType: Fermi + smearingWidth: 0.001 +# elec-initial-magnetization: +# M: 0 +# constrain: False +spintype: z-spin +core-overlap-check: none +converge-empty-states: True +band-projection-params: + ortho: True + norm: False + +### Lattice / Unit Cell ### +latt-move-scale: + s0: 0 + s1: 0 + s2: 0 +lattice-minimize: + nIterations: 00 +symmetries: none +#coulomb-interaction: slab 001 +#coords-type Lattice + +### Solvation & Bias ### +# fluid: LinearPCM +# pcm-variant: CANDLE +# fluid-solvent: H2O +# fluid-cation: +# name: Na+ +# concentration: 0.5 +# fluid-anion: +# name: F- +# concentration: 0.5 + +### Pseudopotential ### +ion-species: GBRV_v1.5/$ID_pbe_v1.uspp + + +### Output Files ### +dump-name: jdftx.$VAR +dump: + - End: + Dtot: True + State: True + BoundCharge: True + Forces: True + Ecomponents: True + VfluidTot: True + ElecDensity: True + KEdensity: True + EigStats: True + BandEigs: True + BandProjections: True + DOS: True diff --git a/src/pymatgen/io/jdftx/sets.py b/src/pymatgen/io/jdftx/sets.py new file mode 100644 index 00000000000..5c9227c918e --- /dev/null +++ b/src/pymatgen/io/jdftx/sets.py @@ -0,0 +1,100 @@ +from __future__ import annotations + +import os +from pathlib import Path +from typing import TYPE_CHECKING + +from pymatgen.io.core import InputSet +from pymatgen.io.jdftx.inputs import JDFTXInfile, JDFTXStructure + +if TYPE_CHECKING: + from pymatgen.core import Structure + from pymatgen.util.typing import PathLike + +FILE_NAMES = {"in": "init.in", "out": "jdftx.out"} + + +class JdftxInputSet(InputSet): + """ + A class to represent a JDFTx input file as a JDFTx InputSet. + + Parameters + ---------- + jdftxinput + A JdftxInput object + """ + + def __init__(self, jdftxinput: JDFTXInfile, structure: Structure) -> None: + self.structure = structure + self.jdftxinput = jdftxinput + + def write_input( + self, + directory: str | Path, + infile: PathLike = FILE_NAMES["in"], + make_dir: bool = True, + overwrite: bool = True, + ) -> None: + """Write JDFTx input file to a directory. + + Parameters + ---------- + directory + Directory to write input files to. + make_dir + Whether to create the directory if it does not already exist. + overwrite + Whether to overwrite an input file if it already exists. + """ + directory = Path(directory) + if make_dir: + os.makedirs(directory, exist_ok=True) + + if not overwrite and (directory / infile).exists(): + raise FileExistsError(f"{directory / infile} already exists.") + jdftx_structure = JDFTXStructure(structure=self.structure) + jdftxinput = condense_jdftxinputs(self.jdftxinput, jdftx_structure) + + jdftxinput.write_file(filename=(directory / infile)) + + @staticmethod + def from_file( + file: PathLike, + ) -> JdftxInputSet: + """Load a set of JDFTx inputs from a filename. + + Parameters + ---------- + directory + Input file to read JDFTx inputs from. + """ + jdftxinput = JDFTXInfile.from_file(file) + structure = jdftxinput.structure + return JdftxInputSet(jdftxinput=jdftxinput, structure=structure) + + +def condense_jdftxinputs(jdftxinput: JDFTXInfile, jdftxstructure: JDFTXStructure) -> JDFTXInfile: + """ + Combine JDFTXInfile and JDFTxStructure into complete JDFTXInfile. + + Function combines a JDFTXInfile class with calculation + settings and a JDFTxStructure that defines the structure + into one JDFTXInfile instance. + + Parameters + ---------- + jdftxinput: JDFTXInfile + A JDFTXInfile object with calculation settings. + + jdftxstructure: JDFTXStructure + A JDFTXStructure object that defines the structure. + + Returns + ------- + JDFTXInfile + A JDFTXInfile that includes the calculation + parameters and input structure. + """ + # force Cartesian coordinates + coords_type = jdftxinput.get("coords-type") + return jdftxinput + JDFTXInfile.from_str(jdftxstructure.get_str(in_cart_coords=(coords_type == "Cartesian"))) diff --git a/tests/io/jdftx/test_sets.py b/tests/io/jdftx/test_sets.py new file mode 100644 index 00000000000..a6b9e8077a5 --- /dev/null +++ b/tests/io/jdftx/test_sets.py @@ -0,0 +1,29 @@ +"""Tests for JDFTx input sets.""" + +from __future__ import annotations + +from pathlib import Path + +from pymatgen.io.jdftx.inputs import JDFTXInfile +from pymatgen.io.jdftx.sets import JdftxInputSet +from pymatgen.util.testing import TEST_FILES_DIR + +from .inputs_test_utils import assert_idential_jif, ex_infile1_fname + +ex_files_dir = Path(TEST_FILES_DIR) / "io" / "jdftx" / "example_files" + + +def test_jdftxinputset_from_directory(): + input_set = JdftxInputSet.from_file(ex_infile1_fname) + jdftx_inputfile = JDFTXInfile.from_file(ex_infile1_fname) + assert_idential_jif(input_set.jdftxinput, jdftx_inputfile) + + +def test_jdftxinputset_write_file(tmp_path): + jdftx_inputfile = JDFTXInfile.from_file(ex_infile1_fname) + input_set = JdftxInputSet(jdftx_inputfile, jdftx_inputfile.structure) + input_set.write_input(tmp_path, infile="test.in") + written_file = tmp_path / "test.in" + read_jdftx_inputfile = JDFTXInfile.from_file(written_file) + assert written_file.exists() + assert_idential_jif(read_jdftx_inputfile, jdftx_inputfile) From 90bd5c62e9993715a776c2b92a18aef038720d22 Mon Sep 17 00:00:00 2001 From: cote3804 Date: Wed, 13 Aug 2025 14:36:39 -0400 Subject: [PATCH 2/2] None structure handling in input set generation --- src/pymatgen/io/jdftx/sets.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pymatgen/io/jdftx/sets.py b/src/pymatgen/io/jdftx/sets.py index 5c9227c918e..516d0f20fcf 100644 --- a/src/pymatgen/io/jdftx/sets.py +++ b/src/pymatgen/io/jdftx/sets.py @@ -70,6 +70,8 @@ def from_file( """ jdftxinput = JDFTXInfile.from_file(file) structure = jdftxinput.structure + if structure is None: + raise ValueError(f"Structure not defined in file {file}.") return JdftxInputSet(jdftxinput=jdftxinput, structure=structure)