-
Notifications
You must be signed in to change notification settings - Fork 124
Atomate2 jz pheasy_phonon #976
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
leslie-zheng
wants to merge
33
commits into
materialsproject:main
Choose a base branch
from
leslie-zheng:atomate2_jz_pheasy
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 5 commits
Commits
Show all changes
33 commits
Select commit
Hold shift + click to select a range
0775785
Add pheasy branch for phonon calculation using LASSO
leslie-zheng 7891554
pheasy_phonons
leslie-zheng d643fc1
add_pheasy_to_vasp_folder
leslie-zheng 93219ce
Add_pheasy_to_vasp_file
leslie-zheng f4b59c6
Merge branch 'main' into atomate2_jz_pheasy
JaGeo e514969
Merge branch 'main' into atomate2_jz_pheasy
JaGeo 11d546b
Modified some parts based on Janine's comments.
leslie-zheng 13acf5a
Merge branch 'atomate2_jz_pheasy' of https://github.com/leslie-zheng/…
leslie-zheng 57ab59f
directly import the class methods from phonons
leslie-zheng 647fe49
clean up the code
leslie-zheng 94a1acc
remove some files.
leslie-zheng b92e34e
minor change
leslie-zheng 05650d0
allow the users to define the number of displacements for random-disp…
leslie-zheng 0bf1bf8
format_adjustment
leslie-zheng e9cd8a6
format_adjustment
leslie-zheng 2bacc86
small update
leslie-zheng 4535497
clean up the code and add more comments
leslie-zheng eab1fe6
minor update
leslie-zheng 36aa5b7
allow the users to control the symmetry precision
leslie-zheng c69df93
Lower the symmetry precision to allow pheasy to find a correct space …
leslie-zheng 92ea25a
Minor update
leslie-zheng 5d6f122
minor adjustment
leslie-zheng e17a316
minor update for flow/pheasy.py
leslie-zheng f46c2fd
resuse some jobs from phonons
leslie-zheng 34a197e
clean up the job(generate_phonon_displacements)
leslie-zheng be89bce
finished cleaning up the pheasy jobs module
leslie-zheng 1316b1d
finished cleaning up the shemas/pheasy
leslie-zheng 833205a
small adjustment to pass the lint
leslie-zheng f741a8c
raise a error if ALM is not installed.
leslie-zheng 30de21a
minor update
leslie-zheng 69956d7
Merge branch 'main' into atomate2_jz_pheasy
leslie-zheng 5e2660a
minor update to pass the lint check
leslie-zheng cabc93a
added support for MLFFs
hrushikesh-s File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,374 @@ | ||
"""Flows for calculating phonons.""" | ||
|
||
from __future__ import annotations | ||
|
||
from abc import ABC, abstractmethod | ||
from dataclasses import dataclass, field | ||
from typing import TYPE_CHECKING | ||
|
||
from jobflow import Flow, Maker | ||
|
||
from atomate2.common.jobs.pheasy import ( | ||
generate_frequencies_eigenvectors, | ||
generate_phonon_displacements, | ||
get_supercell_size, | ||
get_total_energy_per_cell, | ||
run_phonon_displacements, | ||
) | ||
from atomate2.common.jobs.utils import structure_to_conventional, structure_to_primitive | ||
|
||
if TYPE_CHECKING: | ||
from pathlib import Path | ||
|
||
from emmet.core.math import Matrix3D | ||
from pymatgen.core.structure import Structure | ||
|
||
from atomate2.aims.jobs.base import BaseAimsMaker | ||
from atomate2.forcefields.jobs import ForceFieldRelaxMaker, ForceFieldStaticMaker | ||
from atomate2.vasp.jobs.base import BaseVaspMaker | ||
|
||
SUPPORTED_CODES = frozenset(("vasp", "aims", "forcefields")) | ||
|
||
|
||
@dataclass | ||
class BasePhononMaker(Maker, ABC): | ||
""" | ||
Maker to calculate harmonic phonons with a DFT/force field code and Phonopy. | ||
leslie-zheng marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
Calculate the harmonic phonons of a material. Initially, a tight structural | ||
relaxation is performed to obtain a structure without forces on the atoms. | ||
Subsequently, supercells with one displaced atom are generated and accurate | ||
forces are computed for these structures. With the help of phonopy, these | ||
forces are then converted into a dynamical matrix. To correct for polarization | ||
effects, a correction of the dynamical matrix based on BORN charges can | ||
be performed. Finally, phonon densities of states, phonon band structures | ||
and thermodynamic properties are computed. | ||
leslie-zheng marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
.. Note:: | ||
It is heavily recommended to symmetrize the structure before passing it to | ||
this flow. Otherwise, a different space group might be detected and too | ||
many displacement calculations will be generated. | ||
It is recommended to check the convergence parameters here and | ||
adjust them if necessary. The default might not be strict enough | ||
for your specific case. | ||
|
||
Parameters | ||
---------- | ||
name : str | ||
Name of the flows produced by this maker. | ||
sym_reduce : bool | ||
Whether to reduce the number of deformations using symmetry. | ||
symprec : float | ||
Symmetry precision to use in the | ||
reduction of symmetry to find the primitive/conventional cell | ||
(use_primitive_standard_structure, use_conventional_standard_structure) | ||
and to handle all symmetry-related tasks in phonopy | ||
displacement: float | ||
displacement distance for phonons | ||
min_length: float | ||
min length of the supercell that will be built | ||
prefer_90_degrees: bool | ||
if set to True, supercell algorithm will first try to find a supercell | ||
with 3 90 degree angles | ||
get_supercell_size_kwargs: dict | ||
kwargs that will be passed to get_supercell_size to determine supercell size | ||
use_symmetrized_structure: str | ||
allowed strings: "primitive", "conventional", None | ||
|
||
- "primitive" will enforce to start the phonon computation | ||
from the primitive standard structure | ||
according to Setyawan, W., & Curtarolo, S. (2010). | ||
High-throughput electronic band structure calculations: | ||
Challenges and tools. Computational Materials Science, | ||
49(2), 299-312. doi:10.1016/j.commatsci.2010.05.010. | ||
This makes it possible to use certain k-path definitions | ||
with this workflow. Otherwise, we must rely on seekpath | ||
- "conventional" will enforce to start the phonon computation | ||
from the conventional standard structure | ||
according to Setyawan, W., & Curtarolo, S. (2010). | ||
High-throughput electronic band structure calculations: | ||
Challenges and tools. Computational Materials Science, | ||
49(2), 299-312. doi:10.1016/j.commatsci.2010.05.010. | ||
We will however use seekpath and primitive structures | ||
as determined by from phonopy to compute the phonon band structure | ||
bulk_relax_maker: .ForceFieldRelaxMaker, .BaseAimsMaker, .BaseVaspMaker, or None | ||
A maker to perform a tight relaxation on the bulk. | ||
Set to ``None`` to skip the | ||
bulk relaxation | ||
static_energy_maker: .ForceFieldRelaxMaker, .BaseAimsMaker, .BaseVaspMaker, or None | ||
A maker to perform the computation of the DFT energy on the bulk. | ||
Set to ``None`` to skip the | ||
static energy computation | ||
born_maker: .ForceFieldStaticMaker, .BaseAsimsMaker, .BaseVaspMaker, or None | ||
Maker to compute the BORN charges. | ||
phonon_displacement_maker: .ForceFieldStaticMaker, .BaseAimsMaker, .BaseVaspMaker | ||
Maker used to compute the forces for a supercell. | ||
generate_frequencies_eigenvectors_kwargs : dict | ||
Keyword arguments passed to :obj:`generate_frequencies_eigenvectors`. | ||
create_thermal_displacements: bool | ||
Bool that determines if thermal_displacement_matrices are computed | ||
kpath_scheme: str | ||
scheme to generate kpoints. Please be aware that | ||
you can only use seekpath with any kind of cell | ||
Otherwise, please use the standard primitive structure | ||
Available schemes are: | ||
"seekpath", "hinuma", "setyawan_curtarolo", "latimer_munro". | ||
"seekpath" and "hinuma" are the same definition but | ||
seekpath can be used with any kind of unit cell as | ||
it relies on phonopy to handle the relationship | ||
to the primitive cell and not pymatgen | ||
code: str | ||
determines the dft or force field code. | ||
store_force_constants: bool | ||
if True, force constants will be stored | ||
socket: bool | ||
If True, use the socket for the calculation | ||
""" | ||
|
||
name: str = "phonon" | ||
sym_reduce: bool = True | ||
symprec: float = 1e-4 | ||
displacement: float = 0.01 | ||
min_length: float | None = 20.0 | ||
prefer_90_degrees: bool = True | ||
get_supercell_size_kwargs: dict = field(default_factory=dict) | ||
use_symmetrized_structure: str | None = None | ||
bulk_relax_maker: ForceFieldRelaxMaker | BaseVaspMaker | BaseAimsMaker | None = None | ||
static_energy_maker: ForceFieldRelaxMaker | BaseVaspMaker | BaseAimsMaker | None = ( | ||
None | ||
) | ||
born_maker: ForceFieldStaticMaker | BaseVaspMaker | None = None | ||
phonon_displacement_maker: ForceFieldStaticMaker | BaseVaspMaker | BaseAimsMaker = ( | ||
None | ||
) | ||
create_thermal_displacements: bool = False | ||
generate_frequencies_eigenvectors_kwargs: dict = field(default_factory=dict) | ||
kpath_scheme: str = "seekpath" | ||
code: str = None | ||
|
||
# add mpid to the phonon output | ||
mp_id: str = None | ||
store_force_constants: bool = True | ||
socket: bool = False | ||
|
||
def make( | ||
self, | ||
structure: Structure, | ||
prev_dir: str | Path | None = None, | ||
born: list[Matrix3D] | None = None, | ||
epsilon_static: Matrix3D | None = None, | ||
total_dft_energy_per_formula_unit: float | None = None, | ||
supercell_matrix: Matrix3D | None = None, | ||
) -> Flow: | ||
"""Make flow to calculate the phonon properties. | ||
|
||
Parameters | ||
---------- | ||
structure : Structure | ||
A pymatgen structure object. Please start with a structure | ||
that is nearly fully optimized as the internal optimizers | ||
have very strict settings! | ||
prev_dir : str or Path or None | ||
A previous calculation directory to use for copying outputs. | ||
born: Matrix3D | ||
Instead of recomputing born charges and epsilon, these values can also be | ||
provided manually. If born and epsilon_static are provided, the born run | ||
will be skipped it can be provided in the VASP convention with information | ||
for every atom in unit cell. Please be careful when converting structures | ||
within in this workflow as this could lead to errors | ||
epsilon_static: Matrix3D | ||
The high-frequency dielectric constant to use instead of recomputing born | ||
charges and epsilon. If born, epsilon_static are provided, the born run | ||
will be skipped | ||
total_dft_energy_per_formula_unit: float | ||
It has to be given per formula unit (as a result in corresponding Doc). | ||
Instead of recomputing the energy of the bulk structure every time, this | ||
value can also be provided in eV. If it is provided, the static run will be | ||
skipped. This energy is the typical output dft energy of the dft workflow. | ||
No conversion needed. | ||
supercell_matrix: list | ||
Instead of min_length, also a supercell_matrix can be given, e.g. | ||
[[1.0,0.0,0.0],[0.0,1.0,0.0],[0.0,0.0,1.0] | ||
""" | ||
use_symmetrized_structure = self.use_symmetrized_structure | ||
kpath_scheme = self.kpath_scheme | ||
valid_structs = (None, "primitive", "conventional") | ||
if use_symmetrized_structure not in valid_structs: | ||
raise ValueError( | ||
f"Invalid {use_symmetrized_structure=}, use one of {valid_structs}" | ||
) | ||
|
||
if use_symmetrized_structure != "primitive" and kpath_scheme != "seekpath": | ||
raise ValueError( | ||
f"You can't use {kpath_scheme=} with the primitive standard " | ||
"structure, please use seekpath" | ||
) | ||
|
||
valid_schemes = ("seekpath", "hinuma", "setyawan_curtarolo", "latimer_munro") | ||
if kpath_scheme not in valid_schemes: | ||
raise ValueError( | ||
f"{kpath_scheme=} is not implemented, use one of {valid_schemes}" | ||
) | ||
|
||
if self.code is None or self.code not in SUPPORTED_CODES: | ||
raise ValueError( | ||
"The code variable must be passed and it must be a supported code." | ||
f" Supported codes are: {SUPPORTED_CODES}" | ||
) | ||
|
||
jobs = [] | ||
|
||
# TODO: should this be after or before structural optimization as the | ||
# optimization could change the symmetry we could add a tutorial and point out | ||
# that the structure should be nearly optimized before the phonon workflow | ||
if self.use_symmetrized_structure == "primitive": | ||
# These structures are compatible with many | ||
# of the kpath algorithms that are used for Materials Project | ||
prim_job = structure_to_primitive(structure, self.symprec) | ||
jobs.append(prim_job) | ||
structure = prim_job.output | ||
elif self.use_symmetrized_structure == "conventional": | ||
# it could be beneficial to use conventional standard structures to arrive | ||
# faster at supercells with right angles | ||
conv_job = structure_to_conventional(structure, self.symprec) | ||
jobs.append(conv_job) | ||
structure = conv_job.output | ||
|
||
optimization_run_job_dir = None | ||
optimization_run_uuid = None | ||
|
||
if self.bulk_relax_maker is not None: | ||
# optionally relax the structure | ||
bulk_kwargs = {} | ||
if self.prev_calc_dir_argname is not None: | ||
bulk_kwargs[self.prev_calc_dir_argname] = prev_dir | ||
bulk = self.bulk_relax_maker.make(structure, **bulk_kwargs) | ||
jobs.append(bulk) | ||
structure = bulk.output.structure | ||
prev_dir = bulk.output.dir_name | ||
optimization_run_job_dir = bulk.output.dir_name | ||
optimization_run_uuid = bulk.output.uuid | ||
|
||
# if supercell_matrix is None, supercell size will be determined after relax | ||
# maker to ensure that cell lengths are really larger than threshold | ||
if supercell_matrix is None: | ||
supercell_job = get_supercell_size( | ||
structure, | ||
self.min_length, | ||
self.prefer_90_degrees, | ||
**self.get_supercell_size_kwargs, | ||
) | ||
jobs.append(supercell_job) | ||
supercell_matrix = supercell_job.output | ||
|
||
# Computation of static energy | ||
total_dft_energy = None | ||
static_run_job_dir = None | ||
static_run_uuid = None | ||
if (self.static_energy_maker is not None) and ( | ||
total_dft_energy_per_formula_unit is None | ||
): | ||
static_job_kwargs = {} | ||
if self.prev_calc_dir_argname is not None: | ||
static_job_kwargs[self.prev_calc_dir_argname] = prev_dir | ||
static_job = self.static_energy_maker.make( | ||
structure=structure, **static_job_kwargs | ||
) | ||
jobs.append(static_job) | ||
total_dft_energy = static_job.output.output.energy | ||
static_run_job_dir = static_job.output.dir_name | ||
static_run_uuid = static_job.output.uuid | ||
prev_dir = static_job.output.dir_name | ||
elif total_dft_energy_per_formula_unit is not None: | ||
# to make sure that one can reuse results from Doc | ||
compute_total_energy_job = get_total_energy_per_cell( | ||
total_dft_energy_per_formula_unit, structure | ||
) | ||
jobs.append(compute_total_energy_job) | ||
total_dft_energy = compute_total_energy_job.output | ||
|
||
# get a phonon object from phonopy | ||
displacements = generate_phonon_displacements( | ||
structure=structure, | ||
supercell_matrix=supercell_matrix, | ||
displacement=self.displacement, | ||
sym_reduce=self.sym_reduce, | ||
symprec=self.symprec, | ||
use_symmetrized_structure=self.use_symmetrized_structure, | ||
kpath_scheme=self.kpath_scheme, | ||
code=self.code, | ||
) | ||
leslie-zheng marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
jobs.append(displacements) | ||
|
||
# perform the phonon displacement calculations | ||
displacement_calcs = run_phonon_displacements( | ||
displacements=displacements.output, | ||
structure=structure, | ||
supercell_matrix=supercell_matrix, | ||
phonon_maker=self.phonon_displacement_maker, | ||
socket=self.socket, | ||
prev_dir_argname=self.prev_calc_dir_argname, | ||
prev_dir=prev_dir, | ||
) | ||
jobs.append(displacement_calcs) | ||
|
||
# Computation of BORN charges | ||
born_run_job_dir = None | ||
born_run_uuid = None | ||
if self.born_maker is not None and (born is None or epsilon_static is None): | ||
born_kwargs = {} | ||
if self.prev_calc_dir_argname is not None: | ||
born_kwargs[self.prev_calc_dir_argname] = prev_dir | ||
born_job = self.born_maker.make(structure, **born_kwargs) | ||
jobs.append(born_job) | ||
|
||
# I am not happy how we currently access "born" charges | ||
# This is very vasp specific code aims and forcefields | ||
# do not support this at the moment, if this changes we have | ||
# to update this section | ||
epsilon_static = born_job.output.calcs_reversed[0].output.epsilon_static | ||
born = born_job.output.calcs_reversed[0].output.outcar["born"] | ||
born_run_job_dir = born_job.output.dir_name | ||
born_run_uuid = born_job.output.uuid | ||
|
||
phonon_collect = generate_frequencies_eigenvectors( | ||
supercell_matrix=supercell_matrix, | ||
displacement=self.displacement, | ||
sym_reduce=self.sym_reduce, | ||
symprec=self.symprec, | ||
use_symmetrized_structure=self.use_symmetrized_structure, | ||
kpath_scheme=self.kpath_scheme, | ||
code=self.code, | ||
mp_id=self.mp_id, | ||
structure=structure, | ||
displacement_data=displacement_calcs.output, | ||
epsilon_static=epsilon_static, | ||
born=born, | ||
total_dft_energy=total_dft_energy, | ||
static_run_job_dir=static_run_job_dir, | ||
static_run_uuid=static_run_uuid, | ||
born_run_job_dir=born_run_job_dir, | ||
born_run_uuid=born_run_uuid, | ||
optimization_run_job_dir=optimization_run_job_dir, | ||
optimization_run_uuid=optimization_run_uuid, | ||
create_thermal_displacements=self.create_thermal_displacements, | ||
store_force_constants=self.store_force_constants, | ||
**self.generate_frequencies_eigenvectors_kwargs, | ||
) | ||
|
||
jobs.append(phonon_collect) | ||
|
||
# create a flow including all jobs for a phonon computation | ||
return Flow(jobs, phonon_collect.output) | ||
|
||
@property | ||
@abstractmethod | ||
def prev_calc_dir_argname(self) -> str | None: | ||
"""Name of argument informing static maker of previous calculation directory. | ||
|
||
As this differs between different DFT codes (e.g., VASP, CP2K), it | ||
has been left as a property to be implemented by the inheriting class. | ||
|
||
Note: this is only applicable if a relax_maker is specified; i.e., two | ||
calculations are performed for each ordering (relax -> static) | ||
""" |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.