diff --git a/.gitignore b/.gitignore
index 42de640a..6f52fd35 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,7 @@
*.dot
*.png
*.svg
+*.xz
~*
*~
.project
diff --git a/docs/source/images/NaCl-bands.svg b/docs/source/images/NaCl-bands.svg
new file mode 100644
index 00000000..8e0a52e2
--- /dev/null
+++ b/docs/source/images/NaCl-bands.svg
@@ -0,0 +1,15915 @@
+
+
+
diff --git a/docs/source/user_guide/command_line.rst b/docs/source/user_guide/command_line.rst
index 55d16002..a7ea5b04 100644
--- a/docs/source/user_guide/command_line.rst
+++ b/docs/source/user_guide/command_line.rst
@@ -320,7 +320,8 @@ Calculate phonons with a 2x2x2 supercell, after geometry optimization (using the
This will save the Phonopy parameters, including displacements and force constants, to ``NaCl-phonopy.yml`` and ``NaCl-force_constants.hdf5``,
in addition to generating a log file, ``NaCl-phonons-log.yml``, and summary of inputs, ``NaCl-phonons-summary.yml``.
-Additionally, the ``--bands`` option can be added to calculate the band structure and save the results to ``NaCl-auto_bands.yml``:
+Additionally, the ``--bands`` option can be added to calculate the band structure
+and save the results to a compressed yaml file, ``NaCl-auto_bands.yml.xz``:
.. code-block:: bash
@@ -348,6 +349,80 @@ Similar to Phonopy, the supercell matrix can be defined in three ways:
For all options, run ``janus phonons --help``.
+Band paths
+++++++++++
+
+By default, q-points along BZ high symmetry paths are generated using `SeeK-path `_,
+but band paths can also be specified explicitly using the ``--paths`` option to specify a yaml file.
+
+.. code-block:: bash
+
+ janus phonons --struct tests/data/NaCl.cif --bands --plot-to-file --paths tests/data/paths.yml
+
+
+This will save the results in a compressed yaml file, ``NaCl-bands.yml.xz``, as well as the generated plot, ``NaCl-bands.svg``.
+
+The ``--paths`` file must include:
+
+- ``labels``, which label band segment points
+
+- ``paths``, which list reciprocal points in reduced coordinates to give the band paths
+
+ - Multiple lists can be specified to define disconnected paths
+
+- ``npoints``, which gives the number of sampling points, including path ends, in each path segment
+
+These correspond to ``BAND_LABELS``, ``BAND``, and ``BAND_POINTS`` in `phonopy `_.
+
+For example:
+
+.. code-block:: yaml
+
+ labels:
+ - $\Gamma$
+ - $\mathrm{X}$
+ - $\mathrm{U}$
+ - $\mathrm{K}$
+ - $\Gamma$
+ - $\mathrm{L}$
+ - $\mathrm{W}$
+ - $\mathrm{X}$
+ npoints: 101
+ paths:
+ - - - 0.0
+ - 0.0
+ - 0.0
+ - - 0.5
+ - 0.0
+ - 0.5
+ - - 0.625
+ - 0.25
+ - 0.625
+ - - - 0.375
+ - 0.375
+ - 0.75
+ - - 0.0
+ - 0.0
+ - 0.0
+ - - 0.5
+ - 0.5
+ - 0.5
+ - - 0.5
+ - 0.25
+ - 0.75
+ - - 0.5
+ - 0.0
+ - 0.5
+
+
+This defines two disconnected paths, one between :math:`{\Gamma}`, :math:`X` and :math:`U`,
+and one between :math:`K`, :math:`{\Gamma}`, :math:`L`, :math:`W`, and :math:`X`,
+with 101 sampling points for each path segment.
+
+.. image:: ../images/NaCl-bands.svg
+ :height: 700px
+ :align: center
+
Training and fine-tuning MLIPs
------------------------------
diff --git a/janus_core/calculations/phonons.py b/janus_core/calculations/phonons.py
index b69c5ed6..0718908c 100644
--- a/janus_core/calculations/phonons.py
+++ b/janus_core/calculations/phonons.py
@@ -9,7 +9,12 @@
from numpy import ndarray
import phonopy
from phonopy.file_IO import write_force_constants_to_hdf5
+from phonopy.phonon.band_structure import (
+ get_band_qpoints_and_path_connections,
+ get_band_qpoints_by_seekpath,
+)
from phonopy.structure.atoms import PhonopyAtoms
+from yaml import safe_load
from janus_core.calculations.base import BaseCalculation
from janus_core.calculations.geom_opt import GeomOpt
@@ -22,7 +27,7 @@
PathLike,
PhononCalcs,
)
-from janus_core.helpers.utils import none_to_dict, track_progress, write_table
+from janus_core.helpers.utils import none_to_dict, track_progress
class Phonons(BaseCalculation):
@@ -71,6 +76,8 @@ class Phonons(BaseCalculation):
three as the second row, etc. Default is 2.
displacement : float
Displacement for force constants calculation, in A. Default is 0.01.
+ displacement_kwargs : dict[str, Any] | None
+ Keyword arguments to pass to generate_displacements. Default is {}.
mesh : tuple[int, int, int]
Mesh for sampling. Default is (10, 10, 10).
symmetrize : bool
@@ -81,6 +88,16 @@ class Phonons(BaseCalculation):
Default is False.
minimize_kwargs : dict[str, Any] | None
Keyword arguments to pass to geometry optimizer. Default is {}.
+ n_qpoints : int
+ Number of q-points to sample along generated path, including end points.
+ Unused if `qpoint_file` is specified. Default is 51.
+ qpoint_file : PathLike | None
+ Path to yaml file with info to generate a path of q-points for band structure.
+ Default is None.
+ dos_kwargs : dict[str, Any] | None
+ Keyword arguments to pass to run_total_dos. Default is {}.
+ pdos_kwargs : dict[str, Any] | None
+ Keyword arguments to pass to run_projected_dos. Default is {}.
temp_min : float
Start temperature for thermal properties calculations, in K. Default is 0.0.
temp_max : float
@@ -88,8 +105,7 @@ class Phonons(BaseCalculation):
temp_step : float
Temperature step for thermal properties calculations, in K. Default is 50.0.
force_consts_to_hdf5 : bool
- Whether to write force constants in hdf format or not.
- Default is True.
+ Whether to write force constants in hdf format or not. Default is True.
plot_to_file : bool
Whether to plot various graphs as band stuctures, dos/pdos in svg.
Default is False.
@@ -115,7 +131,7 @@ class Phonons(BaseCalculation):
-------
calc_force_constants(write_force_consts)
Calculate force constants and optionally write results.
- write_force_constants(phonopy_file, force_consts_to_hdf5 force_consts_file)
+ write_force_constants(phonopy_file, force_consts_to_hdf5, force_consts_file)
Write results of force constants calculations.
calc_bands(write_bands)
Calculate band structure and optionally write and plot results.
@@ -154,10 +170,15 @@ def __init__(
calcs: MaybeSequence[PhononCalcs] = (),
supercell: MaybeList[int] = 2,
displacement: float = 0.01,
+ displacement_kwargs: dict[str, Any] | None = None,
mesh: tuple[int, int, int] = (10, 10, 10),
symmetrize: bool = False,
minimize: bool = False,
minimize_kwargs: dict[str, Any] | None = None,
+ n_qpoints: int = 51,
+ qpoint_file: PathLike | None = None,
+ dos_kwargs: dict[str, Any] | None = None,
+ pdos_kwargs: dict[str, Any] | None = None,
temp_min: float = 0.0,
temp_max: float = 1000.0,
temp_step: float = 50.0,
@@ -213,6 +234,8 @@ def __init__(
three as the second row, etc. Default is 2.
displacement : float
Displacement for force constants calculation, in A. Default is 0.01.
+ displacement_kwargs : dict[str, Any] | None
+ Keyword arguments to pass to generate_displacements. Default is {}.
mesh : tuple[int, int, int]
Mesh for sampling. Default is (10, 10, 10).
symmetrize : bool
@@ -223,6 +246,16 @@ def __init__(
Default is False.
minimize_kwargs : dict[str, Any] | None
Keyword arguments to pass to geometry optimizer. Default is {}.
+ n_qpoints : int
+ Number of q-points to sample along generated path, including end points.
+ Unused if `qpoint_file` is specified. Default is 51.
+ qpoint_file : PathLike | None
+ Path to yaml file with info to generate a path of q-points for band
+ structure. Default is None.
+ dos_kwargs : dict[str, Any] | None
+ Keyword arguments to pass to run_total_dos. Default is {}.
+ pdos_kwargs : dict[str, Any] | None
+ Keyword arguments to pass to run_projected_dos. Default is {}.
temp_min : float
Start temperature for thermal calculations, in K. Default is 0.0.
temp_max : float
@@ -230,8 +263,7 @@ def __init__(
temp_step : float
Temperature step for thermal calculations, in K. Default is 50.0.
force_consts_to_hdf5 : bool
- Whether to write force constants in hdf format or not.
- Default is True.
+ Whether to write force constants in hdf format or not. Default is True.
plot_to_file : bool
Whether to plot various graphs as band stuctures, dos/pdos in svg.
Default is False.
@@ -246,14 +278,29 @@ def __init__(
enable_progress_bar : bool
Whether to show a progress bar during phonon calculations. Default is False.
"""
- (read_kwargs, minimize_kwargs) = none_to_dict((read_kwargs, minimize_kwargs))
+ (read_kwargs, displacement_kwargs, minimize_kwargs, dos_kwargs, pdos_kwargs) = (
+ none_to_dict(
+ (
+ read_kwargs,
+ displacement_kwargs,
+ minimize_kwargs,
+ dos_kwargs,
+ pdos_kwargs,
+ )
+ )
+ )
self.calcs = calcs
self.displacement = displacement
+ self.displacement_kwargs = displacement_kwargs
self.mesh = mesh
self.symmetrize = symmetrize
self.minimize = minimize
self.minimize_kwargs = minimize_kwargs
+ self.n_qpoints = n_qpoints
+ self.qpoint_file = qpoint_file
+ self.dos_kwargs = dos_kwargs
+ self.pdos_kwargs = pdos_kwargs
self.temp_min = temp_min
self.temp_max = temp_max
self.temp_step = temp_step
@@ -406,7 +453,10 @@ def calc_force_constants(
)
phonon = phonopy.Phonopy(cell, supercell_matrix)
- phonon.generate_displacements(distance=self.displacement)
+ phonon.generate_displacements(
+ distance=self.displacement,
+ **self.displacement_kwargs,
+ )
disp_supercells = phonon.supercells_with_displacements
if self.enable_progress_bar:
@@ -534,17 +584,44 @@ def write_bands(
if save_plots is None:
save_plots = self.plot_to_file
- bands_file = self._build_filename("auto_bands.yml", filename=bands_file)
- self.results["phonon"].auto_band_structure(
- write_yaml=True,
- filename=bands_file,
+ if self.qpoint_file:
+ bands_file = self._build_filename("bands.yml.xz", filename=bands_file)
+
+ with open(self.qpoint_file, encoding="utf8") as file:
+ paths_info = safe_load(file)
+
+ labels = paths_info["labels"]
+ num_q_points = sum(len(q) for q in paths_info["paths"])
+ num_labels = len(labels)
+ assert (
+ num_q_points == num_labels
+ ), "Number of labels is different to number of q-points specified"
+
+ q_points, connections = get_band_qpoints_and_path_connections(
+ band_paths=paths_info["paths"], npoints=paths_info["npoints"]
+ )
+
+ else:
+ bands_file = self._build_filename("auto_bands.yml.xz", filename=bands_file)
+ q_points, labels, connections = get_band_qpoints_by_seekpath(
+ self.results["phonon"].primitive, self.n_qpoints
+ )
+
+ self.results["phonon"].run_band_structure(
+ paths=q_points,
+ path_connections=connections,
+ labels=labels,
with_eigenvectors=self.write_full,
with_group_velocities=self.write_full,
)
+ self.results["phonon"].write_yaml_band_structure(
+ filename=bands_file,
+ compression="lzma",
+ )
bplt = self.results["phonon"].plot_band_structure()
if save_plots:
- plot_file = self._build_filename("auto_bands.svg", filename=plot_file)
+ plot_file = self._build_filename("bands.svg", filename=plot_file)
bplt.savefig(plot_file)
def calc_thermal_props(
@@ -610,23 +687,8 @@ def write_thermal_props(self, thermal_file: PathLike | None = None) -> None:
Name of data file to save thermal properties. Default is inferred from
`file_prefix`.
"""
- thermal_file = self._build_filename("thermal.dat", filename=thermal_file)
-
- data = {
- "temperature": self.results["thermal_properties"]["temperatures"],
- "Cv": self.results["thermal_properties"]["heat_capacity"],
- "H": self.results["thermal_properties"]["free_energy"],
- "S": self.results["thermal_properties"]["entropy"],
- }
-
- with open(thermal_file, "w", encoding="utf8") as out:
- write_table(
- fmt="ascii",
- file=out,
- **data,
- units={"temperature": "K", "Cv": "J/mol/K", "H": "eV", "S": "eV"},
- formats=dict.fromkeys(data, ".8f"),
- )
+ thermal_file = self._build_filename("thermal.yml", filename=thermal_file)
+ self.results["phonon"].write_yaml_thermal_properties(filename=thermal_file)
def calc_dos(
self,
@@ -664,7 +726,7 @@ def calc_dos(
self.tracker.start_task("DOS calculation")
self.results["phonon"].run_mesh(mesh)
- self.results["phonon"].run_total_dos()
+ self.results["phonon"].run_total_dos(**self.dos_kwargs)
if self.logger:
self.logger.info("DOS calculation complete")
@@ -773,7 +835,7 @@ def calc_pdos(
self.results["phonon"].run_mesh(
mesh, with_eigenvectors=True, is_mesh_symmetry=False
)
- self.results["phonon"].run_projected_dos()
+ self.results["phonon"].run_projected_dos(**self.pdos_kwargs)
if self.logger:
self.logger.info("PDOS calculation complete")
diff --git a/janus_core/cli/phonons.py b/janus_core/cli/phonons.py
index d1ee845f..85724fb8 100644
--- a/janus_core/cli/phonons.py
+++ b/janus_core/cli/phonons.py
@@ -15,9 +15,12 @@
Architecture,
CalcKwargs,
Device,
+ DisplacementKwargs,
+ DoSKwargs,
LogPath,
MinimizeKwargs,
ModelPath,
+ PDoSKwargs,
ReadKwargsLast,
StructPath,
Summary,
@@ -46,6 +49,7 @@ def phonons(
displacement: Annotated[
float, Option(help="Displacement for force constants calculation, in A.")
] = 0.01,
+ displacement_kwargs: DisplacementKwargs = None,
mesh: Annotated[
tuple[int, int, int], Option(help="Mesh numbers along a, b, c axes.")
] = (10, 10, 10),
@@ -53,8 +57,28 @@ def phonons(
bool,
Option(help="Whether to compute band structure."),
] = False,
+ n_qpoints: Annotated[
+ int,
+ Option(
+ help=(
+ "Number of q-points to sample along generated path, including end "
+ "points. Unused if `qpoint_file` is specified"
+ )
+ ),
+ ] = 51,
+ qpoint_file: Annotated[
+ Optional[Path],
+ Option(
+ help=(
+ "Path to yaml file with info to generate a path of q-points for band "
+ "structure."
+ )
+ ),
+ ] = None,
dos: Annotated[bool, Option(help="Whether to calculate the DOS.")] = False,
+ dos_kwargs: DoSKwargs = None,
pdos: Annotated[bool, Option(help="Whether to calculate the PDOS.")] = False,
+ pdos_kwargs: PDoSKwargs = None,
thermal: Annotated[
bool, Option(help="Whether to calculate thermal properties.")
] = False,
@@ -134,14 +158,26 @@ def phonons(
matrix row-wise.
displacement : float
Displacement for force constants calculation, in A. Default is 0.01.
+ displacement_kwargs : Optional[dict[str, Any]]
+ Keyword arguments to pass to generate_displacements. Default is {}.
mesh : tuple[int, int, int]
Mesh for sampling. Default is (10, 10, 10).
bands : bool
Whether to calculate and save the band structure. Default is False.
+ n_qpoints : int
+ Number of q-points to sample along generated path, including end points.
+ Unused if `qpoint_file` is specified. Default is 51.
+ qpoint_file : Optional[PathLike]
+ Path to yaml file with info to generate a path of q-points for band structure.
+ Default is None.
dos : bool
Whether to calculate and save the DOS. Default is False.
+ dos_kwargs : Optional[dict[str, Any]]
+ Other keyword arguments to pass to run_total_dos. Default is {}.
pdos : bool
Whether to calculate and save the PDOS. Default is False.
+ pdos_kwargs : Optional[dict[str, Any]]
+ Other keyword arguments to pass to run_projected_dos. Default is {}.
thermal : bool
Whether to calculate thermal properties. Default is False.
temp_min : float
@@ -210,8 +246,22 @@ def phonons(
# Check options from configuration file are all valid
check_config(ctx)
- read_kwargs, calc_kwargs, minimize_kwargs = parse_typer_dicts(
- [read_kwargs, calc_kwargs, minimize_kwargs]
+ (
+ displacement_kwargs,
+ read_kwargs,
+ calc_kwargs,
+ minimize_kwargs,
+ dos_kwargs,
+ pdos_kwargs,
+ ) = parse_typer_dicts(
+ [
+ displacement_kwargs,
+ read_kwargs,
+ calc_kwargs,
+ minimize_kwargs,
+ dos_kwargs,
+ pdos_kwargs,
+ ]
)
# Read only first structure by default and ensure only one image is read
@@ -266,10 +316,15 @@ def phonons(
"calcs": calcs,
"supercell": supercell,
"displacement": displacement,
+ "displacement_kwargs": displacement_kwargs,
"mesh": mesh,
"symmetrize": symmetrize,
"minimize": minimize,
"minimize_kwargs": minimize_kwargs,
+ "n_qpoints": n_qpoints,
+ "qpoint_file": qpoint_file,
+ "dos_kwargs": dos_kwargs,
+ "pdos_kwargs": pdos_kwargs,
"temp_min": temp_min,
"temp_max": temp_max,
"temp_step": temp_step,
diff --git a/janus_core/cli/types.py b/janus_core/cli/types.py
index 31143a94..b37e8a70 100644
--- a/janus_core/cli/types.py
+++ b/janus_core/cli/types.py
@@ -159,6 +159,34 @@ def __str__(self) -> str:
),
]
+DoSKwargs = Annotated[
+ Optional[TyperDict],
+ Option(
+ parser=parse_dict_class,
+ help=(
+ """
+ Keyword arguments to pass to run_total_dos. Must be passed as a dictionary
+ wrapped in quotes, e.g. "{'key' : value}".
+ """
+ ),
+ metavar="DICT",
+ ),
+]
+
+PDoSKwargs = Annotated[
+ Optional[TyperDict],
+ Option(
+ parser=parse_dict_class,
+ help=(
+ """
+ Keyword arguments to pass to run_projected_dos. Must be passed as a
+ dictionary wrapped in quotes, e.g. "{'key' : value}".
+ """
+ ),
+ metavar="DICT",
+ ),
+]
+
EnsembleKwargs = Annotated[
Optional[TyperDict],
Option(
@@ -173,6 +201,20 @@ def __str__(self) -> str:
),
]
+DisplacementKwargs = Annotated[
+ Optional[TyperDict],
+ Option(
+ parser=parse_dict_class,
+ help=(
+ """
+ Keyword arguments to pass to generate_displacements. Must be passed as a
+ dictionary wrapped in quotes, e.g. "{'key' : value}".
+ """
+ ),
+ metavar="DICT",
+ ),
+]
+
PostProcessKwargs = Annotated[
Optional[TyperDict],
Option(
diff --git a/tests/data/paths.yml b/tests/data/paths.yml
new file mode 100644
index 00000000..5d2fcd9e
--- /dev/null
+++ b/tests/data/paths.yml
@@ -0,0 +1,11 @@
+labels:
+- $\mathrm{K}$
+- $\Gamma$
+npoints: 11
+paths:
+- - - 0.375
+ - 0.375
+ - 0.75
+ - - 0.0
+ - 0.0
+ - 0.0
diff --git a/tests/test_phonons.py b/tests/test_phonons.py
index f36bc625..1d03ebae 100644
--- a/tests/test_phonons.py
+++ b/tests/test_phonons.py
@@ -128,7 +128,7 @@ def test_symmetrize(tmp_path):
phonons_1.calc_force_constants()
phonons_2 = Phonons(
- struct=single_point.struct.copy(),
+ struct=phonons_1.struct.copy(),
write_results=False,
minimize=True,
minimize_kwargs={"fmax": 0.001},
diff --git a/tests/test_phonons_cli.py b/tests/test_phonons_cli.py
index e15dd66a..b66c8d2a 100644
--- a/tests/test_phonons_cli.py
+++ b/tests/test_phonons_cli.py
@@ -2,6 +2,7 @@
from __future__ import annotations
+import lzma
from pathlib import Path
import pytest
@@ -26,7 +27,7 @@ def test_help():
def test_phonons():
"""Test calculating force constants and band structure."""
phonopy_path = Path("./NaCl-phonopy.yml").absolute()
- bands_path = Path("./NaCl-auto_bands.yml").absolute()
+ bands_path = Path("./NaCl-auto_bands.yml.xz").absolute()
log_path = Path("./NaCl-phonons-log.yml").absolute()
summary_path = Path("./NaCl-phonons-summary.yml").absolute()
@@ -59,7 +60,7 @@ def test_phonons():
has_eigenvectors = False
has_velocity = False
- with open(bands_path, encoding="utf8") as file:
+ with lzma.open(bands_path, mode="rt") as file:
for line in file:
if "eigenvector" in line:
has_eigenvectors = True
@@ -89,7 +90,7 @@ def test_phonons():
def test_bands_simple(tmp_path):
"""Test calculating force constants and reduced bands information."""
file_prefix = tmp_path / "NaCl"
- autoband_results = tmp_path / "NaCl-auto_bands.yml"
+ autoband_results = tmp_path / "NaCl-auto_bands.yml.xz"
summary_path = tmp_path / "NaCl-phonons-summary.yml"
result = runner.invoke(
@@ -99,6 +100,8 @@ def test_bands_simple(tmp_path):
"--struct",
DATA_PATH / "NaCl.cif",
"--bands",
+ "--n-qpoints",
+ 21,
"--no-write-full",
"--no-hdf5",
"--file-prefix",
@@ -108,9 +111,10 @@ def test_bands_simple(tmp_path):
assert result.exit_code == 0
assert autoband_results.exists()
- with open(autoband_results, encoding="utf8") as file:
+ with lzma.open(autoband_results, mode="rb") as file:
bands = yaml.safe_load(file)
assert "eigenvector" not in bands["phonon"][0]["band"][0]
+ assert bands["nqpoint"] == 126
# Read phonons summary file
assert summary_path.exists()
@@ -149,7 +153,7 @@ def test_hdf5(tmp_path):
def test_thermal_props(tmp_path):
"""Test calculating thermal properties."""
file_prefix = tmp_path / "test" / "NaCl"
- thermal_results = tmp_path / "test" / "NaCl-thermal.dat"
+ thermal_results = tmp_path / "test" / "NaCl-thermal.yml"
result = runner.invoke(
app,
@@ -179,6 +183,8 @@ def test_dos(tmp_path):
"--struct",
DATA_PATH / "NaCl.cif",
"--dos",
+ "--dos-kwargs",
+ "{'freq_min': -1, 'freq_max': 0}",
"--no-hdf5",
"--file-prefix",
file_prefix,
@@ -186,6 +192,9 @@ def test_dos(tmp_path):
)
assert result.exit_code == 0
assert dos_results.exists()
+ lines = dos_results.read_text().splitlines()
+ assert lines[1].split()[0] == "-1.0000000000"
+ assert lines[-1].split()[0] == "0.0000000000"
def test_pdos(tmp_path):
@@ -200,6 +209,8 @@ def test_pdos(tmp_path):
"--struct",
DATA_PATH / "NaCl.cif",
"--pdos",
+ "--pdos-kwargs",
+ "{'freq_min': -1, 'freq_max': 0, 'xyz_projection': True}",
"--no-hdf5",
"--file-prefix",
file_prefix,
@@ -207,6 +218,10 @@ def test_pdos(tmp_path):
)
assert result.exit_code == 0
assert pdos_results.exists()
+ with open(pdos_results, encoding="utf8") as file:
+ lines = file.readlines()
+ assert lines[1].split()[0] == "-1.0000000000"
+ assert lines[-1].split()[0] == "0.0000000000"
def test_plot(tmp_path):
@@ -214,13 +229,13 @@ def test_plot(tmp_path):
file_prefix = tmp_path / "NaCl"
pdos_results = tmp_path / "NaCl-pdos.dat"
dos_results = tmp_path / "NaCl-dos.dat"
- autoband_results = tmp_path / "NaCl-auto_bands.yml"
+ autoband_results = tmp_path / "NaCl-auto_bands.yml.xz"
summary_path = tmp_path / "NaCl-phonons-summary.yml"
svgs = [
tmp_path / "NaCl-dos.svg",
tmp_path / "NaCl-pdos.svg",
tmp_path / "NaCl-bs-dos.svg",
- tmp_path / "NaCl-auto_bands.svg",
+ tmp_path / "NaCl-bands.svg",
]
result = runner.invoke(
@@ -443,3 +458,83 @@ def test_no_carbon(tmp_path):
with open(summary_path, encoding="utf8") as file:
phonon_summary = yaml.safe_load(file)
assert "emissions" not in phonon_summary
+
+
+def test_displacement_kwargs(tmp_path):
+ """Test displacement_kwargs can be set."""
+ file_prefix_1 = tmp_path / "NaCl_1"
+ file_prefix_2 = tmp_path / "NaCl_2"
+ displacement_file_1 = tmp_path / "NaCl_1-phonopy.yml"
+ displacement_file_2 = tmp_path / "NaCl_2-phonopy.yml"
+
+ result = runner.invoke(
+ app,
+ [
+ "phonons",
+ "--struct",
+ DATA_PATH / "NaCl.cif",
+ "--no-hdf5",
+ "--displacement-kwargs",
+ "{'is_plusminus': True}",
+ "--file-prefix",
+ file_prefix_1,
+ ],
+ )
+ assert result.exit_code == 0
+
+ result = runner.invoke(
+ app,
+ [
+ "phonons",
+ "--struct",
+ DATA_PATH / "NaCl.cif",
+ "--no-hdf5",
+ "--displacement-kwargs",
+ "{'is_plusminus': False}",
+ "--file-prefix",
+ file_prefix_2,
+ ],
+ )
+ assert result.exit_code == 0
+
+ # Check parameters
+ with open(displacement_file_1, encoding="utf8") as file:
+ params = yaml.safe_load(file)
+ n_displacements_1 = len(params["displacements"])
+
+ assert n_displacements_1 == 4
+
+ with open(displacement_file_2, encoding="utf8") as file:
+ params = yaml.safe_load(file)
+ n_displacements_2 = len(params["displacements"])
+
+ assert n_displacements_2 == 2
+
+
+def test_paths(tmp_path):
+ """Test displacement_kwargs can be set."""
+ file_prefix = tmp_path / "NaCl"
+ qpoint_file = DATA_PATH / "paths.yml"
+ band_results = tmp_path / "NaCl-bands.yml.xz"
+
+ result = runner.invoke(
+ app,
+ [
+ "phonons",
+ "--struct",
+ DATA_PATH / "NaCl.cif",
+ "--no-hdf5",
+ "--bands",
+ "--qpoint-file",
+ qpoint_file,
+ "--file-prefix",
+ file_prefix,
+ ],
+ )
+ assert result.exit_code == 0
+
+ assert band_results.exists()
+ with lzma.open(band_results, mode="rb") as file:
+ bands = yaml.safe_load(file)
+ assert bands["nqpoint"] == 11
+ assert bands["npath"] == 1