Skip to content
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

[WIP] Implementation of quasi-harmonic approximation #891

Closed
wants to merge 19 commits into from
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
some rough draft of the commonmaker
JaGeo committed Jun 18, 2024
commit b02b9fdf5989e0508d3e33c9f18d1a5e2af3ba10
2 changes: 2 additions & 0 deletions src/atomate2/common/flows/eos.py
Original file line number Diff line number Diff line change
@@ -166,6 +166,8 @@ def make(self, structure: Structure, prev_dir: str | Path = None) -> Flow:
flow_output[key]["energy"] += [output.energy]
flow_output[key]["volume"] += [output.structure.volume]
flow_output[key]["stress"] += [output.stress]
# TODO: make this potentially optional?
flow_output[key]["structure"] += [output.structure]

if self.postprocessor is not None:
min_points = self.postprocessor.min_data_points
93 changes: 93 additions & 0 deletions src/atomate2/common/flows/qha.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
"""Define common EOS flow agnostic to electronic-structure code."""

from __future__ import annotations

import contextlib
from dataclasses import dataclass, field
from typing import TYPE_CHECKING

import numpy as np
from jobflow import Flow, Maker

from atomate2.common.jobs.eos import PostProcessEosEnergy, apply_strain_to_structure
from atomate2.common.flows.eos import CommonEosMaker
from atomate2.common.flows.phonons import BasePhononMaker


if TYPE_CHECKING:
from pathlib import Path

from jobflow import Job
from pymatgen.core import Structure

from atomate2.common.jobs.eos import EOSPostProcessor


@dataclass
class CommonQhaMaker(Maker):
"""
Perform quasi-harmonic approximation.

First relax a structure using relax_maker.
Then perform a series of deformations on the relaxed structure, and
then compute harmonic phonons for each deformed structure.
Finally, compute Gibb's free energy.

Parameters
----------
name : str
Name of the flows produced by this maker.
initial_relax_maker : .Maker | None
Maker to relax the input structure, defaults to None (no initial relaxation).
eos_relax_maker : .Maker
Maker to relax deformed structures for the EOS fit.
phonon_static_maker : .Maker | None
Maker to generate statics after each relaxation, defaults to None.
strain : tuple[float]
Percentage linear strain to apply as a deformation, default = -5% to 5%.
number_of_frames : int
Number of strain calculations to do for EOS fit, default = 6.
#postprocessor : .atomate2.common.jobs.EOSPostProcessor
# Optional postprocessing step, defaults to
# `atomate2.common.jobs.PostProcessEosEnergy`.
#_store_transformation_information : .bool = False
# Whether to store the information about transformations. Unfortunately
# needed at present to handle issues with emmet and pydantic validation
# TODO: remove this when clash is fixed
"""

name: str = "QHA Maker"
initial_relax_maker: Maker = None
eos_relax_maker: Maker = None
static_maker: Maker = None
phonon_maker: Maker = None
linear_strain: tuple[float, float] = (-0.05, 0.05)
number_of_frames: int = 6
#postprocessor: EOSPostProcessor = field(default_factory=PostProcessEosEnergy)
#_store_transformation_information: bool = False

def make(self, structure: Structure, prev_dir: str | Path = None) -> Flow:
"""Run an EOS flow.

Parameters
----------
structure : Structure
A pymatgen structure object.
prev_dir : str or Path or None
A previous calculation directory to copy output files from.

Returns
-------
.Flow, a QHA flow
"""
#In this way, one can easily exchange makers and enforce postprocessor None
self.eos=CommonEosMaker(initial_relax_maker=self.initial_relax_maker, eos_relax_maker=self.eos_relax_maker,
static_maker=self.static_maker, postprocessor=None, number_of_frames=self.number_of_frames)
eos_job=self.eos.make(structure)
phonon_jobs=[]
for frame_idx in range(self.number_of_frames):
phonon_jobs.append(self.phonon_maker.make(eos_job.output["relax"][frame_idx]["structure"]))

# Todo: reuse postprocessor from equation of state to make fits of free energy curves


46 changes: 46 additions & 0 deletions src/atomate2/forcefields/flows/qha.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from atomate2.common.flows.qha import CommonQhaMaker


class QhaMaker(CommonQhaMaker):
"""
Perform quasi-harmonic approximation.

First relax a structure using relax_maker.
Then perform a series of deformations on the relaxed structure, and
then compute harmonic phonons for each deformed structure.
Finally, compute Gibb's free energy.

Parameters
----------
name : str
Name of the flows produced by this maker.
initial_relax_maker : .Maker | None
Maker to relax the input structure, defaults to None (no initial relaxation).
eos_relax_maker : .Maker
Maker to relax deformed structures for the EOS fit.
phonon_static_maker : .Maker | None
Maker to generate statics after each relaxation, defaults to None.
strain : tuple[float]
Percentage linear strain to apply as a deformation, default = -5% to 5%.
number_of_frames : int
Number of strain calculations to do for EOS fit, default = 6.
#postprocessor : .atomate2.common.jobs.EOSPostProcessor
# Optional postprocessing step, defaults to
# `atomate2.common.jobs.PostProcessEosEnergy`.
#_store_transformation_information : .bool = False
# Whether to store the information about transformations. Unfortunately
# needed at present to handle issues with emmet and pydantic validation
# TODO: remove this when clash is fixed
"""

name: str = "CHGNet QHA Maker"
initial_relax_maker: Maker = field(default_factory=CHGNetRelaxMaker)
eos_relax_maker: Maker = field(default_factory=lambda: CHGNetRelaxMaker(relax_cell=False))
static_maker: Maker = None

initial_relax_maker: Maker = None
eos_relax_maker: Maker = None
static_maker: Maker = None
phonon_maker: Maker = None
linear_strain: tuple[float, float] = (-0.05, 0.05)
number_of_frames: int = 6 # postprocessor: EOSPostProcessor = field(default_factory=PostProcessEosEnergy) # _store_transformation_information: bool = False