"""Set up phonons commandline interface."""
from __future__ import annotations
from pathlib import Path
from typing import Annotated
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[
Path | None,
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[
Path | None,
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
Typer (Click) Context. Automatically set.
struct
Path of structure to simulate.
supercell
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
Displacement for force constants calculation, in A. Default is 0.01.
displacement_kwargs
Keyword arguments to pass to generate_displacements. Default is {}.
mesh
Mesh for sampling. Default is (10, 10, 10).
bands
Whether to calculate and save the band structure. Default is False.
n_qpoints
Number of q-points to sample along generated path, including end points.
Unused if `qpoint_file` is specified. Default is 51.
qpoint_file
Path to yaml file with info to generate a path of q-points for band structure.
Default is None.
dos
Whether to calculate and save the DOS. Default is False.
dos_kwargs
Other keyword arguments to pass to run_total_dos. Default is {}.
pdos
Whether to calculate and save the PDOS. Default is False.
pdos_kwargs
Other keyword arguments to pass to run_projected_dos. Default is {}.
thermal
Whether to calculate thermal properties. Default is False.
temp_min
Start temperature for thermal calculations, in K. Unused if `thermal` is False.
Default is 0.0.
temp_max
End temperature for thermal calculations, in K. Unused if `thermal` is False.
Default is 1000.0.
temp_step
Temperature step for thermal calculations, in K. Unused if `thermal` is False.
Default is 50.0.
symmetrize
Whether to symmetrize force constants. Default is False.
minimize
Whether to minimize structure before calculations. Default is False.
fmax
Set force convergence criteria for optimizer in units eV/Å.
Default is 0.1.
minimize_kwargs
Other keyword arguments to pass to geometry optimizer. Default is {}.
hdf5
Whether to save force constants in hdf5 format. Default is True.
plot_to_file
Whether to plot. Default is False.
write_full
Whether to maximize information written in various output files.
Default is True.
arch
MLIP architecture to use for geometry optimization.
Default is "mace_mp".
device
Device to run model on. Default is "cpu".
model_path
Path to MLIP model. Default is `None`.
read_kwargs
Keyword arguments to pass to ase.io.read. By default,
read_kwargs["index"] is 0.
calc_kwargs
Keyword arguments to pass to the selected calculator. Default is {}.
file_prefix
Prefix for output filenames. Default is inferred from structure name, or
chemical formula.
log
Path to write logs to. Default is inferred from the name of the structure file.
tracker
Whether to save carbon emissions of calculation in log file and summary.
Default is True.
summary
Path to save summary of inputs, start/end time, and carbon emissions. Default
is inferred from the name of the structure file.
config
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)