from collections import OrderedDict
from typing import Any, Optional
import numpy as np
from ase.atoms import Atoms
from atomistics.shared.output import OutputEnergyVolumeCurve
from atomistics.workflows.evcurve.debye import (
OutputThermodynamic,
get_thermal_properties_for_energy_volume_curve,
)
from atomistics.workflows.evcurve.helper import (
analyse_results_for_energy_volume_curve,
get_tasks_for_energy_volume_curve,
)
from atomistics.workflows.interface import Workflow
[docs]
class EnergyVolumeCurveWorkflow(Workflow):
[docs]
def __init__(
self,
structure: Atoms,
num_points: int = 11,
fit_type: str = "polynomial",
fit_order: int = 3,
vol_range: float = 0.05,
axes: tuple[str, str, str] = ("x", "y", "z"),
strains: Optional[list[float]] = None,
):
"""
Initialize the EnergyVolumeCurveWorkflow object.
Args:
structure (Atoms): The atomic structure.
num_points (int, optional): The number of points in the energy-volume curve. Defaults to 11.
fit_type (str, optional): The type of fitting function. Defaults to "polynomial".
fit_order (int, optional): The order of the fitting function. Defaults to 3.
vol_range (float, optional): The range of volume variation. Defaults to 0.05.
axes (tuple[str, str, str], optional): The axes along which to vary the volume. Defaults to ("x", "y", "z").
strains (list, optional): The list of strains to apply. Defaults to None.
"""
self.structure = structure
self.num_points = num_points
self.fit_type = fit_type
self.vol_range = vol_range
self.fit_order = fit_order
self.axes = axes
self.strains = strains
self._task_dict: OrderedDict[str, Any] = OrderedDict()
self._fit_dict: dict[str, Any] = {}
@property
def fit_dict(self) -> dict[str, Any]:
"""
Get the fit dictionary.
Returns:
dict: The fit dictionary.
"""
return self._fit_dict
[docs]
def generate_structures(self) -> dict[str, Any]:
"""
Generate the structures for the energy-volume curve.
Returns:
dict: The generated structures.
"""
self._task_dict = OrderedDict(
get_tasks_for_energy_volume_curve(
structure=self.structure,
vol_range=self.vol_range,
num_points=self.num_points,
strain_lst=self.strains,
axes=self.axes,
)
)
return self._task_dict
[docs]
def analyse_structures(
self,
output_dict: dict[str, Any],
output_keys: tuple = OutputEnergyVolumeCurve.keys(),
) -> Any:
"""
Analyse the structures and fit the energy-volume curve.
Args:
output_dict (dict): The output dictionary.
output_keys (tuple, optional): The keys to include in the output. Defaults to OutputEnergyVolumeCurve.keys().
Returns:
dict: The fit dictionary.
"""
self._fit_dict = analyse_results_for_energy_volume_curve(
output_dict=output_dict,
task_dict=self._task_dict,
fit_type=self.fit_type,
fit_order=self.fit_order,
output_keys=output_keys,
)
return self.fit_dict
[docs]
def get_thermal_properties(
self,
t_min: float = 1.0,
t_max: float = 1500.0,
t_step: float = 50.0,
temperatures: Optional[np.ndarray] = None,
constant_volume: bool = False,
output_keys: tuple[str, ...] = OutputThermodynamic.keys(),
) -> dict[str, Any]:
"""
Get the thermal properties of the system.
Args:
t_min (float, optional): The minimum temperature. Defaults to 1.0.
t_max (float, optional): The maximum temperature. Defaults to 1500.0.
t_step (float, optional): The temperature step. Defaults to 50.0.
temperatures (np.ndarray, optional): The array of temperatures. Defaults to None.
constant_volume (bool, optional): Whether to calculate properties at constant volume. Defaults to False.
output_keys (tuple[str], optional): The keys to include in the output. Defaults to OutputThermodynamic.keys().
Returns:
dict: The thermal properties.
"""
return get_thermal_properties_for_energy_volume_curve(
fit_dict=self.fit_dict,
masses=self.structure.get_masses().tolist(),
t_min=t_min,
t_max=t_max,
t_step=t_step,
temperatures=temperatures,
constant_volume=constant_volume,
output_keys=output_keys,
)