-
Notifications
You must be signed in to change notification settings - Fork 135
[WIP] Updated: Lattice dynamics workflow using hiPhive #1367
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
base: main
Are you sure you want to change the base?
Changes from 1 commit
c45b616
7250e2a
82ef1c0
8889d3e
6e9da67
c18fe93
60e9a7b
300da7b
4f7c264
5283d9e
2fb63c2
a249cb8
d5090fb
49ae442
8b0019d
8144b7b
5239a79
e06dd99
4e90106
228641a
4aca655
092baf5
edf646b
ed0458f
c9a9b62
522b593
675db1f
1132bbc
0e3f11f
848633a
bf6c01c
86fa57e
f788405
20ecb5d
7571fad
d17f1c3
a88147b
1a4ff45
8d80ab9
1096e99
027ef9b
bf9d481
5f87e84
26221c9
c2ee042
0cc0627
eb0a801
1a2cf21
623201a
9fbd3d1
f95073c
faefc1e
ee70772
f267351
fcd1f4d
a93b1ab
0cfbd60
697d1d8
c263f2a
a019641
81f5841
ed274ab
d2fe199
56185f6
5296606
dc4eaf0
d474d21
7a9e856
0c4da0a
f5498c5
c3150fa
5f6abad
d53beab
6d3754d
4b2d62f
11ac2fc
69f4ff5
d2e483e
36b7977
3db684d
55aceb8
7b986d6
7f474a3
25f931e
9706bd8
87d8c32
f34d369
13e731a
ff770f3
9770434
2b95bd0
738d825
3d5d4dc
1f638c6
a0d76b6
b1e5dd7
2f6f9f1
319cfc6
3991202
5515d1a
e957c61
4593415
5c9e168
b1f93de
4bcd97e
a41d31e
a0d2050
f1217b6
2790070
58aedd1
f92a7a4
7befb93
06b2a3a
c61dcf7
dbbfa40
af590ba
20ad5c3
bbebbd4
5f977f0
02ff2cc
0aeb863
a600755
9163c8a
7a790a1
355014b
c4712ca
7677939
147cd78
34e908b
13bdff1
66cec7a
32e3224
0ad7277
3b0fefe
88f7b1c
659c938
ec2a1ed
b7bd89b
181f72c
208cc43
5898ada
83b1d92
6580110
fa67259
35eb935
b7f20e8
48e61cb
1d0c9c7
2187f2d
7503383
a7ccf30
d90a7af
68c96a3
6fc2d95
843459d
ecb87fc
9972d90
015e073
52d5540
fcecc5b
2b595a9
c515565
714ef42
5c3f5d1
6fe2d43
7480c2f
ca54221
8174c69
25c89f2
7a49d82
dbd030d
049473b
bf8a116
1424fd5
1cdcaa6
6c44a3a
106399f
711de53
46b45b4
1481f04
0e8d8d1
e29de49
9354b4d
1a80eaa
0258cd9
4780de6
30e38e0
ff32a18
108100f
031e8b5
417eb1d
981d0f1
98da469
06276e2
ba75563
7bccd39
1a22235
ffbad05
f83acf2
f311df6
f6ac987
5ad7147
f1f73be
8a6cec9
04c96f9
3dce858
d4748a7
ee10c84
28e83c3
19b4c28
77ad2e9
a719dd0
332f06a
d715cef
8a3f1e4
4b5cb36
4bb7d0e
44eebd6
72fea4a
c776471
a3b0644
ccb4b36
5d9161e
bb42c85
e9aa668
b9c59fc
a07ca83
f7fa784
b5eac65
22872e5
b49c869
68eea2b
cd36b72
31a8d60
2396b42
a97158f
09228aa
60c5e18
fb53491
bb52e2b
4f28310
e32f960
7c95b6e
cc12b08
8f6b299
63614e2
f91d105
9ea3709
0b8c2f5
e9fe0dc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,7 +10,7 @@ | |
| import warnings | ||
| from itertools import product | ||
| from pathlib import Path | ||
| from typing import TYPE_CHECKING, Any, Optional, Union | ||
| from typing import TYPE_CHECKING, Any | ||
|
|
||
| import matplotlib.pyplot as plt | ||
| import numpy as np | ||
|
|
@@ -37,7 +37,6 @@ | |
| # Phonopy & Phono3py | ||
| from phonopy import Phonopy | ||
| from phonopy.file_IO import parse_FORCE_CONSTANTS | ||
| from phonopy.interface.hiphive_interface import phonopy_atoms_to_ase | ||
| from phonopy.phonon.band_structure import get_band_qpoints_and_path_connections | ||
| from phonopy.structure.symmetry import symmetrize_borns_and_epsilon | ||
| from phonopy.units import VaspToTHz | ||
|
|
@@ -70,9 +69,25 @@ | |
| if TYPE_CHECKING: | ||
| from ase.atoms import Atoms | ||
| from emmet.core.math import Matrix3D | ||
| from phonopy.structure.atoms import PhonopyAtoms | ||
| from pymatgen.core.structure import Structure | ||
|
|
||
|
|
||
| def phonopy_atoms_to_ase(atoms_phonopy: PhonopyAtoms) -> Atoms: | ||
| """Convert PhonopyAtoms to Atoms.""" | ||
| try: | ||
| from ase.atoms import Atoms | ||
| except ImportError as exc: | ||
| raise ModuleNotFoundError("ASE python module was not found.") from exc | ||
|
|
||
| return Atoms( | ||
| cell=atoms_phonopy.cell, | ||
| scaled_positions=atoms_phonopy.scaled_positions, | ||
| numbers=atoms_phonopy.numbers, | ||
| pbc=True, | ||
| ) | ||
|
|
||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
| ev2j = sp.constants.elementary_charge | ||
|
|
@@ -106,7 +121,7 @@ def get_factor(code: str) -> float: | |
| raise ValueError(f"Frequency conversion factor for code ({code}) not defined.") | ||
|
|
||
|
|
||
| def get_cutoffs(supercell_structure: Structure) -> list[list[float]]: | ||
| def get_cutoffs(supercell_structure: Structure) -> list[list[int]]: | ||
| """ | ||
| Determine the trial cutoffs based on a supercell structure. | ||
|
|
||
|
|
@@ -418,7 +433,6 @@ def filter_cutoffs( | |
|
|
||
| logger.info(f"Unique DOFs: {seen_dofs}") | ||
| logger.info(f"New cutoffs: {new_cutoffs}") | ||
|
|
||
| return new_cutoffs | ||
|
|
||
| cutoffs = filter_cutoffs(cutoffs, dofs_list) | ||
|
|
@@ -449,15 +463,15 @@ class ThermalDisplacementData(BaseModel): | |
| "cutoff frequency in THz to avoid numerical issues in the " | ||
| "computation of the thermal displacement parameters" | ||
| ) | ||
| thermal_displacement_matrix_cif: Optional[list[list[Matrix3D]]] = Field( | ||
| thermal_displacement_matrix_cif: list[list[Matrix3D]] | None = Field( | ||
| None, description="field including thermal displacement matrices in CIF format" | ||
| ) | ||
| thermal_displacement_matrix: Optional[list[list[Matrix3D]]] = Field( | ||
| thermal_displacement_matrix: list[list[Matrix3D]] | None = Field( | ||
| None, | ||
| description="field including thermal displacement matrices in Cartesian " | ||
| "coordinate system", | ||
| ) | ||
| temperatures_thermal_displacements: Optional[list[int]] = Field( | ||
| temperatures_thermal_displacements: list[int] | None = Field( | ||
| None, | ||
| description="temperatures at which the thermal displacement matrices" | ||
| "have been computed", | ||
|
|
@@ -467,14 +481,12 @@ class ThermalDisplacementData(BaseModel): | |
| class PhononUUIDs(BaseModel): | ||
| """Collection to save all uuids connected to the phonon run.""" | ||
|
|
||
| optimization_run_uuid: Optional[str] = Field( | ||
| None, description="optimization run uuid" | ||
| ) | ||
| displacements_uuids: Optional[list[str]] = Field( | ||
| optimization_run_uuid: str | None = Field(None, description="optimization run uuid") | ||
| displacements_uuids: list[str] | None = Field( | ||
| None, description="The uuids of the displacement jobs." | ||
| ) | ||
| static_run_uuid: Optional[str] = Field(None, description="static run uuid") | ||
| born_run_uuid: Optional[str] = Field(None, description="born run uuid") | ||
| static_run_uuid: str | None = Field(None, description="static run uuid") | ||
| born_run_uuid: str | None = Field(None, description="born run uuid") | ||
|
|
||
|
|
||
| class ForceConstants(MSONable): | ||
|
|
@@ -487,88 +499,88 @@ def __init__(self, force_constants: list[list[Matrix3D]]) -> None: | |
| class PhononJobDirs(BaseModel): | ||
| """Collection to save all job directories relevant for the phonon run.""" | ||
|
|
||
| displacements_job_dirs: Optional[list[Optional[str]]] = Field( | ||
| displacements_job_dirs: list[str | None] | None = Field( | ||
| None, description="The directories where the displacement jobs were run." | ||
| ) | ||
| static_run_job_dir: Optional[Optional[str]] = Field( | ||
| static_run_job_dir: str | None = Field( | ||
| None, description="Directory where static run was performed." | ||
| ) | ||
| born_run_job_dir: Optional[str] = Field( | ||
| born_run_job_dir: str | None = Field( | ||
| None, description="Directory where born run was performed." | ||
| ) | ||
| optimization_run_job_dir: Optional[str] = Field( | ||
| optimization_run_job_dir: str | None = Field( | ||
| None, description="Directory where optimization run was performed." | ||
| ) | ||
| taskdoc_run_job_dir: Optional[str] = Field( | ||
| taskdoc_run_job_dir: str | None = Field( | ||
| None, description="Directory where task doc was generated." | ||
| ) | ||
|
|
||
|
|
||
| class PhononBSDOSDoc(StructureMetadata, extra="allow"): # type: ignore[call-arg] | ||
| """Collection of all data produced by the phonon workflow.""" | ||
|
|
||
| structure: Optional[Structure] = Field( | ||
| structure: Structure | None = Field( | ||
| None, description="Structure of Materials Project." | ||
| ) | ||
|
|
||
| phonon_bandstructure: Optional[PhononBandStructureSymmLine] = Field( | ||
| phonon_bandstructure: PhononBandStructureSymmLine | None = Field( | ||
| None, | ||
| description="Phonon band structure object.", | ||
| ) | ||
|
|
||
| phonon_dos: Optional[PhononDos] = Field( | ||
| phonon_dos: PhononDos | None = Field( | ||
| None, | ||
| description="Phonon density of states object.", | ||
| ) | ||
|
|
||
| free_energies: Optional[list[float]] = Field( | ||
| free_energies: list[float] | None = Field( | ||
| None, | ||
| description="vibrational part of the free energies in J/mol per " | ||
| "formula unit for temperatures in temperature_list", | ||
| ) | ||
|
|
||
| heat_capacities: Optional[list[float]] = Field( | ||
| heat_capacities: list[float] | None = Field( | ||
| None, | ||
| description="heat capacities in J/K/mol per " | ||
| "formula unit for temperatures in temperature_list", | ||
| ) | ||
|
|
||
| internal_energies: Optional[list[float]] = Field( | ||
| internal_energies: list[float] | None = Field( | ||
| None, | ||
| description="internal energies in J/mol per " | ||
| "formula unit for temperatures in temperature_list", | ||
| ) | ||
| entropies: Optional[list[float]] = Field( | ||
| entropies: list[float] | None = Field( | ||
| None, | ||
| description="entropies in J/(K*mol) per formula unit" | ||
| "for temperatures in temperature_list ", | ||
| ) | ||
|
|
||
| temperatures: Optional[list[int]] = Field( | ||
| temperatures: list[int] | None = Field( | ||
| None, | ||
| description="temperatures at which the vibrational" | ||
| " part of the free energies" | ||
| " and other properties have been computed", | ||
| ) | ||
|
|
||
| total_dft_energy: Optional[float] = Field("total DFT energy per formula unit in eV") | ||
| total_dft_energy: float | None = Field("total DFT energy per formula unit in eV") | ||
|
|
||
| has_imaginary_modes: Optional[bool] = Field( | ||
| has_imaginary_modes: bool | None = Field( | ||
| None, description="if true, structure has imaginary modes" | ||
| ) | ||
|
|
||
| # needed, e.g. to compute Grueneisen parameter etc | ||
| force_constants: Optional[ForceConstants] = Field( | ||
| force_constants: ForceConstants | None = Field( | ||
| None, description="Force constants between every pair of atoms in the structure" | ||
| ) | ||
|
|
||
| born: Optional[list[Matrix3D]] = Field( | ||
| born: list[Matrix3D] | None = Field( | ||
| None, | ||
| description="born charges as computed from phonopy. Only for symmetrically " | ||
| "different atoms", | ||
| ) | ||
|
|
||
| epsilon_static: Optional[Matrix3D] = Field( | ||
| epsilon_static: Matrix3D | None = Field( | ||
| None, description="The high-frequency dielectric constant" | ||
| ) | ||
|
|
||
|
|
@@ -583,15 +595,15 @@ class PhononBSDOSDoc(StructureMetadata, extra="allow"): # type: ignore[call-arg | |
| "Field including settings for Phonopy" | ||
| ) | ||
|
|
||
| thermal_displacement_data: Optional[ThermalDisplacementData] = Field( | ||
| thermal_displacement_data: ThermalDisplacementData | None = Field( | ||
| "Includes all data of the computation of the thermal displacements" | ||
| ) | ||
|
|
||
| jobdirs: Optional[PhononJobDirs] = Field( | ||
| jobdirs: PhononJobDirs | None = Field( | ||
| "Field including all relevant job directories" | ||
| ) | ||
|
|
||
| uuids: Optional[PhononUUIDs] = Field("Field including all relevant uuids") | ||
| uuids: PhononUUIDs | None = Field("Field including all relevant uuids") | ||
|
|
||
| @classmethod | ||
| def from_forces_born( | ||
|
|
@@ -601,7 +613,7 @@ def from_forces_born( | |
| displacement: float, | ||
| sym_reduce: bool, | ||
| symprec: float, | ||
| use_symmetrized_structure: Union[str, None], | ||
| use_symmetrized_structure: str | None, | ||
| kpath_scheme: str, | ||
| code: str, | ||
| displacement_data: dict[str, list], | ||
|
|
@@ -657,7 +669,7 @@ def from_forces_born( | |
| cell = get_phonopy_structure(structure) | ||
|
|
||
| if use_symmetrized_structure == "primitive": | ||
| primitive_matrix: Union[np.ndarray, str] = np.eye(3) | ||
| primitive_matrix: np.ndarray | str = np.eye(3) | ||
| else: | ||
| primitive_matrix = "auto" | ||
|
|
||
|
|
@@ -708,7 +720,11 @@ def from_forces_born( | |
| epsilon = None | ||
|
|
||
| # Produces all force constants | ||
| phonon.produce_force_constants(forces=set_of_forces) | ||
| # For some there is a discrepancy with number of displacements and set_of_forces | ||
| # logger.info(phonon._dataset["first_atoms"]) | ||
| # logger.info(len(phonon.supercells_with_displacements)) | ||
| # logger.info(len(set_of_forces)) | ||
| phonon.produce_force_constants(forces=set_of_forces[:1]) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note to check this line
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @hrushikesh-s Somehow there is a problem of length here which is strange. I can choose to take another subset (of size 1) and the test pass. I'm surprised that set_of_forces does not have the correct length. But maybe it's just the test data that is wrong. |
||
| # add the def run_hiphive() here and get the 2nd order FCs out. | ||
| # then use JZ's way to add FC to the phonon object. | ||
|
|
||
|
|
@@ -1424,7 +1440,7 @@ def _run_cutoffs( | |
| fit_kwargs: dict[str, Any], | ||
| imaginary_tol: float = 0.025, # in THz | ||
| ) -> dict[str, Any]: | ||
| logger.info(f"Testing cutoffs {i+1} out of {n_cutoffs}: {cutoffs}") | ||
| logger.info(f"Testing cutoffs {i + 1} out of {n_cutoffs}: {cutoffs}") | ||
| logger.info(f"fit_method is {fit_method}") | ||
| supercell_atoms = AseAtomsAdaptor.get_atoms(supercell_structure) | ||
| supercell_atoms = structures[0] | ||
|
|
@@ -1477,7 +1493,6 @@ def _run_cutoffs( | |
|
|
||
| parent_phonopy = get_phonopy_structure(parent_structure) | ||
| phonopy = Phonopy(parent_phonopy, supercell_matrix=supercell_matrix) | ||
| phonopy.primitive.get_number_of_atoms() | ||
|
|
||
| # Ensure supercell_matrix is a numpy array | ||
| supercell_matrix = np.array(supercell_matrix) | ||
|
|
@@ -1490,8 +1505,7 @@ def _run_cutoffs( | |
| fcs = fcp.get_force_constants(supercell_atoms) | ||
| logger.info("Did you get the large Condition number error?") | ||
|
|
||
| phonopy.set_force_constants(fcs.get_fc_array(2)) | ||
| phonopy.set_mesh(mesh, is_eigenvectors=True, is_mesh_symmetry=True) | ||
| phonopy.force_constants = fcs.get_fc_array(2) | ||
| phonopy.run_mesh(mesh, with_eigenvectors=True, is_mesh_symmetry=True) | ||
| omega = phonopy.mesh.frequencies # THz | ||
| omega = np.sort(omega.flatten()) | ||
|
|
@@ -1646,10 +1660,10 @@ def get_structure_container( | |
| logger.info(f"Mean Forces: {mean_forces}") | ||
| # Calculate standard deviation of displacements | ||
| std_displacements = np.linalg.norm(displacements, axis=1).std() | ||
| logger.info(f"Standard deviation of displacements: " f"{std_displacements}") | ||
| logger.info(f"Standard deviation of displacements: {std_displacements}") | ||
| # Calculate standard deviation of forces | ||
| std_forces = np.linalg.norm(forces, axis=1).std() | ||
| logger.info(f"Standard deviation of forces: " f"{std_forces}") | ||
| logger.info(f"Standard deviation of forces: {std_forces}") | ||
| if not separate_fit: # fit all | ||
| sc.add_structure(structure) | ||
| # for harmonic fitting | ||
|
|
@@ -1727,12 +1741,14 @@ def harmonic_properties( | |
| mesh = [10, 10, 10] # TODO change this later | ||
| logger.info(f"Mesh: {mesh}") | ||
|
|
||
| phonopy.set_force_constants(fcs2) | ||
| phonopy.set_mesh(mesh, is_eigenvectors=True, is_mesh_symmetry=False) | ||
| phonopy.force_constants = fcs2 | ||
| phonopy.run_mesh(mesh, with_eigenvectors=True, is_mesh_symmetry=False) | ||
| phonopy.run_thermal_properties(temperatures=temperature) | ||
| logger.info("Thermal properties successfully run!") | ||
|
|
||
| _, free_energy, entropy, heat_capacity = phonopy.get_thermal_properties() | ||
| _, free_energy, entropy, heat_capacity = ( | ||
| phonopy.thermal_properties.thermal_properties | ||
| ) | ||
|
|
||
| ## Use the following lines to convert the units to eV/atom | ||
| # free_energy *= 1000/sp.constants.Avogadro/eV2J/natom # kJ/mol to eV/atom | ||
|
|
@@ -2069,7 +2085,7 @@ def gruneisen( | |
| # Convert heat_capacity to an array if it's a scalar | ||
| # heat_capacity = np.array([heat_capacity]) | ||
| logger.info(f"heat capacity = {heat_capacity}") | ||
| vol = phonopy.primitive.get_volume() | ||
| vol = phonopy.unitcell.volume | ||
|
|
||
| logger.info(f"grun_tot: {grun_tot}") | ||
| # logger.info(f"grun_tot shape: {grun_tot.shape}") | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@hrushikesh-s linter suggested to change that because we later round the cutoff. So I was actually wondering if the rounding was correct or not.