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

Simple output interface #190

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
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
84 changes: 31 additions & 53 deletions atomistics/calculators/ase.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,47 +25,6 @@
from atomistics.calculators.interface import TaskName


class ASEExecutor(object):
def __init__(self, ase_structure, ase_calculator):
self.structure = ase_structure
self.structure.calc = ase_calculator

def forces(self):
return self.structure.get_forces()

def energy(self):
return self.structure.get_potential_energy()

def energy_pot(self):
return self.structure.get_potential_energy()

def energy_tot(self):
return (
self.structure.get_potential_energy() + self.structure.get_kinetic_energy()
)

def stress(self):
return self.structure.get_stress(voigt=False)

def pressure(self):
return self.structure.get_stress(voigt=False)

def cell(self):
return self.structure.get_cell()

def positions(self):
return self.structure.get_positions()

def velocities(self):
return self.structure.get_velocities()

def temperature(self):
return self.structure.get_temperature()

def volume(self):
return self.structure.get_volume()


@as_task_dict_evaluator
def evaluate_with_ase(
structure: Atoms,
Expand Down Expand Up @@ -107,10 +66,17 @@ def calc_static_with_ase(
ase_calculator,
output_keys=OutputStatic.keys(),
):
ase_exe = ASEExecutor(ase_structure=structure, ase_calculator=ase_calculator)
return OutputStatic(**{k: getattr(ase_exe, k) for k in OutputStatic.keys()}).get(
output_keys=output_keys
)
structure.calc = ase_calculator
output_dict = {}
if "forces" in output_keys:
output_dict["forces"] = structure.get_forces()
if "energy" in output_keys:
output_dict["energy"] = structure.get_potential_energy()
if "stress" in output_keys:
output_dict["stress"] = structure.get_stress(voigt=False)
if "volume" in output_keys:
output_dict["volume"] = structure.get_volume()
return output_dict


def calc_molecular_dynamics_npt_with_ase(
Expand Down Expand Up @@ -244,12 +210,24 @@ def _calc_molecular_dynamics_with_ase(
cache = {q: [] for q in output_keys}
for i in range(int(run / thermo)):
dyn.run(thermo)
ase_instance = ASEExecutor(
ase_structure=structure, ase_calculator=ase_calculator
)
calc_dict = OutputMolecularDynamics(
**{k: getattr(ase_instance, k) for k in OutputMolecularDynamics.keys()}
).get(output_keys=output_keys)
for k, v in calc_dict.items():
cache[k].append(v)
if "positions" in output_keys:
cache["positions"].append(structure.get_positions())
if "cell" in output_keys:
cache["cell"].append(structure.get_cell())
if "forces" in output_keys:
cache["forces"].append(structure.get_forces())
if "temperature" in output_keys:
cache["temperature"].append(structure.get_temperature())
if "energy_pot" in output_keys:
cache["energy_pot"].append(structure.get_potential_energy())
if "energy_tot" in output_keys:
cache["energy_tot"].append(
structure.get_potential_energy() + structure.get_kinetic_energy()
)
if "pressure" in output_keys:
cache["pressure"].append(structure.get_stress(voigt=False))
if "velocities" in output_keys:
cache["velocities"].append(structure.get_velocities())
if "volume" in output_keys:
cache["volume"].append(structure.get_volume())
return {q: np.array(cache[q]) for q in output_keys}
22 changes: 14 additions & 8 deletions atomistics/calculators/lammps/calculator.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@
LAMMPS_MINIMIZE_VOLUME,
)
from atomistics.calculators.wrapper import as_task_dict_evaluator
from atomistics.shared.thermal_expansion import OutputThermalExpansion
from atomistics.shared.output import OutputStatic, OutputMolecularDynamics
from atomistics.shared.output import (
OutputThermalExpansion,
OutputStatic,
OutputMolecularDynamics,
)


if TYPE_CHECKING:
Expand Down Expand Up @@ -132,12 +135,15 @@ def calc_static_with_lammps(
lmp=lmp,
**kwargs,
)
result_dict = OutputStatic(
forces=lmp_instance.interactive_forces_getter,
energy=lmp_instance.interactive_energy_pot_getter,
stress=lmp_instance.interactive_pressures_getter,
volume=lmp_instance.interactive_volume_getter,
).get(output_keys=output_keys)
result_dict = {}
if "forces" in output_keys:
result_dict["forces"] = lmp_instance.interactive_forces_getter()
if "energy" in output_keys:
result_dict["energy"] = lmp_instance.interactive_energy_pot_getter()
if "stress" in output_keys:
result_dict["stress"] = lmp_instance.interactive_pressures_getter()
if "volume" in output_keys:
result_dict["volume"] = lmp_instance.interactive_volume_getter()
lammps_shutdown(lmp_instance=lmp_instance, close_instance=lmp is None)
return result_dict

Expand Down
31 changes: 20 additions & 11 deletions atomistics/calculators/lammps/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,26 @@ def lammps_calc_md_step(
):
run_str_rendered = Template(run_str).render(run=run)
lmp_instance.interactive_lib_command(run_str_rendered)
return OutputMolecularDynamics(
positions=lmp_instance.interactive_positions_getter,
cell=lmp_instance.interactive_cells_getter,
forces=lmp_instance.interactive_forces_getter,
temperature=lmp_instance.interactive_temperatures_getter,
energy_pot=lmp_instance.interactive_energy_pot_getter,
energy_tot=lmp_instance.interactive_energy_tot_getter,
pressure=lmp_instance.interactive_pressures_getter,
velocities=lmp_instance.interactive_velocities_getter,
volume=lmp_instance.interactive_volume_getter,
).get(output_keys=output_keys)
result_dict = {}
if "positions" in output_keys:
result_dict["positions"] = lmp_instance.interactive_positions_getter()
if "cell" in output_keys:
result_dict["cell"] = lmp_instance.interactive_cells_getter()
if "forces" in output_keys:
result_dict["forces"] = lmp_instance.interactive_forces_getter()
if "temperature" in output_keys:
result_dict["temperature"] = lmp_instance.interactive_temperatures_getter()
if "energy_pot" in output_keys:
result_dict["energy_pot"] = lmp_instance.interactive_energy_pot_getter()
if "energy_tot" in output_keys:
result_dict["energy_tot"] = lmp_instance.interactive_energy_tot_getter()
if "pressure" in output_keys:
result_dict["pressure"] = lmp_instance.interactive_pressures_getter()
if "velocities" in output_keys:
result_dict["velocities"] = lmp_instance.interactive_velocities_getter()
if "volume" in output_keys:
result_dict["volume"] = lmp_instance.interactive_volume_getter()
return result_dict


def lammps_calc_md(
Expand Down
15 changes: 11 additions & 4 deletions atomistics/calculators/qe.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,10 +207,17 @@ def calc_static_with_qe(
call_qe_via_ase_command(
calculation_name=calculation_name, working_directory=working_directory
)
parser = QEStaticParser(filename=output_file_name)
return OutputStatic(**{k: getattr(parser, k) for k in OutputStatic.keys()}).get(
output_keys=output_keys
)
parser = io.read_pw_scf(filename=output_file_name, use_alat=True)
result_dict = {}
if "forces" in output_keys:
result_dict["forces"] = parser.forces
if "energy" in output_keys:
result_dict["energy"] = parser.etot
if "stress" in output_keys:
result_dict["stress"] = parser.stress
if "volume" in output_keys:
result_dict["forces"] = parser.volume
return result_dict


@as_task_dict_evaluator
Expand Down
27 changes: 6 additions & 21 deletions atomistics/shared/thermal_expansion.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,7 @@
from atomistics.shared.output import OutputThermalExpansion


class ThermalExpansionProperties:
def __init__(self, temperatures_lst, volumes_lst):
self._temperatures_lst = temperatures_lst
self._volumes_lst = volumes_lst

def volumes(self):
return self._volumes_lst

def temperatures(self):
return self._temperatures_lst


def get_thermal_expansion_output(temperatures_lst, volumes_lst, output_keys):
thermal = ThermalExpansionProperties(
temperatures_lst=temperatures_lst, volumes_lst=volumes_lst
)
return OutputThermalExpansion(
**{k: getattr(thermal, k) for k in OutputThermalExpansion.keys()}
).get(output_keys=output_keys)
result_dict = {}
if "volumes" in output_keys:
result_dict["volumes"] = volumes_lst
if "temperatures" in output_keys:
result_dict["temperatures"] = temperatures_lst
return result_dict
4 changes: 1 addition & 3 deletions atomistics/workflows/elastic/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,4 @@ def analyse_structures(self, output_dict, output_keys=OutputElastic.keys()):
self._data["e0"] = ene0
self._data["A2"] = A2
elastic = ElasticProperties(elastic_matrix=elastic_matrix)
return OutputElastic(
**{k: getattr(elastic, k) for k in OutputElastic.keys()}
).get(output_keys=output_keys)
return {k: getattr(elastic, k)() for k in output_keys}
29 changes: 17 additions & 12 deletions atomistics/workflows/evcurve/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,19 +175,24 @@ def generate_structures(self):
def analyse_structures(
self, output_dict, output_keys=OutputEnergyVolumeCurve.keys()
):
evcurve = EnergyVolumeCurveProperties(
fit_module=fit_ev_curve_internal(
volume_lst=get_volume_lst(structure_dict=self._structure_dict),
energy_lst=get_energy_lst(
output_dict=output_dict, structure_dict=self._structure_dict
),
fit_type=self.fit_type,
fit_order=self.fit_order,
)
fit_module = fit_ev_curve_internal(
volume_lst=get_volume_lst(structure_dict=self._structure_dict),
energy_lst=get_energy_lst(
output_dict=output_dict, structure_dict=self._structure_dict
),
fit_type=self.fit_type,
fit_order=self.fit_order,
)
self._fit_dict = OutputEnergyVolumeCurve(
**{k: getattr(evcurve, k) for k in OutputEnergyVolumeCurve.keys()}
).get(output_keys=output_keys)
self._fit_dict = {}
for key in OutputEnergyVolumeCurve.keys():
if key == "fit_dict" and key in output_keys:
self._fit_dict[key] = {
k: fit_module.fit_dict[k]
for k in ["fit_type", "least_square_error", "poly_fit", "fit_order"]
if k in fit_module.fit_dict.keys()
}
elif key in output_keys:
self._fit_dict[key] = fit_module.fit_dict[key]
return self.fit_dict

def get_volume_lst(self):
Expand Down
45 changes: 14 additions & 31 deletions atomistics/workflows/phonons/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,30 +117,6 @@ def force_constants(self):
return self._force_constants


class PhonopyThermalProperties(object):
def __init__(self, phonopy_instance):
self._phonopy = phonopy_instance
self._thermal_properties = phonopy_instance.get_thermal_properties_dict()

def free_energy(self):
return self._thermal_properties["free_energy"] * kJ_mol_to_eV

def temperatures(self):
return self._thermal_properties["temperatures"]

def entropy(self):
return self._thermal_properties["entropy"]

def heat_capacity(self):
return self._thermal_properties["heat_capacity"]

def volumes(self):
return np.array(
[self._phonopy.unitcell.get_volume()]
* len(self._thermal_properties["temperatures"])
)


class PhonopyWorkflow(Workflow):
"""
Phonopy wrapper for the calculation of free energy in the framework of quasi harmonic approximation.
Expand Down Expand Up @@ -250,9 +226,7 @@ def analyse_structures(self, output_dict, output_keys=OutputPhonons.keys()):
use_tetrahedron_method=True,
npoints=101,
)
self._phonopy_dict = OutputPhonons(
**{k: getattr(phono, k) for k in OutputPhonons.keys()}
).get(output_keys=output_keys)
self._phonopy_dict = {k: getattr(phono, k)() for k in output_keys}
return self._phonopy_dict

def get_thermal_properties(
Expand Down Expand Up @@ -291,10 +265,19 @@ def get_thermal_properties(
band_indices=band_indices,
is_projection=is_projection,
)
phono = PhonopyThermalProperties(phonopy_instance=self.phonopy)
return OutputThermodynamic(
**{k: getattr(phono, k) for k in OutputThermodynamic.keys()}
).get(output_keys=output_keys)
thermal_properties = self.phonopy.get_thermal_properties_dict()
output_dict = {}
for key in OutputThermodynamic.keys():
if key == "free_energy" and key in output_keys:
output_dict[key] = thermal_properties[key] * kJ_mol_to_eV
elif key == "volumes" and key in output_keys:
output_dict["volumes"] = np.array(
[self.phonopy.unitcell.get_volume()]
* len(thermal_properties["temperatures"])
)
elif key in output_keys:
output_dict[key] = thermal_properties[key]
return output_dict

def get_dynamical_matrix(self, npoints=101):
"""
Expand Down
2 changes: 1 addition & 1 deletion atomistics/workflows/quasiharmonic.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import numpy as np

from atomistics.shared.output import OutputThermodynamic, OutputPhonons
from atomistics.shared.output import OutputThermodynamic
from atomistics.workflows.evcurve.workflow import (
EnergyVolumeCurveWorkflow,
fit_ev_curve,
Expand Down
Loading