# ruff: noqa: I002, FA100
"""Set up phonons commandline interface."""
# Issues with future annotations and typer
# c.f. https://github.com/maxb2/typer-config/issues/295
# from __future__ import annotations
from pathlib import Path
from typing import Annotated, Optional
from typer import Context, Option, Typer
from typer_config import use_config
from janus_core.cli.types import (
Architecture,
CalcKwargs,
Device,
DisplacementKwargs,
DoSKwargs,
LogPath,
MinimizeKwargs,
ModelPath,
PDoSKwargs,
ReadKwargsLast,
StructPath,
Summary,
)
from janus_core.cli.utils import yaml_converter_callback
app = Typer()
[docs]
@app.command()
@use_config(yaml_converter_callback)
def phonons(
# numpydoc ignore=PR02
ctx: Context,
struct: StructPath,
supercell: Annotated[
str,
Option(
help="Supercell matrix, in the Phonopy style. Must be passed as a string "
"in one of three forms: single integer ('2'), which specifies all "
"diagonal elements; three integers ('1 2 3'), which specifies each "
"individual diagonal element; or nine values ('1 2 3 4 5 6 7 8 9'), "
"which specifies all elements, filling the matrix row-wise."
),
] = "2 2 2",
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),
bands: Annotated[
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,
temp_min: Annotated[
float,
Option(help="Start temperature for thermal properties calculations, in K."),
] = 0.0,
temp_max: Annotated[
float,
Option(help="End temperature for thermal properties calculations, in K."),
] = 1000.0,
temp_step: Annotated[
float,
Option(help="Temperature step for thermal properties calculations, in K."),
] = 50,
symmetrize: Annotated[
bool, Option(help="Whether to symmetrize force constants.")
] = False,
minimize: Annotated[
bool, Option(help="Whether to minimize structure before calculations.")
] = False,
fmax: Annotated[
float, Option(help="Maximum force for optimization convergence.")
] = 0.1,
minimize_kwargs: MinimizeKwargs = None,
hdf5: Annotated[
bool, Option(help="Whether to save force constants in hdf5.")
] = True,
plot_to_file: Annotated[
bool,
Option(help="Whether to plot band structure and/or dos/pdos when calculated."),
] = False,
write_full: Annotated[
bool,
Option(
help=(
"Whether to write eigenvectors, group velocities, etc. to bands file."
),
),
] = True,
arch: Architecture = "mace_mp",
device: Device = "cpu",
model_path: ModelPath = None,
read_kwargs: ReadKwargsLast = None,
calc_kwargs: CalcKwargs = None,
file_prefix: Annotated[
Optional[Path],
Option(
help=(
"""
Prefix for output filenames. Default is inferred from structure name,
or chemical formula.
"""
),
),
] = None,
log: LogPath = None,
tracker: Annotated[
bool, Option(help="Whether to save carbon emissions of calculation")
] = True,
summary: Summary = None,
) -> None:
"""
Perform phonon calculations and write out results.
Parameters
----------
ctx : Context
Typer (Click) Context. Automatically set.
struct : Path
Path of structure to simulate.
supercell : str
Supercell matrix, in the Phonopy style. Must be passed as a string in one of
three forms: single integer ('2'), which specifies all diagonal elements;
three integers ('1 2 3'), which specifies each individual diagonal element;
or nine values ('1 2 3 4 5 6 7 8 9'), which specifies all elements, filling the
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
Start temperature for thermal calculations, in K. Unused if `thermal` is False.
Default is 0.0.
temp_max : float
End temperature for thermal calculations, in K. Unused if `thermal` is False.
Default is 1000.0.
temp_step : float
Temperature step for thermal calculations, in K. Unused if `thermal` is False.
Default is 50.0.
symmetrize : bool
Whether to symmetrize force constants. Default is False.
minimize : bool
Whether to minimize structure before calculations. Default is False.
fmax : float
Set force convergence criteria for optimizer in units eV/Å.
Default is 0.1.
minimize_kwargs : Optional[dict[str, Any]]
Other keyword arguments to pass to geometry optimizer. Default is {}.
hdf5 : bool
Whether to save force constants in hdf5 format. Default is True.
plot_to_file : bool
Whether to plot. Default is False.
write_full : bool
Whether to maximize information written in various output files.
Default is True.
arch : Optional[str]
MLIP architecture to use for geometry optimization.
Default is "mace_mp".
device : Optional[str]
Device to run model on. Default is "cpu".
model_path : Optional[str]
Path to MLIP model. Default is `None`.
read_kwargs : Optional[dict[str, Any]]
Keyword arguments to pass to ase.io.read. By default,
read_kwargs["index"] is 0.
calc_kwargs : Optional[dict[str, Any]]
Keyword arguments to pass to the selected calculator. Default is {}.
file_prefix : Optional[PathLike]
Prefix for output filenames. Default is inferred from structure name, or
chemical formula.
log : Optional[Path]
Path to write logs to. Default is inferred from the name of the structure file.
tracker : bool
Whether to save carbon emissions of calculation in log file and summary.
Default is True.
summary : Optional[Path]
Path to save summary of inputs, start/end time, and carbon emissions. Default
is inferred from the name of the structure file.
config : Optional[Path]
Path to yaml configuration file to define the above options. Default is None.
"""
from janus_core.calculations.phonons import Phonons
from janus_core.cli.utils import (
carbon_summary,
check_config,
dict_tuples_to_lists,
end_summary,
parse_typer_dicts,
save_struct_calc,
set_read_kwargs_index,
start_summary,
)
# Check options from configuration file are all valid
check_config(ctx)
(
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
set_read_kwargs_index(read_kwargs)
# Check fmax option not duplicated
if "fmax" in minimize_kwargs:
raise ValueError("'fmax' must be passed through the --fmax option")
minimize_kwargs["fmax"] = fmax
try:
supercell = [int(x) for x in supercell.split()]
except ValueError as exc:
raise ValueError(
"Please pass lattice vectors as integers in the form '1 2 3'"
) from exc
supercell_length = len(supercell)
if supercell_length == 1:
supercell = supercell[0]
elif supercell_length not in [3, 9]:
raise ValueError(
"Please pass lattice vectors as space-separated integers in quotes. "
"For example, '1 2 3'."
)
calcs = []
if bands:
calcs.append("bands")
if thermal:
calcs.append("thermal")
if dos:
calcs.append("dos")
if pdos:
calcs.append("pdos")
log_kwargs = {"filemode": "w"}
if log:
log_kwargs["filename"] = log
# Dictionary of inputs for Phonons class
phonons_kwargs = {
"struct_path": struct,
"arch": arch,
"device": device,
"model_path": model_path,
"read_kwargs": read_kwargs,
"calc_kwargs": calc_kwargs,
"attach_logger": True,
"log_kwargs": log_kwargs,
"track_carbon": tracker,
"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,
"force_consts_to_hdf5": hdf5,
"plot_to_file": plot_to_file,
"write_results": True,
"write_full": write_full,
"file_prefix": file_prefix,
"enable_progress_bar": True,
}
# Initialise phonons
phonon = Phonons(**phonons_kwargs)
# Set summary and log files
summary = phonon._build_filename("phonons-summary.yml", filename=summary).absolute()
log = phonon.log_kwargs["filename"]
# Store inputs for yaml summary
inputs = phonons_kwargs.copy()
# Add structure, MLIP information, and log to inputs
save_struct_calc(
inputs=inputs,
struct=phonon.struct,
struct_path=struct,
arch=arch,
device=device,
model_path=model_path,
read_kwargs=read_kwargs,
calc_kwargs=calc_kwargs,
log=log,
)
# Convert all tuples to list in inputs nested dictionary
dict_tuples_to_lists(inputs)
# Save summary information before calculations begin
start_summary(command="phonons", summary=summary, inputs=inputs)
# Run phonon calculations
phonon.run()
# Save carbon summary
if tracker:
carbon_summary(summary=summary, log=log)
# Time after calculations have finished
end_summary(summary)