From f5aeeef2b5dc72f2b641c868f668a4dab0e86a87 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Fri, 5 Jan 2024 15:06:20 +0100 Subject: [PATCH 01/31] Define the generic format in a separate file --- atomistics/calculators/ase.py | 13 +- atomistics/calculators/lammps/calculator.py | 18 +-- atomistics/calculators/lammps/helpers.py | 15 ++- atomistics/calculators/qe.py | 3 +- atomistics/shared/generic.py | 74 ++++++++++++ atomistics/shared/output.py | 126 +++++++------------- atomistics/workflows/elastic/workflow.py | 3 +- atomistics/workflows/evcurve/debye.py | 4 +- atomistics/workflows/evcurve/workflow.py | 13 +- atomistics/workflows/phonons/workflow.py | 8 +- atomistics/workflows/quasiharmonic.py | 11 +- 11 files changed, 176 insertions(+), 112 deletions(-) create mode 100644 atomistics/shared/generic.py diff --git a/atomistics/calculators/ase.py b/atomistics/calculators/ase.py index 7a99e88b..9b41f954 100644 --- a/atomistics/calculators/ase.py +++ b/atomistics/calculators/ase.py @@ -10,6 +10,11 @@ from atomistics.calculators.interface import get_quantities_from_tasks from atomistics.calculators.wrapper import as_task_dict_evaluator +from atomistics.shared.generic import ( + static_calculation_output_keys, + molecular_dynamics_output_keys, + thermal_expansion_output_keys, +) from atomistics.shared.output import OutputStatic, OutputMolecularDynamics from atomistics.shared.thermal_expansion import ( OutputThermalExpansionProperties, @@ -118,7 +123,7 @@ def evaluate_with_ase( def calc_static_with_ase( structure, ase_calculator, - output=OutputStatic.fields(), + output=static_calculation_output_keys, ): return ASEOutputStatic.get( ASEExecutor(ase_structure=structure, ase_calculator=ase_calculator), *output @@ -152,7 +157,7 @@ def calc_molecular_dynamics_npt_with_ase( pfactor=2e6 * units.GPa * (units.fs**2), temperature=100, externalstress=np.array([0.0, 0.0, 0.0, 0.0, 0.0, 0.0]) * units.bar, - output=ASEOutputMolecularDynamics.fields(), + output=molecular_dynamics_output_keys, ): return _calc_md_step_with_ase( dyn=NPT( @@ -186,7 +191,7 @@ def calc_molecular_dynamics_langevin_with_ase( timestep=1 * units.fs, temperature=100, friction=0.002, - output=ASEOutputMolecularDynamics.fields(), + output=molecular_dynamics_output_keys, ): return _calc_md_step_with_ase( dyn=Langevin( @@ -236,7 +241,7 @@ def calc_molecular_dynamics_thermal_expansion_with_ase( ttime=100 * units.fs, pfactor=2e6 * units.GPa * (units.fs**2), externalstress=np.array([0.0, 0.0, 0.0, 0.0, 0.0, 0.0]) * units.bar, - output=OutputThermalExpansionProperties.fields(), + output=thermal_expansion_output_keys, ): structure_current = structure.copy() temperature_lst = np.arange( diff --git a/atomistics/calculators/lammps/calculator.py b/atomistics/calculators/lammps/calculator.py index 75076e0c..e81454b3 100644 --- a/atomistics/calculators/lammps/calculator.py +++ b/atomistics/calculators/lammps/calculator.py @@ -32,7 +32,11 @@ LammpsOutputStatic, ) from atomistics.calculators.wrapper import as_task_dict_evaluator -from atomistics.shared.thermal_expansion import OutputThermalExpansionProperties +from atomistics.shared.generic import ( + static_calculation_output_keys, + molecular_dynamics_output_keys, + thermal_expansion_output_keys, +) if TYPE_CHECKING: from ase import Atoms @@ -120,7 +124,7 @@ def calc_static_with_lammps( structure, potential_dataframe, lmp=None, - output=LammpsOutputStatic.fields(), + output=static_calculation_output_keys, **kwargs, ): template_str = LAMMPS_THERMO_STYLE + "\n" + LAMMPS_THERMO + "\n" + LAMMPS_RUN @@ -151,7 +155,7 @@ def calc_molecular_dynamics_nvt_with_lammps( seed=4928459, dist="gaussian", lmp=None, - output=LammpsOutputMolecularDynamics.fields(), + output=molecular_dynamics_output_keys, **kwargs, ): init_str = ( @@ -208,7 +212,7 @@ def calc_molecular_dynamics_npt_with_lammps( seed=4928459, dist="gaussian", lmp=None, - output=LammpsOutputMolecularDynamics.fields(), + output=molecular_dynamics_output_keys, **kwargs, ): init_str = ( @@ -266,7 +270,7 @@ def calc_molecular_dynamics_nph_with_lammps( seed=4928459, dist="gaussian", lmp=None, - output=LammpsOutputMolecularDynamics.fields(), + output=molecular_dynamics_output_keys, **kwargs, ): init_str = ( @@ -320,7 +324,7 @@ def calc_molecular_dynamics_langevin_with_lammps( seed=4928459, dist="gaussian", lmp=None, - output=LammpsOutputMolecularDynamics.fields(), + output=molecular_dynamics_output_keys, **kwargs, ): init_str = ( @@ -380,7 +384,7 @@ def calc_molecular_dynamics_thermal_expansion_with_lammps( seed=4928459, dist="gaussian", lmp=None, - output=OutputThermalExpansionProperties.fields(), + output=thermal_expansion_output_keys, **kwargs, ): init_str = ( diff --git a/atomistics/calculators/lammps/helpers.py b/atomistics/calculators/lammps/helpers.py index f4c950a3..e364d412 100644 --- a/atomistics/calculators/lammps/helpers.py +++ b/atomistics/calculators/lammps/helpers.py @@ -10,6 +10,15 @@ OutputThermalExpansionProperties, ThermalExpansionProperties, ) +from atomistics.shared.generic import ( + static_calculation_output_keys, + molecular_dynamics_output_keys, + thermal_expansion_output_keys, + thermodynamic_output_keys, + energy_volume_curve_output_keys, + elastic_matrix_output_keys, + phonon_output_keys, +) from atomistics.shared.tqdm_iterator import get_tqdm_iterator @@ -46,7 +55,7 @@ def lammps_calc_md_step( lmp_instance, run_str, run, - output=LammpsOutputMolecularDynamics.fields(), + output=molecular_dynamics_output_keys, ): run_str_rendered = Template(run_str).render(run=run) lmp_instance.interactive_lib_command(run_str_rendered) @@ -58,7 +67,7 @@ def lammps_calc_md( run_str, run, thermo, - output=LammpsOutputMolecularDynamics.fields(), + output=molecular_dynamics_output_keys, ): results_lst = [ lammps_calc_md_step( @@ -88,7 +97,7 @@ def lammps_thermal_expansion_loop( seed=4928459, dist="gaussian", lmp=None, - output=OutputThermalExpansionProperties.fields(), + output=thermal_expansion_output_keys, **kwargs, ): lmp_instance = lammps_run( diff --git a/atomistics/calculators/qe.py b/atomistics/calculators/qe.py index a6eb261d..dee47cfd 100644 --- a/atomistics/calculators/qe.py +++ b/atomistics/calculators/qe.py @@ -5,6 +5,7 @@ from pwtools import io from atomistics.calculators.interface import get_quantities_from_tasks +from atomistics.shared.generic import static_calculation_output_keys from atomistics.shared.output import OutputStatic from atomistics.calculators.wrapper import as_task_dict_evaluator @@ -189,7 +190,7 @@ def calc_static_with_qe( pseudopotentials=None, tstress=True, tprnfor=True, - output=OutputStatic.fields(), + output=static_calculation_output_keys, **kwargs, ): input_file_name = os.path.join(working_directory, calculation_name + ".pwi") diff --git a/atomistics/shared/generic.py b/atomistics/shared/generic.py new file mode 100644 index 00000000..260614c5 --- /dev/null +++ b/atomistics/shared/generic.py @@ -0,0 +1,74 @@ +static_calculation_output_keys = ( + "forces", # np.ndarray (n, 3) [eV / Ang^2] + "energy", # float [eV] + "stress", # np.ndarray (3, 3) [GPa] + "volume", # float [Ang^3] +) + + +molecular_dynamics_output_keys = ( + "positions", # np.ndarray (t, n, 3) [Ang] + "cell", # np.ndarray (t, 3, 3) [Ang] + "forces", # np.ndarray (t, n, 3) [eV / Ang^2] + "temperature", # np.ndarray (t) [K] + "energy_pot", # np.ndarray (t) [eV] + "energy_tot", # np.ndarray (t) [eV] + "pressure", # np.ndarray (t, 3, 3) [GPa] + "velocities", # np.ndarray (t, n, 3) [eV / Ang] + "volume", # np.ndarray (t) [Ang^3] +) + + +thermal_expansion_output_keys = ( + "temperatures", # np.ndarray (T) [K] + "volumes", # np.ndarray (T) [Ang^3] +) + + +thermodynamic_output_keys = ( + "temperatures", # np.ndarray (T) [K] + "volumes", # np.ndarray (T) [Ang^3] + "free_energy", # np.ndarray (T) [eV] + "entropy", # np.ndarray (T) [eV] + "heat_capacity", # np.ndarray (T) [eV] +) + + +energy_volume_curve_output_keys = ( + "energy_eq", # float [eV] + "volume_eq", # float [Ang^3] + "bulkmodul_eq", # float [GPa] + "b_prime_eq", # float + "fit_dict", # dict + "energy", # np.ndarray (V) [eV] + "volume", # np.ndarray (V) [Ang^3] +) + + +elastic_matrix_output_keys = ( + "elastic_matrix", # np.ndarray (6,6) [GPa] + "elastic_matrix_inverse", # np.ndarray (6,6) [GPa] + "bulkmodul_voigt", # float [GPa] + "bulkmodul_reuss", # float [GPa] + "bulkmodul_hill", # float [GPa] + "shearmodul_voigt", # float [GPa] + "shearmodul_reuss", # float [GPa] + "shearmodul_hill", # float [GPa] + "youngsmodul_voigt", # float [GPa] + "youngsmodul_reuss", # float [GPa] + "youngsmodul_hill", # float [GPa] + "poissonsratio_voigt", # float + "poissonsratio_reuss", # float + "poissonsratio_hill", # float + "AVR", # float + "elastic_matrix_eigval", # np.ndarray (6,6) [GPa] +) + + +phonon_output_keys = ( + "mesh_dict", # dict + "band_structure_dict", # dict + "total_dos_dict", # dict + "dynamical_matrix", # dict + "force_constants", # dict +) diff --git a/atomistics/shared/output.py b/atomistics/shared/output.py index f391bba8..66fcc954 100644 --- a/atomistics/shared/output.py +++ b/atomistics/shared/output.py @@ -1,5 +1,15 @@ import dataclasses +from atomistics.shared.generic import ( + static_calculation_output_keys, + molecular_dynamics_output_keys, + thermal_expansion_output_keys, + thermodynamic_output_keys, + energy_volume_curve_output_keys, + elastic_matrix_output_keys, + phonon_output_keys, +) + @dataclasses.dataclass class Output: @@ -11,96 +21,50 @@ def get(self, engine, *output: str) -> dict: return {q: getattr(self, q)(engine) for q in output} -@dataclasses.dataclass -class OutputStatic(Output): - forces: callable - energy: callable - stress: callable - volume: callable - - -@dataclasses.dataclass -class OutputMolecularDynamics(Output): - positions: callable - cell: callable - forces: callable - temperature: callable - energy_pot: callable - energy_tot: callable - pressure: callable - velocities: callable - volume: callable - - -@dataclasses.dataclass -class OutputThermalExpansion(Output): - temperatures: callable - volumes: callable - - -@dataclasses.dataclass -class OutputThermodynamic(OutputThermalExpansion): - free_energy: callable - entropy: callable - heat_capacity: callable - +OutputStatic = dataclasses.make_dataclass( + cls_name="OutputStatic", + fields=[(key, callable) for key in static_calculation_output_keys], + bases=(Output, ) +) -@dataclasses.dataclass -class EquilibriumEnergy(Output): - energy_eq: callable - -@dataclasses.dataclass -class EquilibriumVolume(Output): - volume_eq: callable +OutputMolecularDynamics = dataclasses.make_dataclass( + cls_name="OutputMolecularDynamics", + fields=[(key, callable) for key in molecular_dynamics_output_keys], + bases=(Output, ) +) -@dataclasses.dataclass -class EquilibriumBulkModul(Output): - bulkmodul_eq: callable +OutputThermalExpansion = dataclasses.make_dataclass( + cls_name="OutputThermalExpansion", + fields=[(key, callable) for key in thermal_expansion_output_keys], + bases=(Output, ) +) -@dataclasses.dataclass -class EquilibriumBulkModulDerivative(Output): - b_prime_eq: callable +OutputThermodynamic = dataclasses.make_dataclass( + cls_name="OutputThermodynamic", + fields=[(key, callable) for key in thermodynamic_output_keys], + bases=(Output, ) +) -@dataclasses.dataclass -class OutputEnergyVolumeCurve( - EquilibriumEnergy, - EquilibriumVolume, - EquilibriumBulkModul, - EquilibriumBulkModulDerivative, -): - fit_dict: callable - energy: callable - volume: callable +OutputEnergyVolumeCurve = dataclasses.make_dataclass( + cls_name="OutputEnergyVolumeCurve", + fields=[(key, callable) for key in energy_volume_curve_output_keys], + bases=(Output, ) +) -@dataclasses.dataclass -class OutputElastic(Output): - elastic_matrix: callable - elastic_matrix_inverse: callable - bulkmodul_voigt: callable - bulkmodul_reuss: callable - bulkmodul_hill: callable - shearmodul_voigt: callable - shearmodul_reuss: callable - shearmodul_hill: callable - youngsmodul_voigt: callable - youngsmodul_reuss: callable - youngsmodul_hill: callable - poissonsratio_voigt: callable - poissonsratio_reuss: callable - poissonsratio_hill: callable - AVR: callable - elastic_matrix_eigval: callable +OutputElastic = dataclasses.make_dataclass( + cls_name="OutputElastic", + fields=[(key, callable) for key in elastic_matrix_output_keys], + bases=(Output, ) +) -@dataclasses.dataclass -class OutputPhonons(Output): - mesh_dict: callable - band_structure_dict: callable - total_dos_dict: callable - dynamical_matrix: callable - force_constants: callable +OutputPhonons = dataclasses.make_dataclass( + cls_name="OutputPhonons", + fields=[(key, callable) for key in phonon_output_keys], + bases=(Output, ) +) diff --git a/atomistics/workflows/elastic/workflow.py b/atomistics/workflows/elastic/workflow.py index a2834ce2..85d0544d 100644 --- a/atomistics/workflows/elastic/workflow.py +++ b/atomistics/workflows/elastic/workflow.py @@ -1,6 +1,7 @@ import numpy as np from atomistics.shared.output import OutputElastic +from atomistics.shared.generic import elastic_matrix_output_keys from atomistics.workflows.interface import Workflow from atomistics.workflows.elastic.elastic_moduli import ElasticProperties from atomistics.workflows.elastic.helper import ( @@ -44,7 +45,7 @@ def generate_structures(self): ) return {"calc_energy": self._structure_dict} - def analyse_structures(self, output_dict, output=OutputElastic.fields()): + def analyse_structures(self, output_dict, output=elastic_matrix_output_keys): """ Args: diff --git a/atomistics/workflows/evcurve/debye.py b/atomistics/workflows/evcurve/debye.py index 8218dc7b..0d313770 100644 --- a/atomistics/workflows/evcurve/debye.py +++ b/atomistics/workflows/evcurve/debye.py @@ -2,7 +2,7 @@ import scipy.constants import scipy.optimize -from atomistics.shared.output import OutputThermodynamic +from atomistics.shared.generic import thermodynamic_output_keys from atomistics.workflows.evcurve.fit import interpolate_energy from atomistics.workflows.evcurve.thermo import get_thermo_bulk_model @@ -236,7 +236,7 @@ def get_thermal_properties( temperatures=None, constant_volume=False, num_steps=50, - output=OutputThermodynamic.fields(), + output=thermodynamic_output_keys, ): return DebyeOutputThermodynamic.get( DebyeThermalProperties( diff --git a/atomistics/workflows/evcurve/workflow.py b/atomistics/workflows/evcurve/workflow.py index 35ae70fc..32ea8d49 100644 --- a/atomistics/workflows/evcurve/workflow.py +++ b/atomistics/workflows/evcurve/workflow.py @@ -2,13 +2,14 @@ from ase.atoms import Atoms from collections import OrderedDict +from atomistics.shared.generic import ( + thermodynamic_output_keys, + energy_volume_curve_output_keys, +) from atomistics.shared.output import OutputEnergyVolumeCurve from atomistics.workflows.evcurve.fit import EnergyVolumeFit from atomistics.workflows.interface import Workflow -from atomistics.workflows.evcurve.debye import ( - get_thermal_properties, - OutputThermodynamic, -) +from atomistics.workflows.evcurve.debye import get_thermal_properties def _strain_axes( @@ -183,7 +184,7 @@ def generate_structures(self): self._structure_dict[1 + np.round(strain, 7)] = basis return {"calc_energy": self._structure_dict} - def analyse_structures(self, output_dict, output=OutputEnergyVolumeCurve.fields()): + def analyse_structures(self, output_dict, output=energy_volume_curve_output_keys): self._fit_dict = EnergyVolumeCurveOutputEnergyVolumeCurve.get( EnergyVolumeCurveProperties( fit_module=fit_ev_curve_internal( @@ -228,7 +229,7 @@ def get_thermal_properties( t_step=50, temperatures=None, constant_volume=False, - output=OutputThermodynamic.fields(), + output=thermodynamic_output_keys, ): return get_thermal_properties( fit_dict=self.fit_dict, diff --git a/atomistics/workflows/phonons/workflow.py b/atomistics/workflows/phonons/workflow.py index 1701ed7c..afec1dff 100644 --- a/atomistics/workflows/phonons/workflow.py +++ b/atomistics/workflows/phonons/workflow.py @@ -6,6 +6,10 @@ from phonopy.file_IO import write_FORCE_CONSTANTS import structuretoolkit +from atomistics.shared.generic import ( + thermodynamic_output_keys, + phonon_output_keys, +) from atomistics.shared.output import OutputThermodynamic, OutputPhonons from atomistics.workflows.interface import Workflow from atomistics.workflows.phonons.helper import ( @@ -239,7 +243,7 @@ def _restore_magmoms(self, structure): structure.set_initial_magnetic_moments(magmoms) return structure - def analyse_structures(self, output_dict, output=OutputPhonons.fields()): + def analyse_structures(self, output_dict, output=phonon_output_keys): """ Returns: @@ -281,7 +285,7 @@ def get_thermal_properties( pretend_real=False, band_indices=None, is_projection=False, - output=OutputThermodynamic.fields(), + output=thermodynamic_output_keys, ): """ Returns thermal properties at constant volume in the given temperature range. Can only be called after job diff --git a/atomistics/workflows/quasiharmonic.py b/atomistics/workflows/quasiharmonic.py index 68ac3238..ea57da4b 100644 --- a/atomistics/workflows/quasiharmonic.py +++ b/atomistics/workflows/quasiharmonic.py @@ -1,6 +1,7 @@ import numpy as np -from atomistics.shared.output import OutputThermodynamic, OutputPhonons +from atomistics.shared.generic import thermodynamic_output_keys +from atomistics.shared.output import OutputThermodynamic from atomistics.workflows.evcurve.workflow import ( EnergyVolumeCurveWorkflow, fit_ev_curve, @@ -36,7 +37,7 @@ def get_thermal_properties( band_indices=None, is_projection=False, quantum_mechanical=True, - output=OutputThermodynamic.fields(), + output=thermodynamic_output_keys, ): """ Returns thermal properties at constant volume in the given temperature range. Can only be called after job @@ -135,7 +136,7 @@ def _get_thermal_properties_quantum_mechanical( pretend_real=False, band_indices=None, is_projection=False, - output=OutputThermodynamic.fields(), + output=thermodynamic_output_keys, ): """ Returns thermal properties at constant volume in the given temperature range. Can only be called after job @@ -380,7 +381,7 @@ def get_thermal_properties( band_indices=None, is_projection=False, quantum_mechanical=True, - output=OutputThermodynamic.fields(), + output=thermodynamic_output_keys, ): """ Returns thermal properties at constant volume in the given temperature range. Can only be called after job @@ -416,7 +417,7 @@ def get_thermal_properties( band_indices=band_indices, is_projection=is_projection, quantum_mechanical=quantum_mechanical, - output=OutputThermodynamic.fields(), + output=thermodynamic_output_keys, ) def get_thermal_expansion( From c4f5197c9ebdbdda4e76138c871da03aa85d0020 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Fri, 5 Jan 2024 15:14:10 +0100 Subject: [PATCH 02/31] fix bug --- atomistics/workflows/evcurve/debye.py | 1 + 1 file changed, 1 insertion(+) diff --git a/atomistics/workflows/evcurve/debye.py b/atomistics/workflows/evcurve/debye.py index 0d313770..4779a287 100644 --- a/atomistics/workflows/evcurve/debye.py +++ b/atomistics/workflows/evcurve/debye.py @@ -2,6 +2,7 @@ import scipy.constants import scipy.optimize +from atomistics.shared.output import OutputThermodynamic from atomistics.shared.generic import thermodynamic_output_keys from atomistics.workflows.evcurve.fit import interpolate_energy from atomistics.workflows.evcurve.thermo import get_thermo_bulk_model From 8f84cbe7ed9063d318ad15900dbe2b5cd32039f7 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Fri, 5 Jan 2024 15:21:01 +0100 Subject: [PATCH 03/31] make_output_dataclass() --- atomistics/shared/output.py | 47 +++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/atomistics/shared/output.py b/atomistics/shared/output.py index 66fcc954..a6f1556f 100644 --- a/atomistics/shared/output.py +++ b/atomistics/shared/output.py @@ -13,58 +13,55 @@ @dataclasses.dataclass class Output: - @classmethod - def fields(cls): - return tuple(field.name for field in dataclasses.fields(cls)) - def get(self, engine, *output: str) -> dict: return {q: getattr(self, q)(engine) for q in output} -OutputStatic = dataclasses.make_dataclass( +def make_output_dataclass(cls_name, output_keys): + return dataclasses.make_dataclass( + cls_name=cls_name, + fields=[(key, callable) for key in output_keys], + bases=(Output, ), +) + + +OutputStatic = make_output_dataclass( cls_name="OutputStatic", - fields=[(key, callable) for key in static_calculation_output_keys], - bases=(Output, ) + output_keys=static_calculation_output_keys ) -OutputMolecularDynamics = dataclasses.make_dataclass( +OutputMolecularDynamics = make_output_dataclass( cls_name="OutputMolecularDynamics", - fields=[(key, callable) for key in molecular_dynamics_output_keys], - bases=(Output, ) + output_keys=molecular_dynamics_output_keys, ) -OutputThermalExpansion = dataclasses.make_dataclass( +OutputThermalExpansion = make_output_dataclass( cls_name="OutputThermalExpansion", - fields=[(key, callable) for key in thermal_expansion_output_keys], - bases=(Output, ) + output_keys=thermal_expansion_output_keys, ) -OutputThermodynamic = dataclasses.make_dataclass( +OutputThermodynamic = make_output_dataclass( cls_name="OutputThermodynamic", - fields=[(key, callable) for key in thermodynamic_output_keys], - bases=(Output, ) + output_keys=thermodynamic_output_keys, ) -OutputEnergyVolumeCurve = dataclasses.make_dataclass( +OutputEnergyVolumeCurve = make_output_dataclass( cls_name="OutputEnergyVolumeCurve", - fields=[(key, callable) for key in energy_volume_curve_output_keys], - bases=(Output, ) + output_keys=energy_volume_curve_output_keys, ) -OutputElastic = dataclasses.make_dataclass( +OutputElastic = make_output_dataclass( cls_name="OutputElastic", - fields=[(key, callable) for key in elastic_matrix_output_keys], - bases=(Output, ) + output_keys=elastic_matrix_output_keys, ) -OutputPhonons = dataclasses.make_dataclass( +OutputPhonons = make_output_dataclass( cls_name="OutputPhonons", - fields=[(key, callable) for key in phonon_output_keys], - bases=(Output, ) + output_keys=phonon_output_keys, ) From 7a5bc1a835861fa365c2584afcbe17124f7a3345 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Fri, 5 Jan 2024 15:21:34 +0100 Subject: [PATCH 04/31] black formating --- atomistics/shared/generic.py | 2 +- atomistics/shared/output.py | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/atomistics/shared/generic.py b/atomistics/shared/generic.py index 260614c5..9a9cc286 100644 --- a/atomistics/shared/generic.py +++ b/atomistics/shared/generic.py @@ -40,7 +40,7 @@ "bulkmodul_eq", # float [GPa] "b_prime_eq", # float "fit_dict", # dict - "energy", # np.ndarray (V) [eV] + "energy", # np.ndarray (V) [eV] "volume", # np.ndarray (V) [Ang^3] ) diff --git a/atomistics/shared/output.py b/atomistics/shared/output.py index a6f1556f..6b166223 100644 --- a/atomistics/shared/output.py +++ b/atomistics/shared/output.py @@ -19,15 +19,14 @@ def get(self, engine, *output: str) -> dict: def make_output_dataclass(cls_name, output_keys): return dataclasses.make_dataclass( - cls_name=cls_name, - fields=[(key, callable) for key in output_keys], - bases=(Output, ), -) + cls_name=cls_name, + fields=[(key, callable) for key in output_keys], + bases=(Output,), + ) OutputStatic = make_output_dataclass( - cls_name="OutputStatic", - output_keys=static_calculation_output_keys + cls_name="OutputStatic", output_keys=static_calculation_output_keys ) From 51462e28189198a73a96ff216a99b6d61bb58839 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Fri, 5 Jan 2024 15:24:02 +0100 Subject: [PATCH 05/31] no more dataclass --- atomistics/shared/output.py | 16 +++++++--------- atomistics/workflows/elastic/workflow.py | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/atomistics/shared/output.py b/atomistics/shared/output.py index 6b166223..5a1635d1 100644 --- a/atomistics/shared/output.py +++ b/atomistics/shared/output.py @@ -1,4 +1,4 @@ -import dataclasses +from dataclasses import make_dataclass from atomistics.shared.generic import ( static_calculation_output_keys, @@ -11,17 +11,15 @@ ) -@dataclasses.dataclass -class Output: - def get(self, engine, *output: str) -> dict: - return {q: getattr(self, q)(engine) for q in output} - - def make_output_dataclass(cls_name, output_keys): - return dataclasses.make_dataclass( + return make_dataclass( cls_name=cls_name, fields=[(key, callable) for key in output_keys], - bases=(Output,), + namespace={ + "get": lambda self, engine, *output: { + q: getattr(self, q)(engine) for q in output + } + }, ) diff --git a/atomistics/workflows/elastic/workflow.py b/atomistics/workflows/elastic/workflow.py index 85d0544d..c8cf8069 100644 --- a/atomistics/workflows/elastic/workflow.py +++ b/atomistics/workflows/elastic/workflow.py @@ -11,7 +11,7 @@ elastic_matrix_output_elastic = OutputElastic( - **{k: getattr(ElasticProperties, k) for k in OutputElastic.fields()} + **{k: getattr(ElasticProperties, k) for k in elastic_matrix_output_keys} ) From a3919d0ef47f4a035ac3c91f98ba5f834cd090ce Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Fri, 5 Jan 2024 15:54:34 +0100 Subject: [PATCH 06/31] Initialize dataclass only shortly before usage --- atomistics/calculators/ase.py | 40 +++++++++------------ atomistics/calculators/lammps/calculator.py | 12 ++++--- atomistics/calculators/lammps/helpers.py | 19 ++++++---- atomistics/calculators/lammps/output.py | 24 ------------- atomistics/calculators/qe.py | 17 ++++----- atomistics/workflows/elastic/workflow.py | 11 ++---- atomistics/workflows/evcurve/debye.py | 17 ++++----- atomistics/workflows/evcurve/workflow.py | 21 +++++------ atomistics/workflows/phonons/workflow.py | 34 ++++++++---------- atomistics/workflows/quasiharmonic.py | 17 ++++----- 10 files changed, 82 insertions(+), 130 deletions(-) delete mode 100644 atomistics/calculators/lammps/output.py diff --git a/atomistics/calculators/ase.py b/atomistics/calculators/ase.py index 9b41f954..e6c86fa4 100644 --- a/atomistics/calculators/ase.py +++ b/atomistics/calculators/ase.py @@ -64,26 +64,6 @@ def get_volume(self): return self.structure.get_volume() -ASEOutputStatic = OutputStatic( - forces=ASEExecutor.get_forces, - energy=ASEExecutor.get_energy, - stress=ASEExecutor.get_stress, - volume=ASEExecutor.get_volume, -) - -ASEOutputMolecularDynamics = OutputMolecularDynamics( - positions=ASEExecutor.get_positions, - cell=ASEExecutor.get_cell, - forces=ASEExecutor.get_forces, - temperature=ASEExecutor.get_temperature, - energy_pot=ASEExecutor.get_energy, - energy_tot=ASEExecutor.get_total_energy, - pressure=ASEExecutor.get_stress, - velocities=ASEExecutor.get_velocities, - volume=ASEExecutor.get_volume, -) - - @as_task_dict_evaluator def evaluate_with_ase( structure: Atoms, @@ -125,15 +105,29 @@ def calc_static_with_ase( ase_calculator, output=static_calculation_output_keys, ): - return ASEOutputStatic.get( - ASEExecutor(ase_structure=structure, ase_calculator=ase_calculator), *output - ) + return OutputStatic( + forces=ASEExecutor.get_forces, + energy=ASEExecutor.get_energy, + stress=ASEExecutor.get_stress, + volume=ASEExecutor.get_volume, + ).get(ASEExecutor(ase_structure=structure, ase_calculator=ase_calculator), *output) def _calc_md_step_with_ase( dyn, structure, ase_calculator, temperature, run, thermo, output ): structure.calc = ase_calculator + ASEOutputMolecularDynamics = OutputMolecularDynamics( + positions=ASEExecutor.get_positions, + cell=ASEExecutor.get_cell, + forces=ASEExecutor.get_forces, + temperature=ASEExecutor.get_temperature, + energy_pot=ASEExecutor.get_energy, + energy_tot=ASEExecutor.get_total_energy, + pressure=ASEExecutor.get_stress, + velocities=ASEExecutor.get_velocities, + volume=ASEExecutor.get_volume, + ) MaxwellBoltzmannDistribution(atoms=structure, temperature_K=temperature) cache = {q: [] for q in output} for i in range(int(run / thermo)): diff --git a/atomistics/calculators/lammps/calculator.py b/atomistics/calculators/lammps/calculator.py index e81454b3..81a859e9 100644 --- a/atomistics/calculators/lammps/calculator.py +++ b/atomistics/calculators/lammps/calculator.py @@ -27,11 +27,8 @@ LAMMPS_RUN, LAMMPS_MINIMIZE_VOLUME, ) -from atomistics.calculators.lammps.output import ( - LammpsOutputMolecularDynamics, - LammpsOutputStatic, -) from atomistics.calculators.wrapper import as_task_dict_evaluator +from atomistics.shared.output import OutputStatic from atomistics.shared.generic import ( static_calculation_output_keys, molecular_dynamics_output_keys, @@ -138,7 +135,12 @@ def calc_static_with_lammps( lmp=lmp, **kwargs, ) - result_dict = LammpsOutputStatic.get(lmp_instance, *output) + result_dict = OutputStatic( + forces=LammpsASELibrary.interactive_forces_getter, + energy=LammpsASELibrary.interactive_energy_pot_getter, + stress=LammpsASELibrary.interactive_pressures_getter, + volume=LammpsASELibrary.interactive_volume_getter, + ).get(lmp_instance, *output) lammps_shutdown(lmp_instance=lmp_instance, close_instance=lmp is None) return result_dict diff --git a/atomistics/calculators/lammps/helpers.py b/atomistics/calculators/lammps/helpers.py index e364d412..c2550ce5 100644 --- a/atomistics/calculators/lammps/helpers.py +++ b/atomistics/calculators/lammps/helpers.py @@ -5,19 +5,14 @@ from pylammpsmpi import LammpsASELibrary from atomistics.calculators.lammps.potential import validate_potential_dataframe -from atomistics.calculators.lammps.output import LammpsOutputMolecularDynamics from atomistics.shared.thermal_expansion import ( + OutputMolecularDynamics, OutputThermalExpansionProperties, ThermalExpansionProperties, ) from atomistics.shared.generic import ( - static_calculation_output_keys, molecular_dynamics_output_keys, thermal_expansion_output_keys, - thermodynamic_output_keys, - energy_volume_curve_output_keys, - elastic_matrix_output_keys, - phonon_output_keys, ) from atomistics.shared.tqdm_iterator import get_tqdm_iterator @@ -59,7 +54,17 @@ def lammps_calc_md_step( ): run_str_rendered = Template(run_str).render(run=run) lmp_instance.interactive_lib_command(run_str_rendered) - return LammpsOutputMolecularDynamics.get(lmp_instance, *output) + return OutputMolecularDynamics( + positions=LammpsASELibrary.interactive_positions_getter, + cell=LammpsASELibrary.interactive_cells_getter, + forces=LammpsASELibrary.interactive_forces_getter, + temperature=LammpsASELibrary.interactive_temperatures_getter, + energy_pot=LammpsASELibrary.interactive_energy_pot_getter, + energy_tot=LammpsASELibrary.interactive_energy_tot_getter, + pressure=LammpsASELibrary.interactive_pressures_getter, + velocities=LammpsASELibrary.interactive_velocities_getter, + volume=LammpsASELibrary.interactive_volume_getter, + ).get(lmp_instance, *output) def lammps_calc_md( diff --git a/atomistics/calculators/lammps/output.py b/atomistics/calculators/lammps/output.py deleted file mode 100644 index 88e9fb12..00000000 --- a/atomistics/calculators/lammps/output.py +++ /dev/null @@ -1,24 +0,0 @@ -from atomistics.shared.output import ( - OutputStatic, - OutputMolecularDynamics, -) -from pylammpsmpi import LammpsASELibrary - - -LammpsOutputStatic = OutputStatic( - forces=LammpsASELibrary.interactive_forces_getter, - energy=LammpsASELibrary.interactive_energy_pot_getter, - stress=LammpsASELibrary.interactive_pressures_getter, - volume=LammpsASELibrary.interactive_volume_getter, -) -LammpsOutputMolecularDynamics = OutputMolecularDynamics( - positions=LammpsASELibrary.interactive_positions_getter, - cell=LammpsASELibrary.interactive_cells_getter, - forces=LammpsASELibrary.interactive_forces_getter, - temperature=LammpsASELibrary.interactive_temperatures_getter, - energy_pot=LammpsASELibrary.interactive_energy_pot_getter, - energy_tot=LammpsASELibrary.interactive_energy_tot_getter, - pressure=LammpsASELibrary.interactive_pressures_getter, - velocities=LammpsASELibrary.interactive_velocities_getter, - volume=LammpsASELibrary.interactive_volume_getter, -) diff --git a/atomistics/calculators/qe.py b/atomistics/calculators/qe.py index dee47cfd..d6ef6d51 100644 --- a/atomistics/calculators/qe.py +++ b/atomistics/calculators/qe.py @@ -27,14 +27,6 @@ def get_volume(self): return self.parser.volume -QuantumEspressoOutputStatic = OutputStatic( - forces=QEStaticParser.get_forces, - energy=QEStaticParser.get_energy, - stress=QEStaticParser.get_stress, - volume=QEStaticParser.get_volume, -) - - def call_qe_via_ase_command(calculation_name, working_directory): subprocess.check_output( os.environ["ASE_ESPRESSO_COMMAND"].replace("PREFIX", calculation_name), @@ -216,9 +208,12 @@ def calc_static_with_qe( call_qe_via_ase_command( calculation_name=calculation_name, working_directory=working_directory ) - return QuantumEspressoOutputStatic.get( - QEStaticParser(filename=output_file_name), *output - ) + return OutputStatic( + forces=QEStaticParser.get_forces, + energy=QEStaticParser.get_energy, + stress=QEStaticParser.get_stress, + volume=QEStaticParser.get_volume, + ).get(QEStaticParser(filename=output_file_name), *output) @as_task_dict_evaluator diff --git a/atomistics/workflows/elastic/workflow.py b/atomistics/workflows/elastic/workflow.py index c8cf8069..5b919b37 100644 --- a/atomistics/workflows/elastic/workflow.py +++ b/atomistics/workflows/elastic/workflow.py @@ -10,11 +10,6 @@ ) -elastic_matrix_output_elastic = OutputElastic( - **{k: getattr(ElasticProperties, k) for k in elastic_matrix_output_keys} -) - - class ElasticMatrixWorkflow(Workflow): def __init__( self, structure, num_of_point=5, eps_range=0.005, sqrt_eta=True, fit_order=2 @@ -67,6 +62,6 @@ def analyse_structures(self, output_dict, output=elastic_matrix_output_keys): self._data["strain_energy"] = strain_energy self._data["e0"] = ene0 self._data["A2"] = A2 - return elastic_matrix_output_elastic.get( - ElasticProperties(elastic_matrix=elastic_matrix), *output - ) + return OutputElastic( + **{k: getattr(ElasticProperties, k) for k in elastic_matrix_output_keys} + ).get(ElasticProperties(elastic_matrix=elastic_matrix), *output) diff --git a/atomistics/workflows/evcurve/debye.py b/atomistics/workflows/evcurve/debye.py index 4779a287..6603dce9 100644 --- a/atomistics/workflows/evcurve/debye.py +++ b/atomistics/workflows/evcurve/debye.py @@ -77,15 +77,6 @@ def get_volumes(self): return np.array([self._pes.volumes[0]] * len(self._temperatures)) -DebyeOutputThermodynamic = OutputThermodynamic( - temperatures=DebyeThermalProperties.get_temperatures, - free_energy=DebyeThermalProperties.get_free_energy, - entropy=DebyeThermalProperties.get_entropy, - heat_capacity=DebyeThermalProperties.get_heat_capacity, - volumes=DebyeThermalProperties.get_volumes, -) - - def _debye_kernel(xi): return xi**3 / (np.exp(xi) - 1) @@ -239,7 +230,13 @@ def get_thermal_properties( num_steps=50, output=thermodynamic_output_keys, ): - return DebyeOutputThermodynamic.get( + return OutputThermodynamic( + temperatures=DebyeThermalProperties.get_temperatures, + free_energy=DebyeThermalProperties.get_free_energy, + entropy=DebyeThermalProperties.get_entropy, + heat_capacity=DebyeThermalProperties.get_heat_capacity, + volumes=DebyeThermalProperties.get_volumes, + ).get( DebyeThermalProperties( fit_dict=fit_dict, masses=masses, diff --git a/atomistics/workflows/evcurve/workflow.py b/atomistics/workflows/evcurve/workflow.py index 32ea8d49..b1133302 100644 --- a/atomistics/workflows/evcurve/workflow.py +++ b/atomistics/workflows/evcurve/workflow.py @@ -128,17 +128,6 @@ def get_fit_dict(self): } -EnergyVolumeCurveOutputEnergyVolumeCurve = OutputEnergyVolumeCurve( - fit_dict=EnergyVolumeCurveProperties.get_fit_dict, - energy=EnergyVolumeCurveProperties.get_energies, - volume=EnergyVolumeCurveProperties.get_volumes, - b_prime_eq=EnergyVolumeCurveProperties.get_bulkmodul_pressure_derivative_eq, - bulkmodul_eq=EnergyVolumeCurveProperties.get_bulkmodul_eq, - energy_eq=EnergyVolumeCurveProperties.get_energy_eq, - volume_eq=EnergyVolumeCurveProperties.get_volume_eq, -) - - class EnergyVolumeCurveWorkflow(Workflow): def __init__( self, @@ -185,7 +174,15 @@ def generate_structures(self): return {"calc_energy": self._structure_dict} def analyse_structures(self, output_dict, output=energy_volume_curve_output_keys): - self._fit_dict = EnergyVolumeCurveOutputEnergyVolumeCurve.get( + self._fit_dict = OutputEnergyVolumeCurve( + fit_dict=EnergyVolumeCurveProperties.get_fit_dict, + energy=EnergyVolumeCurveProperties.get_energies, + volume=EnergyVolumeCurveProperties.get_volumes, + b_prime_eq=EnergyVolumeCurveProperties.get_bulkmodul_pressure_derivative_eq, + bulkmodul_eq=EnergyVolumeCurveProperties.get_bulkmodul_eq, + energy_eq=EnergyVolumeCurveProperties.get_energy_eq, + volume_eq=EnergyVolumeCurveProperties.get_volume_eq, + ).get( EnergyVolumeCurveProperties( fit_module=fit_ev_curve_internal( volume_lst=get_volume_lst(structure_dict=self._structure_dict), diff --git a/atomistics/workflows/phonons/workflow.py b/atomistics/workflows/phonons/workflow.py index afec1dff..4f602932 100644 --- a/atomistics/workflows/phonons/workflow.py +++ b/atomistics/workflows/phonons/workflow.py @@ -145,22 +145,6 @@ def get_volumes(self): ) -PhonopyOutputPhonons = OutputPhonons( - mesh_dict=PhonopyProperties.get_mesh_dict, - band_structure_dict=PhonopyProperties.get_band_structure_dict, - total_dos_dict=PhonopyProperties.get_total_dos_dict, - dynamical_matrix=PhonopyProperties.get_dynamical_matrix, - force_constants=PhonopyProperties.get_force_constants, -) -PhonopyOutputThermodynamic = OutputThermodynamic( - temperatures=PhonopyThermalProperties.get_temperatures, - free_energy=PhonopyThermalProperties.get_free_energy, - entropy=PhonopyThermalProperties.get_entropy, - heat_capacity=PhonopyThermalProperties.get_heat_capacity, - volumes=PhonopyThermalProperties.get_volumes, -) - - class PhonopyWorkflow(Workflow): """ Phonopy wrapper for the calculation of free energy in the framework of quasi harmonic approximation. @@ -253,7 +237,13 @@ def analyse_structures(self, output_dict, output=phonon_output_keys): output_dict = output_dict["forces"] forces_lst = [output_dict[k] for k in sorted(output_dict.keys())] self.phonopy.forces = forces_lst - self._phonopy_dict = PhonopyOutputPhonons.get( + self._phonopy_dict = OutputPhonons( + mesh_dict=PhonopyProperties.get_mesh_dict, + band_structure_dict=PhonopyProperties.get_band_structure_dict, + total_dos_dict=PhonopyProperties.get_total_dos_dict, + dynamical_matrix=PhonopyProperties.get_dynamical_matrix, + force_constants=PhonopyProperties.get_force_constants, + ).get( PhonopyProperties( phonopy_instance=self.phonopy, dos_mesh=self._dos_mesh, @@ -311,9 +301,13 @@ def get_thermal_properties( band_indices=band_indices, is_projection=is_projection, ) - return PhonopyOutputThermodynamic.get( - PhonopyThermalProperties(phonopy_instance=self.phonopy), *output - ) + return OutputThermodynamic( + temperatures=PhonopyThermalProperties.get_temperatures, + free_energy=PhonopyThermalProperties.get_free_energy, + entropy=PhonopyThermalProperties.get_entropy, + heat_capacity=PhonopyThermalProperties.get_heat_capacity, + volumes=PhonopyThermalProperties.get_volumes, + ).get(PhonopyThermalProperties(phonopy_instance=self.phonopy), *output) def get_dynamical_matrix(self, npoints=101): """ diff --git a/atomistics/workflows/quasiharmonic.py b/atomistics/workflows/quasiharmonic.py index ea57da4b..787e2977 100644 --- a/atomistics/workflows/quasiharmonic.py +++ b/atomistics/workflows/quasiharmonic.py @@ -113,7 +113,13 @@ def get_thermal_properties( not quantum_mechanical ): # heat capacity and entropy are not yet implemented for the classical approach. output = ["free_energy", "temperatures", "volumes"] - return QuasiHarmonicOutputThermodynamic.get( + return OutputThermodynamic( + temperatures=QuasiHarmonicThermalProperties.get_temperatures, + free_energy=QuasiHarmonicThermalProperties.get_free_energy, + entropy=QuasiHarmonicThermalProperties.get_entropy, + heat_capacity=QuasiHarmonicThermalProperties.get_heat_capacity, + volumes=QuasiHarmonicThermalProperties.get_volumes, + ).get( QuasiHarmonicThermalProperties( temperatures=temperatures, thermal_properties_dict=tp_collect_dict, @@ -275,15 +281,6 @@ def get_volumes(self): return self._volumes_selected_lst -QuasiHarmonicOutputThermodynamic = OutputThermodynamic( - temperatures=QuasiHarmonicThermalProperties.get_temperatures, - free_energy=QuasiHarmonicThermalProperties.get_free_energy, - entropy=QuasiHarmonicThermalProperties.get_entropy, - heat_capacity=QuasiHarmonicThermalProperties.get_heat_capacity, - volumes=QuasiHarmonicThermalProperties.get_volumes, -) - - class QuasiHarmonicWorkflow(EnergyVolumeCurveWorkflow): def __init__( self, From f2f6edb0a76016099d672c94640c4905b94786a9 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Fri, 5 Jan 2024 16:03:09 +0100 Subject: [PATCH 07/31] Fix LAMMPS bug --- atomistics/calculators/lammps/helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/atomistics/calculators/lammps/helpers.py b/atomistics/calculators/lammps/helpers.py index c2550ce5..329918bd 100644 --- a/atomistics/calculators/lammps/helpers.py +++ b/atomistics/calculators/lammps/helpers.py @@ -5,8 +5,8 @@ from pylammpsmpi import LammpsASELibrary from atomistics.calculators.lammps.potential import validate_potential_dataframe +from atomistics.shared.output import OutputMolecularDynamics from atomistics.shared.thermal_expansion import ( - OutputMolecularDynamics, OutputThermalExpansionProperties, ThermalExpansionProperties, ) From c70d2eddb04567c737f877c6156ba59e10e71d56 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Fri, 5 Jan 2024 16:39:15 +0100 Subject: [PATCH 08/31] define function calls directly --- atomistics/calculators/ase.py | 50 +++++++---------- atomistics/calculators/lammps/calculator.py | 10 ++-- atomistics/calculators/lammps/helpers.py | 32 +++++------ atomistics/calculators/qe.py | 11 ++-- atomistics/shared/output.py | 4 +- atomistics/shared/thermal_expansion.py | 12 ++-- atomistics/workflows/elastic/workflow.py | 5 +- atomistics/workflows/evcurve/debye.py | 34 ++++++------ atomistics/workflows/evcurve/workflow.py | 38 ++++++------- atomistics/workflows/phonons/workflow.py | 61 ++++++++++----------- atomistics/workflows/quasiharmonic.py | 28 +++++----- 11 files changed, 134 insertions(+), 151 deletions(-) diff --git a/atomistics/calculators/ase.py b/atomistics/calculators/ase.py index e6c86fa4..4858c0af 100644 --- a/atomistics/calculators/ase.py +++ b/atomistics/calculators/ase.py @@ -16,10 +16,7 @@ thermal_expansion_output_keys, ) from atomistics.shared.output import OutputStatic, OutputMolecularDynamics -from atomistics.shared.thermal_expansion import ( - OutputThermalExpansionProperties, - ThermalExpansionProperties, -) +from atomistics.shared.thermal_expansion import get_thermal_expansion_output from atomistics.shared.tqdm_iterator import get_tqdm_iterator if TYPE_CHECKING: @@ -105,37 +102,35 @@ def calc_static_with_ase( ase_calculator, output=static_calculation_output_keys, ): + ase_exe = ASEExecutor(ase_structure=structure, ase_calculator=ase_calculator) return OutputStatic( - forces=ASEExecutor.get_forces, - energy=ASEExecutor.get_energy, - stress=ASEExecutor.get_stress, - volume=ASEExecutor.get_volume, - ).get(ASEExecutor(ase_structure=structure, ase_calculator=ase_calculator), *output) + forces=ase_exe.get_forces, + energy=ase_exe.get_energy, + stress=ase_exe.get_stress, + volume=ase_exe.get_volume, + ).get(*output) def _calc_md_step_with_ase( dyn, structure, ase_calculator, temperature, run, thermo, output ): structure.calc = ase_calculator - ASEOutputMolecularDynamics = OutputMolecularDynamics( - positions=ASEExecutor.get_positions, - cell=ASEExecutor.get_cell, - forces=ASEExecutor.get_forces, - temperature=ASEExecutor.get_temperature, - energy_pot=ASEExecutor.get_energy, - energy_tot=ASEExecutor.get_total_energy, - pressure=ASEExecutor.get_stress, - velocities=ASEExecutor.get_velocities, - volume=ASEExecutor.get_volume, - ) MaxwellBoltzmannDistribution(atoms=structure, temperature_K=temperature) cache = {q: [] for q in output} for i in range(int(run / thermo)): dyn.run(thermo) - calc_dict = ASEOutputMolecularDynamics.get( - ASEExecutor(ase_structure=structure, ase_calculator=ase_calculator), - *output, - ) + ase_exe = ASEExecutor(ase_structure=structure, ase_calculator=ase_calculator) + calc_dict = OutputMolecularDynamics( + positions=ase_exe.get_positions, + cell=ase_exe.get_cell, + forces=ase_exe.get_forces, + temperature=ase_exe.get_temperature, + energy_pot=ase_exe.get_energy, + energy_tot=ase_exe.get_total_energy, + pressure=ase_exe.get_stress, + velocities=ase_exe.get_velocities, + volume=ase_exe.get_volume, + ).get(*output) for k, v in calc_dict.items(): cache[k].append(v) return {q: np.array(cache[q]) for q in output} @@ -257,9 +252,6 @@ def calc_molecular_dynamics_thermal_expansion_with_ase( structure_current.set_cell(cell=result_dict["cell"][-1], scale_atoms=True) temperature_md_lst.append(result_dict["temperature"][-1]) volume_md_lst.append(result_dict["volume"][-1]) - return OutputThermalExpansionProperties.get( - ThermalExpansionProperties( - temperatures_lst=temperature_md_lst, volumes_lst=volume_md_lst - ), - *output, + return get_thermal_expansion_output( + temperatures_lst=temperature_md_lst, volumes_lst=volume_md_lst, output=output ) diff --git a/atomistics/calculators/lammps/calculator.py b/atomistics/calculators/lammps/calculator.py index 81a859e9..3506ed8d 100644 --- a/atomistics/calculators/lammps/calculator.py +++ b/atomistics/calculators/lammps/calculator.py @@ -136,11 +136,11 @@ def calc_static_with_lammps( **kwargs, ) result_dict = OutputStatic( - forces=LammpsASELibrary.interactive_forces_getter, - energy=LammpsASELibrary.interactive_energy_pot_getter, - stress=LammpsASELibrary.interactive_pressures_getter, - volume=LammpsASELibrary.interactive_volume_getter, - ).get(lmp_instance, *output) + 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) lammps_shutdown(lmp_instance=lmp_instance, close_instance=lmp is None) return result_dict diff --git a/atomistics/calculators/lammps/helpers.py b/atomistics/calculators/lammps/helpers.py index 329918bd..a7224daa 100644 --- a/atomistics/calculators/lammps/helpers.py +++ b/atomistics/calculators/lammps/helpers.py @@ -6,10 +6,7 @@ from atomistics.calculators.lammps.potential import validate_potential_dataframe from atomistics.shared.output import OutputMolecularDynamics -from atomistics.shared.thermal_expansion import ( - OutputThermalExpansionProperties, - ThermalExpansionProperties, -) +from atomistics.shared.thermal_expansion import get_thermal_expansion_output from atomistics.shared.generic import ( molecular_dynamics_output_keys, thermal_expansion_output_keys, @@ -55,16 +52,16 @@ 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=LammpsASELibrary.interactive_positions_getter, - cell=LammpsASELibrary.interactive_cells_getter, - forces=LammpsASELibrary.interactive_forces_getter, - temperature=LammpsASELibrary.interactive_temperatures_getter, - energy_pot=LammpsASELibrary.interactive_energy_pot_getter, - energy_tot=LammpsASELibrary.interactive_energy_tot_getter, - pressure=LammpsASELibrary.interactive_pressures_getter, - velocities=LammpsASELibrary.interactive_velocities_getter, - volume=LammpsASELibrary.interactive_volume_getter, - ).get(lmp_instance, *output) + 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) def lammps_calc_md( @@ -135,11 +132,8 @@ def lammps_thermal_expansion_loop( volume_md_lst.append(lmp_instance.interactive_volume_getter()) temperature_md_lst.append(lmp_instance.interactive_temperatures_getter()) lammps_shutdown(lmp_instance=lmp_instance, close_instance=lmp is None) - return OutputThermalExpansionProperties.get( - ThermalExpansionProperties( - temperatures_lst=temperature_md_lst, volumes_lst=volume_md_lst - ), - *output, + return get_thermal_expansion_output( + temperatures_lst=temperature_md_lst, volumes_lst=volume_md_lst, output=output ) diff --git a/atomistics/calculators/qe.py b/atomistics/calculators/qe.py index d6ef6d51..6936d8fa 100644 --- a/atomistics/calculators/qe.py +++ b/atomistics/calculators/qe.py @@ -208,12 +208,13 @@ def calc_static_with_qe( call_qe_via_ase_command( calculation_name=calculation_name, working_directory=working_directory ) + qe_parser = QEStaticParser(filename=output_file_name) return OutputStatic( - forces=QEStaticParser.get_forces, - energy=QEStaticParser.get_energy, - stress=QEStaticParser.get_stress, - volume=QEStaticParser.get_volume, - ).get(QEStaticParser(filename=output_file_name), *output) + forces=qe_parser.get_forces, + energy=qe_parser.get_energy, + stress=qe_parser.get_stress, + volume=qe_parser.get_volume, + ).get(*output) @as_task_dict_evaluator diff --git a/atomistics/shared/output.py b/atomistics/shared/output.py index 5a1635d1..9636d161 100644 --- a/atomistics/shared/output.py +++ b/atomistics/shared/output.py @@ -16,9 +16,7 @@ def make_output_dataclass(cls_name, output_keys): cls_name=cls_name, fields=[(key, callable) for key in output_keys], namespace={ - "get": lambda self, engine, *output: { - q: getattr(self, q)(engine) for q in output - } + "get": lambda self, *output: {q: getattr(self, q)() for q in output} }, ) diff --git a/atomistics/shared/thermal_expansion.py b/atomistics/shared/thermal_expansion.py index 6b026ad2..38fb1651 100644 --- a/atomistics/shared/thermal_expansion.py +++ b/atomistics/shared/thermal_expansion.py @@ -13,7 +13,11 @@ def get_temperatures(self): return self._temperatures_lst -OutputThermalExpansionProperties = OutputThermalExpansion( - temperatures=ThermalExpansionProperties.get_temperatures, - volumes=ThermalExpansionProperties.get_volumes, -) +def get_thermal_expansion_output(temperatures_lst, volumes_lst, output): + thermal_properties = ThermalExpansionProperties( + temperatures_lst=temperatures_lst, volumes_lst=volumes_lst + ) + return OutputThermalExpansion( + temperatures=thermal_properties.get_temperatures, + volumes=thermal_properties.get_volumes, + ).get(*output) diff --git a/atomistics/workflows/elastic/workflow.py b/atomistics/workflows/elastic/workflow.py index 5b919b37..95b57d7d 100644 --- a/atomistics/workflows/elastic/workflow.py +++ b/atomistics/workflows/elastic/workflow.py @@ -62,6 +62,7 @@ def analyse_structures(self, output_dict, output=elastic_matrix_output_keys): self._data["strain_energy"] = strain_energy self._data["e0"] = ene0 self._data["A2"] = A2 + elastic_prop = ElasticProperties(elastic_matrix=elastic_matrix) return OutputElastic( - **{k: getattr(ElasticProperties, k) for k in elastic_matrix_output_keys} - ).get(ElasticProperties(elastic_matrix=elastic_matrix), *output) + **{k: getattr(elastic_prop, k) for k in elastic_matrix_output_keys} + ).get(*output) diff --git a/atomistics/workflows/evcurve/debye.py b/atomistics/workflows/evcurve/debye.py index 6603dce9..54aca6fe 100644 --- a/atomistics/workflows/evcurve/debye.py +++ b/atomistics/workflows/evcurve/debye.py @@ -230,22 +230,20 @@ def get_thermal_properties( num_steps=50, output=thermodynamic_output_keys, ): - return OutputThermodynamic( - temperatures=DebyeThermalProperties.get_temperatures, - free_energy=DebyeThermalProperties.get_free_energy, - entropy=DebyeThermalProperties.get_entropy, - heat_capacity=DebyeThermalProperties.get_heat_capacity, - volumes=DebyeThermalProperties.get_volumes, - ).get( - DebyeThermalProperties( - fit_dict=fit_dict, - masses=masses, - t_min=t_min, - t_max=t_max, - t_step=t_step, - temperatures=temperatures, - constant_volume=constant_volume, - num_steps=num_steps, - ), - *output, + debye_thermal = DebyeThermalProperties( + fit_dict=fit_dict, + masses=masses, + t_min=t_min, + t_max=t_max, + t_step=t_step, + temperatures=temperatures, + constant_volume=constant_volume, + num_steps=num_steps, ) + return OutputThermodynamic( + temperatures=debye_thermal.get_temperatures, + free_energy=debye_thermal.get_free_energy, + entropy=debye_thermal.get_entropy, + heat_capacity=debye_thermal.get_heat_capacity, + volumes=debye_thermal.get_volumes, + ).get(*output) diff --git a/atomistics/workflows/evcurve/workflow.py b/atomistics/workflows/evcurve/workflow.py index b1133302..15633fd2 100644 --- a/atomistics/workflows/evcurve/workflow.py +++ b/atomistics/workflows/evcurve/workflow.py @@ -174,27 +174,25 @@ def generate_structures(self): return {"calc_energy": self._structure_dict} def analyse_structures(self, output_dict, output=energy_volume_curve_output_keys): - self._fit_dict = OutputEnergyVolumeCurve( - fit_dict=EnergyVolumeCurveProperties.get_fit_dict, - energy=EnergyVolumeCurveProperties.get_energies, - volume=EnergyVolumeCurveProperties.get_volumes, - b_prime_eq=EnergyVolumeCurveProperties.get_bulkmodul_pressure_derivative_eq, - bulkmodul_eq=EnergyVolumeCurveProperties.get_bulkmodul_eq, - energy_eq=EnergyVolumeCurveProperties.get_energy_eq, - volume_eq=EnergyVolumeCurveProperties.get_volume_eq, - ).get( - 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, - ) - ), - *output, + ev_prop = 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, + ) ) + self._fit_dict = OutputEnergyVolumeCurve( + fit_dict=ev_prop.get_fit_dict, + energy=ev_prop.get_energies, + volume=ev_prop.get_volumes, + b_prime_eq=ev_prop.get_bulkmodul_pressure_derivative_eq, + bulkmodul_eq=ev_prop.get_bulkmodul_eq, + energy_eq=ev_prop.get_energy_eq, + volume_eq=ev_prop.get_volume_eq, + ).get(*output) return self.fit_dict def get_volume_lst(self): diff --git a/atomistics/workflows/phonons/workflow.py b/atomistics/workflows/phonons/workflow.py index 4f602932..a91378db 100644 --- a/atomistics/workflows/phonons/workflow.py +++ b/atomistics/workflows/phonons/workflow.py @@ -237,32 +237,30 @@ def analyse_structures(self, output_dict, output=phonon_output_keys): output_dict = output_dict["forces"] forces_lst = [output_dict[k] for k in sorted(output_dict.keys())] self.phonopy.forces = forces_lst - self._phonopy_dict = OutputPhonons( - mesh_dict=PhonopyProperties.get_mesh_dict, - band_structure_dict=PhonopyProperties.get_band_structure_dict, - total_dos_dict=PhonopyProperties.get_total_dos_dict, - dynamical_matrix=PhonopyProperties.get_dynamical_matrix, - force_constants=PhonopyProperties.get_force_constants, - ).get( - PhonopyProperties( - phonopy_instance=self.phonopy, - dos_mesh=self._dos_mesh, - shift=None, - is_time_reversal=True, - is_mesh_symmetry=True, - with_eigenvectors=False, - with_group_velocities=False, - is_gamma_center=False, - number_of_snapshots=self._number_of_snapshots, - sigma=None, - freq_min=None, - freq_max=None, - freq_pitch=None, - use_tetrahedron_method=True, - npoints=101, - ), - *output, + phono_prop = PhonopyProperties( + phonopy_instance=self.phonopy, + dos_mesh=self._dos_mesh, + shift=None, + is_time_reversal=True, + is_mesh_symmetry=True, + with_eigenvectors=False, + with_group_velocities=False, + is_gamma_center=False, + number_of_snapshots=self._number_of_snapshots, + sigma=None, + freq_min=None, + freq_max=None, + freq_pitch=None, + use_tetrahedron_method=True, + npoints=101, ) + self._phonopy_dict = OutputPhonons( + mesh_dict=phono_prop.get_mesh_dict, + band_structure_dict=phono_prop.get_band_structure_dict, + total_dos_dict=phono_prop.get_total_dos_dict, + dynamical_matrix=phono_prop.get_dynamical_matrix, + force_constants=phono_prop.get_force_constants, + ).get(*output) return self._phonopy_dict def get_thermal_properties( @@ -301,13 +299,14 @@ def get_thermal_properties( band_indices=band_indices, is_projection=is_projection, ) + phono_thermal = PhonopyThermalProperties(phonopy_instance=self.phonopy) return OutputThermodynamic( - temperatures=PhonopyThermalProperties.get_temperatures, - free_energy=PhonopyThermalProperties.get_free_energy, - entropy=PhonopyThermalProperties.get_entropy, - heat_capacity=PhonopyThermalProperties.get_heat_capacity, - volumes=PhonopyThermalProperties.get_volumes, - ).get(PhonopyThermalProperties(phonopy_instance=self.phonopy), *output) + temperatures=phono_thermal.get_temperatures, + free_energy=phono_thermal.get_free_energy, + entropy=phono_thermal.get_entropy, + heat_capacity=phono_thermal.get_heat_capacity, + volumes=phono_thermal.get_volumes, + ).get(*output) def get_dynamical_matrix(self, npoints=101): """ diff --git a/atomistics/workflows/quasiharmonic.py b/atomistics/workflows/quasiharmonic.py index 787e2977..592dee59 100644 --- a/atomistics/workflows/quasiharmonic.py +++ b/atomistics/workflows/quasiharmonic.py @@ -113,22 +113,20 @@ def get_thermal_properties( not quantum_mechanical ): # heat capacity and entropy are not yet implemented for the classical approach. output = ["free_energy", "temperatures", "volumes"] - return OutputThermodynamic( - temperatures=QuasiHarmonicThermalProperties.get_temperatures, - free_energy=QuasiHarmonicThermalProperties.get_free_energy, - entropy=QuasiHarmonicThermalProperties.get_entropy, - heat_capacity=QuasiHarmonicThermalProperties.get_heat_capacity, - volumes=QuasiHarmonicThermalProperties.get_volumes, - ).get( - QuasiHarmonicThermalProperties( - temperatures=temperatures, - thermal_properties_dict=tp_collect_dict, - strain_lst=strain_lst, - volumes_lst=volume_lst, - volumes_selected_lst=vol_lst, - ), - *output, + qh_thermal = QuasiHarmonicThermalProperties( + temperatures=temperatures, + thermal_properties_dict=tp_collect_dict, + strain_lst=strain_lst, + volumes_lst=volume_lst, + volumes_selected_lst=vol_lst, ) + return OutputThermodynamic( + temperatures=qh_thermal.get_temperatures, + free_energy=qh_thermal.get_free_energy, + entropy=qh_thermal.get_entropy, + heat_capacity=qh_thermal.get_heat_capacity, + volumes=qh_thermal.get_volumes, + ).get(*output) def _get_thermal_properties_quantum_mechanical( From cc938b47b3ca5b1a36833eddc5b1c302795ccf19 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Fri, 5 Jan 2024 16:42:49 +0100 Subject: [PATCH 09/31] refactor get call --- atomistics/calculators/ase.py | 4 ++-- atomistics/calculators/lammps/calculator.py | 2 +- atomistics/calculators/lammps/helpers.py | 2 +- atomistics/calculators/qe.py | 2 +- atomistics/shared/output.py | 2 +- atomistics/shared/thermal_expansion.py | 2 +- atomistics/workflows/elastic/workflow.py | 2 +- atomistics/workflows/evcurve/debye.py | 2 +- atomistics/workflows/evcurve/workflow.py | 2 +- atomistics/workflows/phonons/workflow.py | 4 ++-- atomistics/workflows/quasiharmonic.py | 2 +- 11 files changed, 13 insertions(+), 13 deletions(-) diff --git a/atomistics/calculators/ase.py b/atomistics/calculators/ase.py index 4858c0af..364f005f 100644 --- a/atomistics/calculators/ase.py +++ b/atomistics/calculators/ase.py @@ -108,7 +108,7 @@ def calc_static_with_ase( energy=ase_exe.get_energy, stress=ase_exe.get_stress, volume=ase_exe.get_volume, - ).get(*output) + ).get(output=output) def _calc_md_step_with_ase( @@ -130,7 +130,7 @@ def _calc_md_step_with_ase( pressure=ase_exe.get_stress, velocities=ase_exe.get_velocities, volume=ase_exe.get_volume, - ).get(*output) + ).get(output=output) for k, v in calc_dict.items(): cache[k].append(v) return {q: np.array(cache[q]) for q in output} diff --git a/atomistics/calculators/lammps/calculator.py b/atomistics/calculators/lammps/calculator.py index 3506ed8d..cd762e3f 100644 --- a/atomistics/calculators/lammps/calculator.py +++ b/atomistics/calculators/lammps/calculator.py @@ -140,7 +140,7 @@ def calc_static_with_lammps( energy=lmp_instance.interactive_energy_pot_getter, stress=lmp_instance.interactive_pressures_getter, volume=lmp_instance.interactive_volume_getter, - ).get(*output) + ).get(output=output) lammps_shutdown(lmp_instance=lmp_instance, close_instance=lmp is None) return result_dict diff --git a/atomistics/calculators/lammps/helpers.py b/atomistics/calculators/lammps/helpers.py index a7224daa..f0ba668c 100644 --- a/atomistics/calculators/lammps/helpers.py +++ b/atomistics/calculators/lammps/helpers.py @@ -61,7 +61,7 @@ def lammps_calc_md_step( pressure=lmp_instance.interactive_pressures_getter, velocities=lmp_instance.interactive_velocities_getter, volume=lmp_instance.interactive_volume_getter, - ).get(*output) + ).get(output=output) def lammps_calc_md( diff --git a/atomistics/calculators/qe.py b/atomistics/calculators/qe.py index 6936d8fa..78834862 100644 --- a/atomistics/calculators/qe.py +++ b/atomistics/calculators/qe.py @@ -214,7 +214,7 @@ def calc_static_with_qe( energy=qe_parser.get_energy, stress=qe_parser.get_stress, volume=qe_parser.get_volume, - ).get(*output) + ).get(output=output) @as_task_dict_evaluator diff --git a/atomistics/shared/output.py b/atomistics/shared/output.py index 9636d161..3672d9d7 100644 --- a/atomistics/shared/output.py +++ b/atomistics/shared/output.py @@ -16,7 +16,7 @@ def make_output_dataclass(cls_name, output_keys): cls_name=cls_name, fields=[(key, callable) for key in output_keys], namespace={ - "get": lambda self, *output: {q: getattr(self, q)() for q in output} + "get": lambda self, output: {q: getattr(self, q)() for q in output} }, ) diff --git a/atomistics/shared/thermal_expansion.py b/atomistics/shared/thermal_expansion.py index 38fb1651..7bab40e9 100644 --- a/atomistics/shared/thermal_expansion.py +++ b/atomistics/shared/thermal_expansion.py @@ -20,4 +20,4 @@ def get_thermal_expansion_output(temperatures_lst, volumes_lst, output): return OutputThermalExpansion( temperatures=thermal_properties.get_temperatures, volumes=thermal_properties.get_volumes, - ).get(*output) + ).get(output=output) diff --git a/atomistics/workflows/elastic/workflow.py b/atomistics/workflows/elastic/workflow.py index 95b57d7d..02298da5 100644 --- a/atomistics/workflows/elastic/workflow.py +++ b/atomistics/workflows/elastic/workflow.py @@ -65,4 +65,4 @@ def analyse_structures(self, output_dict, output=elastic_matrix_output_keys): elastic_prop = ElasticProperties(elastic_matrix=elastic_matrix) return OutputElastic( **{k: getattr(elastic_prop, k) for k in elastic_matrix_output_keys} - ).get(*output) + ).get(output=output) diff --git a/atomistics/workflows/evcurve/debye.py b/atomistics/workflows/evcurve/debye.py index 54aca6fe..91e499ae 100644 --- a/atomistics/workflows/evcurve/debye.py +++ b/atomistics/workflows/evcurve/debye.py @@ -246,4 +246,4 @@ def get_thermal_properties( entropy=debye_thermal.get_entropy, heat_capacity=debye_thermal.get_heat_capacity, volumes=debye_thermal.get_volumes, - ).get(*output) + ).get(output=output) diff --git a/atomistics/workflows/evcurve/workflow.py b/atomistics/workflows/evcurve/workflow.py index 15633fd2..a94ab7c3 100644 --- a/atomistics/workflows/evcurve/workflow.py +++ b/atomistics/workflows/evcurve/workflow.py @@ -192,7 +192,7 @@ def analyse_structures(self, output_dict, output=energy_volume_curve_output_keys bulkmodul_eq=ev_prop.get_bulkmodul_eq, energy_eq=ev_prop.get_energy_eq, volume_eq=ev_prop.get_volume_eq, - ).get(*output) + ).get(output=output) return self.fit_dict def get_volume_lst(self): diff --git a/atomistics/workflows/phonons/workflow.py b/atomistics/workflows/phonons/workflow.py index a91378db..cea855aa 100644 --- a/atomistics/workflows/phonons/workflow.py +++ b/atomistics/workflows/phonons/workflow.py @@ -260,7 +260,7 @@ def analyse_structures(self, output_dict, output=phonon_output_keys): total_dos_dict=phono_prop.get_total_dos_dict, dynamical_matrix=phono_prop.get_dynamical_matrix, force_constants=phono_prop.get_force_constants, - ).get(*output) + ).get(output=output) return self._phonopy_dict def get_thermal_properties( @@ -306,7 +306,7 @@ def get_thermal_properties( entropy=phono_thermal.get_entropy, heat_capacity=phono_thermal.get_heat_capacity, volumes=phono_thermal.get_volumes, - ).get(*output) + ).get(output=output) def get_dynamical_matrix(self, npoints=101): """ diff --git a/atomistics/workflows/quasiharmonic.py b/atomistics/workflows/quasiharmonic.py index 592dee59..21155eb0 100644 --- a/atomistics/workflows/quasiharmonic.py +++ b/atomistics/workflows/quasiharmonic.py @@ -126,7 +126,7 @@ def get_thermal_properties( entropy=qh_thermal.get_entropy, heat_capacity=qh_thermal.get_heat_capacity, volumes=qh_thermal.get_volumes, - ).get(*output) + ).get(output=output) def _get_thermal_properties_quantum_mechanical( From 919e34dd935f2da1f2e6d53b26cfcce328c084ca Mon Sep 17 00:00:00 2001 From: pyiron-runner Date: Fri, 5 Jan 2024 15:45:21 +0000 Subject: [PATCH 10/31] Format black --- atomistics/shared/output.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/atomistics/shared/output.py b/atomistics/shared/output.py index 3672d9d7..022549d1 100644 --- a/atomistics/shared/output.py +++ b/atomistics/shared/output.py @@ -15,9 +15,7 @@ def make_output_dataclass(cls_name, output_keys): return make_dataclass( cls_name=cls_name, fields=[(key, callable) for key in output_keys], - namespace={ - "get": lambda self, output: {q: getattr(self, q)() for q in output} - }, + namespace={"get": lambda self, output: {q: getattr(self, q)() for q in output}}, ) From ffcd8818da3d3e9d8fc8046f47f6f37781519fa8 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Fri, 5 Jan 2024 22:58:57 +0100 Subject: [PATCH 11/31] Replace dataclass by abstract class --- atomistics/calculators/ase.py | 32 +-- atomistics/calculators/lammps/calculator.py | 11 +- atomistics/calculators/lammps/helpers.py | 18 +- atomistics/calculators/lammps/output.py | 39 +++ atomistics/calculators/qe.py | 13 +- atomistics/shared/generic.py | 74 ----- atomistics/shared/output.py | 255 +++++++++++++++--- .../workflows/elastic/elastic_moduli.py | 4 +- atomistics/workflows/elastic/workflow.py | 13 +- atomistics/workflows/evcurve/debye.py | 19 +- atomistics/workflows/evcurve/workflow.py | 19 +- atomistics/workflows/phonons/workflow.py | 31 +-- atomistics/workflows/quasiharmonic.py | 19 +- 13 files changed, 310 insertions(+), 237 deletions(-) create mode 100644 atomistics/calculators/lammps/output.py delete mode 100644 atomistics/shared/generic.py diff --git a/atomistics/calculators/ase.py b/atomistics/calculators/ase.py index b07bac0c..49457e43 100644 --- a/atomistics/calculators/ase.py +++ b/atomistics/calculators/ase.py @@ -10,12 +10,13 @@ from atomistics.calculators.interface import get_quantities_from_tasks from atomistics.calculators.wrapper import as_task_dict_evaluator -from atomistics.shared.generic import ( +from atomistics.shared.output import ( + OutputStatic, + OutputMolecularDynamics, static_calculation_output_keys, molecular_dynamics_output_keys, thermal_expansion_output_keys, ) -from atomistics.shared.output import OutputStatic, OutputMolecularDynamics from atomistics.shared.thermal_expansion import get_thermal_expansion_output from atomistics.shared.tqdm_iterator import get_tqdm_iterator @@ -26,7 +27,7 @@ from atomistics.calculators.interface import TaskName -class ASEExecutor(object): +class ASEExecutor(OutputStatic, OutputMolecularDynamics): def __init__(self, ase_structure, ase_calculator): self.structure = ase_structure self.structure.calc = ase_calculator @@ -108,13 +109,9 @@ def calc_static_with_ase( ase_calculator, output=static_calculation_output_keys, ): - ase_exe = ASEExecutor(ase_structure=structure, ase_calculator=ase_calculator) - return OutputStatic( - forces=ase_exe.forces, - energy=ase_exe.energy, - stress=ase_exe.stress, - volume=ase_exe.volume, - ).get(output=output) + return ASEExecutor( + ase_structure=structure, ase_calculator=ase_calculator + ).get_output(output=output) def _calc_md_step_with_ase( @@ -125,18 +122,9 @@ def _calc_md_step_with_ase( cache = {q: [] for q in output} for i in range(int(run / thermo)): dyn.run(thermo) - ase_exe = ASEExecutor(ase_structure=structure, ase_calculator=ase_calculator) - calc_dict = OutputMolecularDynamics( - positions=ase_exe.positions, - cell=ase_exe.cell, - forces=ase_exe.forces, - temperature=ase_exe.temperature, - energy_pot=ase_exe.energy, - energy_tot=ase_exe.energy_tot, - pressure=ase_exe.pressure, - velocities=ase_exe.velocities, - volume=ase_exe.volume, - ).get(output=output) + calc_dict = ASEExecutor( + ase_structure=structure, ase_calculator=ase_calculator + ).get_output(output=output) for k, v in calc_dict.items(): cache[k].append(v) return {q: np.array(cache[q]) for q in output} diff --git a/atomistics/calculators/lammps/calculator.py b/atomistics/calculators/lammps/calculator.py index cd762e3f..46e0a09b 100644 --- a/atomistics/calculators/lammps/calculator.py +++ b/atomistics/calculators/lammps/calculator.py @@ -7,6 +7,7 @@ from pylammpsmpi import LammpsASELibrary from atomistics.calculators.interface import get_quantities_from_tasks +from atomistics.calculators.lammps.output import LammpsOutput from atomistics.calculators.lammps.helpers import ( lammps_calc_md, lammps_run, @@ -28,8 +29,7 @@ LAMMPS_MINIMIZE_VOLUME, ) from atomistics.calculators.wrapper import as_task_dict_evaluator -from atomistics.shared.output import OutputStatic -from atomistics.shared.generic import ( +from atomistics.shared.output import ( static_calculation_output_keys, molecular_dynamics_output_keys, thermal_expansion_output_keys, @@ -135,12 +135,7 @@ 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=output) + result_dict = LammpsOutput(lmp=lmp_instance).get_output(output=output) lammps_shutdown(lmp_instance=lmp_instance, close_instance=lmp is None) return result_dict diff --git a/atomistics/calculators/lammps/helpers.py b/atomistics/calculators/lammps/helpers.py index f0ba668c..5f7849d8 100644 --- a/atomistics/calculators/lammps/helpers.py +++ b/atomistics/calculators/lammps/helpers.py @@ -5,12 +5,12 @@ from pylammpsmpi import LammpsASELibrary from atomistics.calculators.lammps.potential import validate_potential_dataframe -from atomistics.shared.output import OutputMolecularDynamics -from atomistics.shared.thermal_expansion import get_thermal_expansion_output -from atomistics.shared.generic import ( +from atomistics.calculators.lammps.output import LammpsOutput +from atomistics.shared.output import ( molecular_dynamics_output_keys, thermal_expansion_output_keys, ) +from atomistics.shared.thermal_expansion import get_thermal_expansion_output from atomistics.shared.tqdm_iterator import get_tqdm_iterator @@ -51,17 +51,7 @@ 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=output) + return LammpsOutput(lmp=lmp_instance).get_output(output=output) def lammps_calc_md( diff --git a/atomistics/calculators/lammps/output.py b/atomistics/calculators/lammps/output.py new file mode 100644 index 00000000..2a665126 --- /dev/null +++ b/atomistics/calculators/lammps/output.py @@ -0,0 +1,39 @@ +from atomistics.shared.output import OutputMolecularDynamics, OutputStatic + + +class LammpsOutput(OutputMolecularDynamics, OutputStatic): + def __init__(self, lmp): + self._lmp = lmp + + def forces(self): + return self._lmp.interactive_forces_getter() + + def energy(self): + return self._lmp.interactive_energy_pot_getter() + + def stress(self): + return self._lmp.interactive_pressures_getter() + + def volume(self): + return self._lmp.interactive_volume_getter() + + def positions(self): + return self._lmp.interactive_positions_getter() + + def cell(self): + return self._lmp.interactive_cells_getter() + + def temperature(self): + return self._lmp.interactive_temperatures_getter() + + def energy_pot(self): + return self._lmp.interactive_energy_pot_getter() + + def energy_tot(self): + return self._lmp.interactive_energy_tot_getter() + + def pressure(self): + return self._lmp.interactive_pressures_getter() + + def velocities(self): + return self._lmp.interactive_velocities_getter() diff --git a/atomistics/calculators/qe.py b/atomistics/calculators/qe.py index f68c756f..2c6f94d9 100644 --- a/atomistics/calculators/qe.py +++ b/atomistics/calculators/qe.py @@ -5,12 +5,11 @@ from pwtools import io from atomistics.calculators.interface import get_quantities_from_tasks -from atomistics.shared.generic import static_calculation_output_keys -from atomistics.shared.output import OutputStatic +from atomistics.shared.output import OutputStatic, static_calculation_output_keys from atomistics.calculators.wrapper import as_task_dict_evaluator -class QEStaticParser(object): +class QEStaticParser(OutputStatic): def __init__(self, filename): self.parser = io.read_pw_scf(filename=filename, use_alat=True) @@ -208,13 +207,7 @@ def calc_static_with_qe( call_qe_via_ase_command( calculation_name=calculation_name, working_directory=working_directory ) - qe_parser = QEStaticParser(filename=output_file_name) - return OutputStatic( - forces=qe_parser.forces, - energy=qe_parser.energy, - stress=qe_parser.stress, - volume=qe_parser.volume, - ).get(output=output) + return QEStaticParser(filename=output_file_name).get_output(output=output) @as_task_dict_evaluator diff --git a/atomistics/shared/generic.py b/atomistics/shared/generic.py deleted file mode 100644 index 9a9cc286..00000000 --- a/atomistics/shared/generic.py +++ /dev/null @@ -1,74 +0,0 @@ -static_calculation_output_keys = ( - "forces", # np.ndarray (n, 3) [eV / Ang^2] - "energy", # float [eV] - "stress", # np.ndarray (3, 3) [GPa] - "volume", # float [Ang^3] -) - - -molecular_dynamics_output_keys = ( - "positions", # np.ndarray (t, n, 3) [Ang] - "cell", # np.ndarray (t, 3, 3) [Ang] - "forces", # np.ndarray (t, n, 3) [eV / Ang^2] - "temperature", # np.ndarray (t) [K] - "energy_pot", # np.ndarray (t) [eV] - "energy_tot", # np.ndarray (t) [eV] - "pressure", # np.ndarray (t, 3, 3) [GPa] - "velocities", # np.ndarray (t, n, 3) [eV / Ang] - "volume", # np.ndarray (t) [Ang^3] -) - - -thermal_expansion_output_keys = ( - "temperatures", # np.ndarray (T) [K] - "volumes", # np.ndarray (T) [Ang^3] -) - - -thermodynamic_output_keys = ( - "temperatures", # np.ndarray (T) [K] - "volumes", # np.ndarray (T) [Ang^3] - "free_energy", # np.ndarray (T) [eV] - "entropy", # np.ndarray (T) [eV] - "heat_capacity", # np.ndarray (T) [eV] -) - - -energy_volume_curve_output_keys = ( - "energy_eq", # float [eV] - "volume_eq", # float [Ang^3] - "bulkmodul_eq", # float [GPa] - "b_prime_eq", # float - "fit_dict", # dict - "energy", # np.ndarray (V) [eV] - "volume", # np.ndarray (V) [Ang^3] -) - - -elastic_matrix_output_keys = ( - "elastic_matrix", # np.ndarray (6,6) [GPa] - "elastic_matrix_inverse", # np.ndarray (6,6) [GPa] - "bulkmodul_voigt", # float [GPa] - "bulkmodul_reuss", # float [GPa] - "bulkmodul_hill", # float [GPa] - "shearmodul_voigt", # float [GPa] - "shearmodul_reuss", # float [GPa] - "shearmodul_hill", # float [GPa] - "youngsmodul_voigt", # float [GPa] - "youngsmodul_reuss", # float [GPa] - "youngsmodul_hill", # float [GPa] - "poissonsratio_voigt", # float - "poissonsratio_reuss", # float - "poissonsratio_hill", # float - "AVR", # float - "elastic_matrix_eigval", # np.ndarray (6,6) [GPa] -) - - -phonon_output_keys = ( - "mesh_dict", # dict - "band_structure_dict", # dict - "total_dos_dict", # dict - "dynamical_matrix", # dict - "force_constants", # dict -) diff --git a/atomistics/shared/output.py b/atomistics/shared/output.py index 022549d1..0802cfd5 100644 --- a/atomistics/shared/output.py +++ b/atomistics/shared/output.py @@ -1,60 +1,229 @@ -from dataclasses import make_dataclass +from abc import ABC, abstractmethod -from atomistics.shared.generic import ( - static_calculation_output_keys, - molecular_dynamics_output_keys, - thermal_expansion_output_keys, - thermodynamic_output_keys, - energy_volume_curve_output_keys, - elastic_matrix_output_keys, - phonon_output_keys, -) +class Output: + def get_output(self, output): + return {q: getattr(self, q)() for q in output} -def make_output_dataclass(cls_name, output_keys): - return make_dataclass( - cls_name=cls_name, - fields=[(key, callable) for key in output_keys], - namespace={"get": lambda self, output: {q: getattr(self, q)() for q in output}}, - ) + @classmethod + def get_keys(cls): + return [ + k + for k in cls.__dict__.keys() + if k[0] != "_" and k not in ["get_output", "get_keys"] + ] -OutputStatic = make_output_dataclass( - cls_name="OutputStatic", output_keys=static_calculation_output_keys -) +class OutputStatic(ABC, Output): + @abstractmethod + def forces(self): # np.ndarray (n, 3) [eV / Ang^2] + pass + @abstractmethod + def energy(self): # float [eV] + pass -OutputMolecularDynamics = make_output_dataclass( - cls_name="OutputMolecularDynamics", - output_keys=molecular_dynamics_output_keys, -) + @abstractmethod + def stress(self): # np.ndarray (3, 3) [GPa] + pass + @abstractmethod + def volume(self): # float [Ang^3] + pass -OutputThermalExpansion = make_output_dataclass( - cls_name="OutputThermalExpansion", - output_keys=thermal_expansion_output_keys, -) +class OutputMolecularDynamics(ABC, Output): + @abstractmethod + def positions(self): # np.ndarray (t, n, 3) [Ang] + pass -OutputThermodynamic = make_output_dataclass( - cls_name="OutputThermodynamic", - output_keys=thermodynamic_output_keys, -) + @abstractmethod + def cell(self): # np.ndarray (t, 3, 3) [Ang] + pass + @abstractmethod + def forces(self): # np.ndarray (n, 3) [eV / Ang^2] + pass -OutputEnergyVolumeCurve = make_output_dataclass( - cls_name="OutputEnergyVolumeCurve", - output_keys=energy_volume_curve_output_keys, -) + @abstractmethod + def temperature(self): # np.ndarray (t) [K] + pass + @abstractmethod + def energy_pot(self): # np.ndarray (t) [eV] + pass -OutputElastic = make_output_dataclass( - cls_name="OutputElastic", - output_keys=elastic_matrix_output_keys, -) + @abstractmethod + def energy_tot(self): # np.ndarray (t) [eV] + pass + @abstractmethod + def pressure(self): # np.ndarray (t, 3, 3) [GPa] + pass -OutputPhonons = make_output_dataclass( - cls_name="OutputPhonons", - output_keys=phonon_output_keys, -) + @abstractmethod + def velocities(self): # np.ndarray (t, n, 3) [eV / Ang] + pass + + @abstractmethod + def volume(self): # np.ndarray (t) [Ang^3] + pass + + +class OutputThermalExpansion(ABC, Output): + @abstractmethod + def temperatures(self): # np.ndarray (T) [K] + pass + + @abstractmethod + def volumes(self): # np.ndarray (T) [Ang^3] + pass + + +class OutputThermodynamic(ABC, Output): + @abstractmethod + def temperatures(self): # np.ndarray (T) [K] + pass + + @abstractmethod + def volumes(self): # np.ndarray (T) [Ang^3] + pass + + @abstractmethod + def free_energy(self): # np.ndarray (T) [eV] + pass + + @abstractmethod + def entropy(self): # np.ndarray (T) [eV] + pass + + @abstractmethod + def heat_capacity(self): # np.ndarray (T) [eV] + pass + + +class OutputEnergyVolumeCurve(ABC, Output): + @abstractmethod + def energy_eq(self): # float [eV] + pass + + @abstractmethod + def volume_eq(self): # float [Ang^3] + pass + + @abstractmethod + def bulkmodul_eq(self): # float [GPa] + pass + + @abstractmethod + def b_prime_eq(self): # float + pass + + @abstractmethod + def fit_dict(self): # dict + pass + + @abstractmethod + def energy(self): # np.ndarray (V) [eV] + pass + + @abstractmethod + def volume(self): # np.ndarray (V) [Ang^3] + pass + + +class OutputElastic(ABC, Output): + @abstractmethod + def elastic_matrix(self): # np.ndarray (6,6) [GPa] + pass + + @abstractmethod + def elastic_matrix_inverse(self): # np.ndarray (6,6) [GPa] + pass + + @abstractmethod + def bulkmodul_voigt(self): # float [GPa] + pass + + @abstractmethod + def bulkmodul_reuss(self): # float [GPa] + pass + + @abstractmethod + def bulkmodul_hill(self): # float [GPa] + pass + + @abstractmethod + def shearmodul_voigt(self): # float [GPa] + pass + + @abstractmethod + def shearmodul_reuss(self): # float [GPa] + pass + + @abstractmethod + def shearmodul_hill(self): # float [GPa] + pass + + @abstractmethod + def youngsmodul_voigt(self): # float [GPa] + pass + + @abstractmethod + def youngsmodul_reuss(self): # float [GPa] + pass + + @abstractmethod + def youngsmodul_hill(self): # float [GPa] + pass + + @abstractmethod + def poissonsratio_voigt(self): # float + pass + + @abstractmethod + def poissonsratio_reuss(self): # float + pass + + @abstractmethod + def poissonsratio_hill(self): # float + pass + + @abstractmethod + def AVR(self): # float + pass + + @abstractmethod + def elastic_matrix_eigval(self): # np.ndarray (6,6) [GPa] + pass + + +class OutputPhonons(ABC, Output): + @abstractmethod + def mesh_dict(self): # dict + pass + + @abstractmethod + def band_structure_dict(self): # dict + pass + + @abstractmethod + def total_dos_dict(self): # dict + pass + + @abstractmethod + def dynamical_matrix(self): # dict + pass + + @abstractmethod + def force_constants(self): # dict + pass + + +static_calculation_output_keys = OutputStatic.get_keys() +molecular_dynamics_output_keys = OutputMolecularDynamics.get_keys() +thermal_expansion_output_keys = OutputThermalExpansion.get_keys() +thermodynamic_output_keys = OutputMolecularDynamics.get_keys() +energy_volume_curve_output_keys = OutputEnergyVolumeCurve.get_keys() +elastic_matrix_output_keys = OutputElastic.get_keys() +phonon_output_keys = OutputPhonons.get_keys() diff --git a/atomistics/workflows/elastic/elastic_moduli.py b/atomistics/workflows/elastic/elastic_moduli.py index 3fd07e6c..aac10baf 100644 --- a/atomistics/workflows/elastic/elastic_moduli.py +++ b/atomistics/workflows/elastic/elastic_moduli.py @@ -2,6 +2,8 @@ import numpy as np +from atomistics.shared.output import OutputElastic + def get_bulkmodul_voigt(elastic_matrix): return ( @@ -121,7 +123,7 @@ def _hill_approximation(voigt, reuss): return 0.50 * (voigt + reuss) -class ElasticProperties: +class ElasticProperties(OutputElastic): def __init__(self, elastic_matrix): self._elastic_matrix = elastic_matrix diff --git a/atomistics/workflows/elastic/workflow.py b/atomistics/workflows/elastic/workflow.py index 02298da5..a11d8227 100644 --- a/atomistics/workflows/elastic/workflow.py +++ b/atomistics/workflows/elastic/workflow.py @@ -1,7 +1,9 @@ import numpy as np -from atomistics.shared.output import OutputElastic -from atomistics.shared.generic import elastic_matrix_output_keys +from atomistics.shared.output import ( + OutputElastic, + elastic_matrix_output_keys, +) from atomistics.workflows.interface import Workflow from atomistics.workflows.elastic.elastic_moduli import ElasticProperties from atomistics.workflows.elastic.helper import ( @@ -62,7 +64,6 @@ def analyse_structures(self, output_dict, output=elastic_matrix_output_keys): self._data["strain_energy"] = strain_energy self._data["e0"] = ene0 self._data["A2"] = A2 - elastic_prop = ElasticProperties(elastic_matrix=elastic_matrix) - return OutputElastic( - **{k: getattr(elastic_prop, k) for k in elastic_matrix_output_keys} - ).get(output=output) + return ElasticProperties(elastic_matrix=elastic_matrix).get_output( + output=output + ) diff --git a/atomistics/workflows/evcurve/debye.py b/atomistics/workflows/evcurve/debye.py index 34c9d061..6d7e965d 100644 --- a/atomistics/workflows/evcurve/debye.py +++ b/atomistics/workflows/evcurve/debye.py @@ -2,13 +2,15 @@ import scipy.constants import scipy.optimize -from atomistics.shared.output import OutputThermodynamic -from atomistics.shared.generic import thermodynamic_output_keys +from atomistics.shared.output import ( + OutputThermodynamic, + thermodynamic_output_keys, +) from atomistics.workflows.evcurve.fit import interpolate_energy from atomistics.workflows.evcurve.thermo import get_thermo_bulk_model -class DebyeThermalProperties(object): +class DebyeThermalProperties(OutputThermodynamic): def __init__( self, fit_dict, @@ -230,7 +232,7 @@ def get_thermal_properties( num_steps=50, output=thermodynamic_output_keys, ): - debye_thermal = DebyeThermalProperties( + return DebyeThermalProperties( fit_dict=fit_dict, masses=masses, t_min=t_min, @@ -239,11 +241,4 @@ def get_thermal_properties( temperatures=temperatures, constant_volume=constant_volume, num_steps=num_steps, - ) - return OutputThermodynamic( - temperatures=debye_thermal.temperatures, - free_energy=debye_thermal.free_energy, - entropy=debye_thermal.entropy, - heat_capacity=debye_thermal.heat_capacity, - volumes=debye_thermal.volumes, - ).get(output=output) + ).get_output(output=output) diff --git a/atomistics/workflows/evcurve/workflow.py b/atomistics/workflows/evcurve/workflow.py index 5e044e9e..aff3f18c 100644 --- a/atomistics/workflows/evcurve/workflow.py +++ b/atomistics/workflows/evcurve/workflow.py @@ -2,11 +2,11 @@ from ase.atoms import Atoms from collections import OrderedDict -from atomistics.shared.generic import ( +from atomistics.shared.output import ( + OutputEnergyVolumeCurve, thermodynamic_output_keys, energy_volume_curve_output_keys, ) -from atomistics.shared.output import OutputEnergyVolumeCurve from atomistics.workflows.evcurve.fit import EnergyVolumeFit from atomistics.workflows.interface import Workflow from atomistics.workflows.evcurve.debye import get_thermal_properties @@ -98,7 +98,7 @@ def fit_ev_curve(volume_lst, energy_lst, fit_type, fit_order): ).fit_dict -class EnergyVolumeCurveProperties: +class EnergyVolumeCurveProperties(OutputEnergyVolumeCurve): def __init__(self, fit_module): self._fit_module = fit_module @@ -174,7 +174,7 @@ def generate_structures(self): return {"calc_energy": self._structure_dict} def analyse_structures(self, output_dict, output=energy_volume_curve_output_keys): - ev_prop = EnergyVolumeCurveProperties( + self.fit_dict = EnergyVolumeCurveProperties( fit_module=fit_ev_curve_internal( volume_lst=get_volume_lst(structure_dict=self._structure_dict), energy_lst=get_energy_lst( @@ -183,16 +183,7 @@ def analyse_structures(self, output_dict, output=energy_volume_curve_output_keys fit_type=self.fit_type, fit_order=self.fit_order, ) - ) - self._fit_dict = OutputEnergyVolumeCurve( - fit_dict=ev_prop.fit_dict, - energy=ev_prop.energy, - volume=ev_prop.volume, - b_prime_eq=ev_prop.b_prime_eq, - bulkmodul_eq=ev_prop.bulkmodul_eq, - energy_eq=ev_prop.energy_eq, - volume_eq=ev_prop.volume_eq, - ).get(output=output) + ).get_output(output=output) return self.fit_dict def get_volume_lst(self): diff --git a/atomistics/workflows/phonons/workflow.py b/atomistics/workflows/phonons/workflow.py index d416e309..b0e8fc72 100644 --- a/atomistics/workflows/phonons/workflow.py +++ b/atomistics/workflows/phonons/workflow.py @@ -6,11 +6,12 @@ from phonopy.file_IO import write_FORCE_CONSTANTS import structuretoolkit -from atomistics.shared.generic import ( +from atomistics.shared.output import ( + OutputThermodynamic, + OutputPhonons, thermodynamic_output_keys, phonon_output_keys, ) -from atomistics.shared.output import OutputThermodynamic, OutputPhonons from atomistics.workflows.interface import Workflow from atomistics.workflows.phonons.helper import ( get_supercell_matrix, @@ -22,7 +23,7 @@ from atomistics.workflows.phonons.units import VaspToTHz, kJ_mol_to_eV -class PhonopyProperties(object): +class PhonopyProperties(OutputThermodynamic): def __init__( self, phonopy_instance, @@ -121,7 +122,7 @@ def force_constants(self): return self._force_constants -class PhonopyThermalProperties(object): +class PhonopyThermalProperties(OutputPhonons): def __init__(self, phonopy_instance): self._phonopy = phonopy_instance self._thermal_properties = phonopy_instance.get_thermal_properties_dict() @@ -237,7 +238,7 @@ def analyse_structures(self, output_dict, output=phonon_output_keys): output_dict = output_dict["forces"] forces_lst = [output_dict[k] for k in sorted(output_dict.keys())] self.phonopy.forces = forces_lst - phono_prop = PhonopyProperties( + self._phonopy_dict = PhonopyProperties( phonopy_instance=self.phonopy, dos_mesh=self._dos_mesh, shift=None, @@ -253,14 +254,7 @@ def analyse_structures(self, output_dict, output=phonon_output_keys): freq_pitch=None, use_tetrahedron_method=True, npoints=101, - ) - self._phonopy_dict = OutputPhonons( - mesh_dict=phono_prop.mesh_dict, - band_structure_dict=phono_prop.band_structure_dict, - total_dos_dict=phono_prop.total_dos_dict, - dynamical_matrix=phono_prop.dynamical_matrix, - force_constants=phono_prop.force_constants, - ).get(output=output) + ).get_output(output=output) return self._phonopy_dict def get_thermal_properties( @@ -299,14 +293,9 @@ def get_thermal_properties( band_indices=band_indices, is_projection=is_projection, ) - phono_thermal = PhonopyThermalProperties(phonopy_instance=self.phonopy) - return OutputThermodynamic( - temperatures=phono_thermal.temperatures, - free_energy=phono_thermal.free_energy, - entropy=phono_thermal.entropy, - heat_capacity=phono_thermal.heat_capacity, - volumes=phono_thermal.volumes, - ).get(output=output) + return PhonopyThermalProperties(phonopy_instance=self.phonopy).get_output( + output=output + ) def get_dynamical_matrix(self, npoints=101): """ diff --git a/atomistics/workflows/quasiharmonic.py b/atomistics/workflows/quasiharmonic.py index 5d0734e3..f7c33366 100644 --- a/atomistics/workflows/quasiharmonic.py +++ b/atomistics/workflows/quasiharmonic.py @@ -1,7 +1,9 @@ import numpy as np -from atomistics.shared.generic import thermodynamic_output_keys -from atomistics.shared.output import OutputThermodynamic +from atomistics.shared.output import ( + OutputThermodynamic, + thermodynamic_output_keys, +) from atomistics.workflows.evcurve.workflow import ( EnergyVolumeCurveWorkflow, fit_ev_curve, @@ -113,20 +115,13 @@ def get_thermal_properties( not quantum_mechanical ): # heat capacity and entropy are not yet implemented for the classical approach. output = ["free_energy", "temperatures", "volumes"] - qh_thermal = QuasiHarmonicThermalProperties( + return QuasiHarmonicThermalProperties( temperatures=temperatures, thermal_properties_dict=tp_collect_dict, strain_lst=strain_lst, volumes_lst=volume_lst, volumes_selected_lst=vol_lst, - ) - return OutputThermodynamic( - temperatures=qh_thermal.temperatures, - free_energy=qh_thermal.free_energy, - entropy=qh_thermal.entropy, - heat_capacity=qh_thermal.heat_capacity, - volumes=qh_thermal.volumes, - ).get(output=output) + ).get_output(output=output) def _get_thermal_properties_quantum_mechanical( @@ -232,7 +227,7 @@ def _get_thermal_properties_classical( return tp_collect_dict -class QuasiHarmonicThermalProperties(object): +class QuasiHarmonicThermalProperties(OutputThermodynamic): def __init__( self, temperatures, From 33a183b5838fe691b9f003076069ce30b069c692 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Fri, 5 Jan 2024 23:04:18 +0100 Subject: [PATCH 12/31] fix phonon jobs --- atomistics/workflows/phonons/workflow.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/atomistics/workflows/phonons/workflow.py b/atomistics/workflows/phonons/workflow.py index b0e8fc72..d04cb624 100644 --- a/atomistics/workflows/phonons/workflow.py +++ b/atomistics/workflows/phonons/workflow.py @@ -23,7 +23,7 @@ from atomistics.workflows.phonons.units import VaspToTHz, kJ_mol_to_eV -class PhonopyProperties(OutputThermodynamic): +class PhonopyProperties(OutputPhonons): def __init__( self, phonopy_instance, @@ -122,7 +122,7 @@ def force_constants(self): return self._force_constants -class PhonopyThermalProperties(OutputPhonons): +class PhonopyThermalProperties(OutputThermodynamic): def __init__(self, phonopy_instance): self._phonopy = phonopy_instance self._thermal_properties = phonopy_instance.get_thermal_properties_dict() From de46f34e72e64dc9002f5612659386b23c7a15be Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Fri, 5 Jan 2024 23:07:01 +0100 Subject: [PATCH 13/31] fit energy volume curve --- atomistics/workflows/evcurve/workflow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/atomistics/workflows/evcurve/workflow.py b/atomistics/workflows/evcurve/workflow.py index aff3f18c..c732acb3 100644 --- a/atomistics/workflows/evcurve/workflow.py +++ b/atomistics/workflows/evcurve/workflow.py @@ -174,7 +174,7 @@ def generate_structures(self): return {"calc_energy": self._structure_dict} def analyse_structures(self, output_dict, output=energy_volume_curve_output_keys): - self.fit_dict = EnergyVolumeCurveProperties( + self._fit_dict = EnergyVolumeCurveProperties( fit_module=fit_ev_curve_internal( volume_lst=get_volume_lst(structure_dict=self._structure_dict), energy_lst=get_energy_lst( From aeae541074a5465b8f654aa16a7f6079e24d9d40 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Fri, 5 Jan 2024 23:17:29 +0100 Subject: [PATCH 14/31] more fixes --- atomistics/shared/output.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/atomistics/shared/output.py b/atomistics/shared/output.py index 0802cfd5..686290d3 100644 --- a/atomistics/shared/output.py +++ b/atomistics/shared/output.py @@ -223,7 +223,7 @@ def force_constants(self): # dict static_calculation_output_keys = OutputStatic.get_keys() molecular_dynamics_output_keys = OutputMolecularDynamics.get_keys() thermal_expansion_output_keys = OutputThermalExpansion.get_keys() -thermodynamic_output_keys = OutputMolecularDynamics.get_keys() +thermodynamic_output_keys = OutputThermodynamic.get_keys() energy_volume_curve_output_keys = OutputEnergyVolumeCurve.get_keys() elastic_matrix_output_keys = OutputElastic.get_keys() phonon_output_keys = OutputPhonons.get_keys() From 6d2e837bd07740e13d41d205226afda29d227546 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Fri, 5 Jan 2024 23:25:25 +0100 Subject: [PATCH 15/31] return tuple from get_keys() --- atomistics/shared/output.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/atomistics/shared/output.py b/atomistics/shared/output.py index 686290d3..64fba5d8 100644 --- a/atomistics/shared/output.py +++ b/atomistics/shared/output.py @@ -7,11 +7,11 @@ def get_output(self, output): @classmethod def get_keys(cls): - return [ + return tuple([ k for k in cls.__dict__.keys() if k[0] != "_" and k not in ["get_output", "get_keys"] - ] + ]) class OutputStatic(ABC, Output): From 1acb557b969d85332dd78b85da64863ade59295a Mon Sep 17 00:00:00 2001 From: pyiron-runner Date: Fri, 5 Jan 2024 22:30:23 +0000 Subject: [PATCH 16/31] Format black --- atomistics/shared/output.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/atomistics/shared/output.py b/atomistics/shared/output.py index 64fba5d8..c85cf396 100644 --- a/atomistics/shared/output.py +++ b/atomistics/shared/output.py @@ -7,11 +7,13 @@ def get_output(self, output): @classmethod def get_keys(cls): - return tuple([ - k - for k in cls.__dict__.keys() - if k[0] != "_" and k not in ["get_output", "get_keys"] - ]) + return tuple( + [ + k + for k in cls.__dict__.keys() + if k[0] != "_" and k not in ["get_output", "get_keys"] + ] + ) class OutputStatic(ABC, Output): From bbd6e9922937428df9eea4e6337358a3af71efc1 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Fri, 5 Jan 2024 23:36:08 +0100 Subject: [PATCH 17/31] fix thermal expasion --- atomistics/shared/thermal_expansion.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/atomistics/shared/thermal_expansion.py b/atomistics/shared/thermal_expansion.py index b357c9e0..3b65628e 100644 --- a/atomistics/shared/thermal_expansion.py +++ b/atomistics/shared/thermal_expansion.py @@ -1,7 +1,7 @@ from atomistics.shared.output import OutputThermalExpansion -class ThermalExpansionProperties: +class ThermalExpansionProperties(OutputThermalExpansion): def __init__(self, temperatures_lst, volumes_lst): self._temperatures_lst = temperatures_lst self._volumes_lst = volumes_lst @@ -14,10 +14,6 @@ def temperatures(self): def get_thermal_expansion_output(temperatures_lst, volumes_lst, output): - thermal_properties = ThermalExpansionProperties( + return ThermalExpansionProperties( temperatures_lst=temperatures_lst, volumes_lst=volumes_lst - ) - return OutputThermalExpansion( - temperatures=thermal_properties.temperatures, - volumes=thermal_properties.volumes, - ).get(output=output) + ).get_output(output=output) From 9cd9cb9fc1c786c7af216f7909ac2e67324b8188 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Sat, 6 Jan 2024 00:00:07 +0100 Subject: [PATCH 18/31] fix coverage --- tests/test_shared_output.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 tests/test_shared_output.py diff --git a/tests/test_shared_output.py b/tests/test_shared_output.py new file mode 100644 index 00000000..41d99029 --- /dev/null +++ b/tests/test_shared_output.py @@ -0,0 +1,33 @@ +from unittest import TestCase +from atomistics.shared.output import ( + OutputStatic, + OutputMolecularDynamics, + OutputThermalExpansion, + OutputThermodynamic, + OutputEnergyVolumeCurve, + OutputElastic, + OutputPhonons, +) + + +class TestSharedOutput(TestCase): + + def test_return_none(self): + for output_cls in [ + OutputStatic, + OutputMolecularDynamics, + OutputThermalExpansion, + OutputThermodynamic, + OutputEnergyVolumeCurve, + OutputElastic, + OutputPhonons, + ]: + output_cls.__abstractmethods__ = set() + + class Demo(output_cls): + pass + + dm = Demo() + + for func in dm.get_keys(): + self.assertIsNone(getattr(dm, func)()) From ab97e6caf56e5e132222aafb047f35835dee7da8 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Sat, 6 Jan 2024 00:13:03 +0100 Subject: [PATCH 19/31] fix function list --- tests/test_shared_output.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/test_shared_output.py b/tests/test_shared_output.py index 41d99029..80f1741c 100644 --- a/tests/test_shared_output.py +++ b/tests/test_shared_output.py @@ -25,9 +25,12 @@ def test_return_none(self): output_cls.__abstractmethods__ = set() class Demo(output_cls): - pass + def __init__(self): + super().__init__() + self._demo = None dm = Demo() - for func in dm.get_keys(): - self.assertIsNone(getattr(dm, func)()) + for func in dir(dm): + if func[0] != "_" and func not in ['get_keys', 'get_output']: + self.assertIsNone(getattr(dm, func)()) From ddcf101e13a42dfdc32cbd58f8aaa27c935669b4 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Sat, 6 Jan 2024 07:32:32 +0100 Subject: [PATCH 20/31] be more explicit --- atomistics/calculators/ase.py | 12 +++++------- atomistics/calculators/lammps/calculator.py | 18 +++++++++--------- atomistics/calculators/lammps/helpers.py | 10 +++++----- atomistics/calculators/qe.py | 4 ++-- atomistics/shared/output.py | 9 --------- atomistics/workflows/elastic/workflow.py | 7 ++----- atomistics/workflows/evcurve/debye.py | 7 ++----- atomistics/workflows/evcurve/workflow.py | 7 +++---- atomistics/workflows/phonons/workflow.py | 6 ++---- atomistics/workflows/quasiharmonic.py | 13 +++++-------- 10 files changed, 35 insertions(+), 58 deletions(-) diff --git a/atomistics/calculators/ase.py b/atomistics/calculators/ase.py index 49457e43..3a4a623b 100644 --- a/atomistics/calculators/ase.py +++ b/atomistics/calculators/ase.py @@ -13,9 +13,7 @@ from atomistics.shared.output import ( OutputStatic, OutputMolecularDynamics, - static_calculation_output_keys, - molecular_dynamics_output_keys, - thermal_expansion_output_keys, + OutputThermalExpansion, ) from atomistics.shared.thermal_expansion import get_thermal_expansion_output from atomistics.shared.tqdm_iterator import get_tqdm_iterator @@ -107,7 +105,7 @@ def evaluate_with_ase( def calc_static_with_ase( structure, ase_calculator, - output=static_calculation_output_keys, + output=OutputStatic.get_keys(), ): return ASEExecutor( ase_structure=structure, ase_calculator=ase_calculator @@ -140,7 +138,7 @@ def calc_molecular_dynamics_npt_with_ase( pfactor=2e6 * units.GPa * (units.fs**2), temperature=100, externalstress=np.array([0.0, 0.0, 0.0, 0.0, 0.0, 0.0]) * units.bar, - output=molecular_dynamics_output_keys, + output=OutputMolecularDynamics.get_keys(), ): return _calc_md_step_with_ase( dyn=NPT( @@ -174,7 +172,7 @@ def calc_molecular_dynamics_langevin_with_ase( timestep=1 * units.fs, temperature=100, friction=0.002, - output=molecular_dynamics_output_keys, + output=OutputMolecularDynamics.get_keys(), ): return _calc_md_step_with_ase( dyn=Langevin( @@ -224,7 +222,7 @@ def calc_molecular_dynamics_thermal_expansion_with_ase( ttime=100 * units.fs, pfactor=2e6 * units.GPa * (units.fs**2), externalstress=np.array([0.0, 0.0, 0.0, 0.0, 0.0, 0.0]) * units.bar, - output=thermal_expansion_output_keys, + output=OutputThermalExpansion.get_keys(), ): structure_current = structure.copy() temperature_lst = np.arange( diff --git a/atomistics/calculators/lammps/calculator.py b/atomistics/calculators/lammps/calculator.py index 46e0a09b..d43de0b5 100644 --- a/atomistics/calculators/lammps/calculator.py +++ b/atomistics/calculators/lammps/calculator.py @@ -30,9 +30,9 @@ ) from atomistics.calculators.wrapper import as_task_dict_evaluator from atomistics.shared.output import ( - static_calculation_output_keys, - molecular_dynamics_output_keys, - thermal_expansion_output_keys, + OutputStatic, + OutputMolecularDynamics, + OutputThermalExpansion, ) if TYPE_CHECKING: @@ -121,7 +121,7 @@ def calc_static_with_lammps( structure, potential_dataframe, lmp=None, - output=static_calculation_output_keys, + output=OutputStatic.get_keys(), **kwargs, ): template_str = LAMMPS_THERMO_STYLE + "\n" + LAMMPS_THERMO + "\n" + LAMMPS_RUN @@ -152,7 +152,7 @@ def calc_molecular_dynamics_nvt_with_lammps( seed=4928459, dist="gaussian", lmp=None, - output=molecular_dynamics_output_keys, + output=OutputMolecularDynamics.get_keys(), **kwargs, ): init_str = ( @@ -209,7 +209,7 @@ def calc_molecular_dynamics_npt_with_lammps( seed=4928459, dist="gaussian", lmp=None, - output=molecular_dynamics_output_keys, + output=OutputMolecularDynamics.get_keys(), **kwargs, ): init_str = ( @@ -267,7 +267,7 @@ def calc_molecular_dynamics_nph_with_lammps( seed=4928459, dist="gaussian", lmp=None, - output=molecular_dynamics_output_keys, + output=OutputMolecularDynamics.get_keys(), **kwargs, ): init_str = ( @@ -321,7 +321,7 @@ def calc_molecular_dynamics_langevin_with_lammps( seed=4928459, dist="gaussian", lmp=None, - output=molecular_dynamics_output_keys, + output=OutputMolecularDynamics.get_keys(), **kwargs, ): init_str = ( @@ -381,7 +381,7 @@ def calc_molecular_dynamics_thermal_expansion_with_lammps( seed=4928459, dist="gaussian", lmp=None, - output=thermal_expansion_output_keys, + output=OutputThermalExpansion.get_keys(), **kwargs, ): init_str = ( diff --git a/atomistics/calculators/lammps/helpers.py b/atomistics/calculators/lammps/helpers.py index 5f7849d8..f80fd496 100644 --- a/atomistics/calculators/lammps/helpers.py +++ b/atomistics/calculators/lammps/helpers.py @@ -7,8 +7,8 @@ from atomistics.calculators.lammps.potential import validate_potential_dataframe from atomistics.calculators.lammps.output import LammpsOutput from atomistics.shared.output import ( - molecular_dynamics_output_keys, - thermal_expansion_output_keys, + OutputMolecularDynamics, + OutputThermalExpansion, ) from atomistics.shared.thermal_expansion import get_thermal_expansion_output from atomistics.shared.tqdm_iterator import get_tqdm_iterator @@ -47,7 +47,7 @@ def lammps_calc_md_step( lmp_instance, run_str, run, - output=molecular_dynamics_output_keys, + output=OutputMolecularDynamics.get_keys(), ): run_str_rendered = Template(run_str).render(run=run) lmp_instance.interactive_lib_command(run_str_rendered) @@ -59,7 +59,7 @@ def lammps_calc_md( run_str, run, thermo, - output=molecular_dynamics_output_keys, + output=OutputMolecularDynamics.get_keys(), ): results_lst = [ lammps_calc_md_step( @@ -89,7 +89,7 @@ def lammps_thermal_expansion_loop( seed=4928459, dist="gaussian", lmp=None, - output=thermal_expansion_output_keys, + output=OutputThermalExpansion.get_keys(), **kwargs, ): lmp_instance = lammps_run( diff --git a/atomistics/calculators/qe.py b/atomistics/calculators/qe.py index 2c6f94d9..7e509ed3 100644 --- a/atomistics/calculators/qe.py +++ b/atomistics/calculators/qe.py @@ -5,7 +5,7 @@ from pwtools import io from atomistics.calculators.interface import get_quantities_from_tasks -from atomistics.shared.output import OutputStatic, static_calculation_output_keys +from atomistics.shared.output import OutputStatic from atomistics.calculators.wrapper import as_task_dict_evaluator @@ -181,7 +181,7 @@ def calc_static_with_qe( pseudopotentials=None, tstress=True, tprnfor=True, - output=static_calculation_output_keys, + output=OutputStatic.get_keys(), **kwargs, ): input_file_name = os.path.join(working_directory, calculation_name + ".pwi") diff --git a/atomistics/shared/output.py b/atomistics/shared/output.py index c85cf396..fba66203 100644 --- a/atomistics/shared/output.py +++ b/atomistics/shared/output.py @@ -220,12 +220,3 @@ def dynamical_matrix(self): # dict @abstractmethod def force_constants(self): # dict pass - - -static_calculation_output_keys = OutputStatic.get_keys() -molecular_dynamics_output_keys = OutputMolecularDynamics.get_keys() -thermal_expansion_output_keys = OutputThermalExpansion.get_keys() -thermodynamic_output_keys = OutputThermodynamic.get_keys() -energy_volume_curve_output_keys = OutputEnergyVolumeCurve.get_keys() -elastic_matrix_output_keys = OutputElastic.get_keys() -phonon_output_keys = OutputPhonons.get_keys() diff --git a/atomistics/workflows/elastic/workflow.py b/atomistics/workflows/elastic/workflow.py index a11d8227..7f90f81e 100644 --- a/atomistics/workflows/elastic/workflow.py +++ b/atomistics/workflows/elastic/workflow.py @@ -1,9 +1,6 @@ import numpy as np -from atomistics.shared.output import ( - OutputElastic, - elastic_matrix_output_keys, -) +from atomistics.shared.output import OutputElastic from atomistics.workflows.interface import Workflow from atomistics.workflows.elastic.elastic_moduli import ElasticProperties from atomistics.workflows.elastic.helper import ( @@ -42,7 +39,7 @@ def generate_structures(self): ) return {"calc_energy": self._structure_dict} - def analyse_structures(self, output_dict, output=elastic_matrix_output_keys): + def analyse_structures(self, output_dict, output=OutputElastic.get_keys()): """ Args: diff --git a/atomistics/workflows/evcurve/debye.py b/atomistics/workflows/evcurve/debye.py index 6d7e965d..ccc1dc34 100644 --- a/atomistics/workflows/evcurve/debye.py +++ b/atomistics/workflows/evcurve/debye.py @@ -2,10 +2,7 @@ import scipy.constants import scipy.optimize -from atomistics.shared.output import ( - OutputThermodynamic, - thermodynamic_output_keys, -) +from atomistics.shared.output import OutputThermodynamic from atomistics.workflows.evcurve.fit import interpolate_energy from atomistics.workflows.evcurve.thermo import get_thermo_bulk_model @@ -230,7 +227,7 @@ def get_thermal_properties( temperatures=None, constant_volume=False, num_steps=50, - output=thermodynamic_output_keys, + output=OutputThermodynamic.get_keys(), ): return DebyeThermalProperties( fit_dict=fit_dict, diff --git a/atomistics/workflows/evcurve/workflow.py b/atomistics/workflows/evcurve/workflow.py index c732acb3..f77c7c11 100644 --- a/atomistics/workflows/evcurve/workflow.py +++ b/atomistics/workflows/evcurve/workflow.py @@ -4,8 +4,7 @@ from atomistics.shared.output import ( OutputEnergyVolumeCurve, - thermodynamic_output_keys, - energy_volume_curve_output_keys, + OutputThermodynamic, ) from atomistics.workflows.evcurve.fit import EnergyVolumeFit from atomistics.workflows.interface import Workflow @@ -173,7 +172,7 @@ def generate_structures(self): self._structure_dict[1 + np.round(strain, 7)] = basis return {"calc_energy": self._structure_dict} - def analyse_structures(self, output_dict, output=energy_volume_curve_output_keys): + def analyse_structures(self, output_dict, output=OutputEnergyVolumeCurve.get_keys()): self._fit_dict = EnergyVolumeCurveProperties( fit_module=fit_ev_curve_internal( volume_lst=get_volume_lst(structure_dict=self._structure_dict), @@ -215,7 +214,7 @@ def get_thermal_properties( t_step=50, temperatures=None, constant_volume=False, - output=thermodynamic_output_keys, + output=OutputThermodynamic.get_keys(), ): return get_thermal_properties( fit_dict=self.fit_dict, diff --git a/atomistics/workflows/phonons/workflow.py b/atomistics/workflows/phonons/workflow.py index d04cb624..e91f28de 100644 --- a/atomistics/workflows/phonons/workflow.py +++ b/atomistics/workflows/phonons/workflow.py @@ -9,8 +9,6 @@ from atomistics.shared.output import ( OutputThermodynamic, OutputPhonons, - thermodynamic_output_keys, - phonon_output_keys, ) from atomistics.workflows.interface import Workflow from atomistics.workflows.phonons.helper import ( @@ -228,7 +226,7 @@ def _restore_magmoms(self, structure): structure.set_initial_magnetic_moments(magmoms) return structure - def analyse_structures(self, output_dict, output=phonon_output_keys): + def analyse_structures(self, output_dict, output=OutputPhonons.get_keys()): """ Returns: @@ -267,7 +265,7 @@ def get_thermal_properties( pretend_real=False, band_indices=None, is_projection=False, - output=thermodynamic_output_keys, + output=OutputThermodynamic.get_keys(), ): """ Returns thermal properties at constant volume in the given temperature range. Can only be called after job diff --git a/atomistics/workflows/quasiharmonic.py b/atomistics/workflows/quasiharmonic.py index f7c33366..0f3929f7 100644 --- a/atomistics/workflows/quasiharmonic.py +++ b/atomistics/workflows/quasiharmonic.py @@ -1,9 +1,6 @@ import numpy as np -from atomistics.shared.output import ( - OutputThermodynamic, - thermodynamic_output_keys, -) +from atomistics.shared.output import OutputThermodynamic from atomistics.workflows.evcurve.workflow import ( EnergyVolumeCurveWorkflow, fit_ev_curve, @@ -39,7 +36,7 @@ def get_thermal_properties( band_indices=None, is_projection=False, quantum_mechanical=True, - output=thermodynamic_output_keys, + output=OutputThermodynamic.get_keys(), ): """ Returns thermal properties at constant volume in the given temperature range. Can only be called after job @@ -135,7 +132,7 @@ def _get_thermal_properties_quantum_mechanical( pretend_real=False, band_indices=None, is_projection=False, - output=thermodynamic_output_keys, + output=OutputThermodynamic.get_keys(), ): """ Returns thermal properties at constant volume in the given temperature range. Can only be called after job @@ -371,7 +368,7 @@ def get_thermal_properties( band_indices=None, is_projection=False, quantum_mechanical=True, - output=thermodynamic_output_keys, + output=OutputThermodynamic.get_keys(), ): """ Returns thermal properties at constant volume in the given temperature range. Can only be called after job @@ -407,7 +404,7 @@ def get_thermal_properties( band_indices=band_indices, is_projection=is_projection, quantum_mechanical=quantum_mechanical, - output=thermodynamic_output_keys, + output=OutputThermodynamic.get_keys(), ) def get_thermal_expansion( From a6b774f1600c01eabe02979c8c437937d0edc185 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Sat, 6 Jan 2024 07:32:52 +0100 Subject: [PATCH 21/31] black formatting --- atomistics/workflows/evcurve/workflow.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/atomistics/workflows/evcurve/workflow.py b/atomistics/workflows/evcurve/workflow.py index f77c7c11..67904786 100644 --- a/atomistics/workflows/evcurve/workflow.py +++ b/atomistics/workflows/evcurve/workflow.py @@ -172,7 +172,9 @@ def generate_structures(self): self._structure_dict[1 + np.round(strain, 7)] = basis return {"calc_energy": self._structure_dict} - def analyse_structures(self, output_dict, output=OutputEnergyVolumeCurve.get_keys()): + def analyse_structures( + self, output_dict, output=OutputEnergyVolumeCurve.get_keys() + ): self._fit_dict = EnergyVolumeCurveProperties( fit_module=fit_ev_curve_internal( volume_lst=get_volume_lst(structure_dict=self._structure_dict), From 0665c4d15550d574faa4c493a1338c52c6ca9e2d Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Sat, 6 Jan 2024 08:05:41 +0100 Subject: [PATCH 22/31] rename output parameter to output_keys --- atomistics/calculators/ase.py | 28 +++++++++++---------- atomistics/calculators/lammps/calculator.py | 26 +++++++++---------- atomistics/calculators/lammps/helpers.py | 16 ++++++------ atomistics/calculators/qe.py | 6 ++--- atomistics/shared/output.py | 6 ++--- atomistics/shared/thermal_expansion.py | 4 +-- atomistics/workflows/elastic/workflow.py | 6 ++--- atomistics/workflows/evcurve/debye.py | 4 +-- atomistics/workflows/evcurve/workflow.py | 10 ++++---- atomistics/workflows/phonons/workflow.py | 8 +++--- atomistics/workflows/quasiharmonic.py | 24 ++++++++++-------- docs/source/workflows.md | 8 +++--- notebooks/lammps_workflows.ipynb | 8 +++--- tests/test_lammps_md.py | 2 +- tests/test_phonons_lammps.py | 2 +- 15 files changed, 82 insertions(+), 76 deletions(-) diff --git a/atomistics/calculators/ase.py b/atomistics/calculators/ase.py index 3a4a623b..133f7e68 100644 --- a/atomistics/calculators/ase.py +++ b/atomistics/calculators/ase.py @@ -95,7 +95,7 @@ def evaluate_with_ase( return calc_static_with_ase( structure=structure, ase_calculator=ase_calculator, - output=get_quantities_from_tasks(tasks=tasks), + output_keys=get_quantities_from_tasks(tasks=tasks), ) else: raise ValueError("The ASE calculator does not implement:", tasks) @@ -105,27 +105,27 @@ def evaluate_with_ase( def calc_static_with_ase( structure, ase_calculator, - output=OutputStatic.get_keys(), + output_keys=OutputStatic.keys(), ): return ASEExecutor( ase_structure=structure, ase_calculator=ase_calculator - ).get_output(output=output) + ).get_output(output_keys=output_keys) def _calc_md_step_with_ase( - dyn, structure, ase_calculator, temperature, run, thermo, output + dyn, structure, ase_calculator, temperature, run, thermo, output_keys ): structure.calc = ase_calculator MaxwellBoltzmannDistribution(atoms=structure, temperature_K=temperature) - cache = {q: [] for q in output} + cache = {q: [] for q in output_keys} for i in range(int(run / thermo)): dyn.run(thermo) calc_dict = ASEExecutor( ase_structure=structure, ase_calculator=ase_calculator - ).get_output(output=output) + ).get_output(output_keys=output_keys) for k, v in calc_dict.items(): cache[k].append(v) - return {q: np.array(cache[q]) for q in output} + return {q: np.array(cache[q]) for q in output_keys} def calc_molecular_dynamics_npt_with_ase( @@ -138,7 +138,7 @@ def calc_molecular_dynamics_npt_with_ase( pfactor=2e6 * units.GPa * (units.fs**2), temperature=100, externalstress=np.array([0.0, 0.0, 0.0, 0.0, 0.0, 0.0]) * units.bar, - output=OutputMolecularDynamics.get_keys(), + output_keys=OutputMolecularDynamics.keys(), ): return _calc_md_step_with_ase( dyn=NPT( @@ -160,7 +160,7 @@ def calc_molecular_dynamics_npt_with_ase( temperature=temperature, run=run, thermo=thermo, - output=output, + output_keys=output_keys, ) @@ -172,7 +172,7 @@ def calc_molecular_dynamics_langevin_with_ase( timestep=1 * units.fs, temperature=100, friction=0.002, - output=OutputMolecularDynamics.get_keys(), + output_keys=OutputMolecularDynamics.keys(), ): return _calc_md_step_with_ase( dyn=Langevin( @@ -186,7 +186,7 @@ def calc_molecular_dynamics_langevin_with_ase( temperature=temperature, run=run, thermo=thermo, - output=output, + output_keys=output_keys, ) @@ -222,7 +222,7 @@ def calc_molecular_dynamics_thermal_expansion_with_ase( ttime=100 * units.fs, pfactor=2e6 * units.GPa * (units.fs**2), externalstress=np.array([0.0, 0.0, 0.0, 0.0, 0.0, 0.0]) * units.bar, - output=OutputThermalExpansion.get_keys(), + output_keys=OutputThermalExpansion.keys(), ): structure_current = structure.copy() temperature_lst = np.arange( @@ -245,5 +245,7 @@ def calc_molecular_dynamics_thermal_expansion_with_ase( temperature_md_lst.append(result_dict["temperature"][-1]) volume_md_lst.append(result_dict["volume"][-1]) return get_thermal_expansion_output( - temperatures_lst=temperature_md_lst, volumes_lst=volume_md_lst, output=output + temperatures_lst=temperature_md_lst, + volumes_lst=volume_md_lst, + output_keys=output_keys, ) diff --git a/atomistics/calculators/lammps/calculator.py b/atomistics/calculators/lammps/calculator.py index d43de0b5..d48bf7e6 100644 --- a/atomistics/calculators/lammps/calculator.py +++ b/atomistics/calculators/lammps/calculator.py @@ -121,7 +121,7 @@ def calc_static_with_lammps( structure, potential_dataframe, lmp=None, - output=OutputStatic.get_keys(), + output_keys=OutputStatic.keys(), **kwargs, ): template_str = LAMMPS_THERMO_STYLE + "\n" + LAMMPS_THERMO + "\n" + LAMMPS_RUN @@ -135,7 +135,7 @@ def calc_static_with_lammps( lmp=lmp, **kwargs, ) - result_dict = LammpsOutput(lmp=lmp_instance).get_output(output=output) + result_dict = LammpsOutput(lmp=lmp_instance).get_output(output_keys=output_keys) lammps_shutdown(lmp_instance=lmp_instance, close_instance=lmp is None) return result_dict @@ -152,7 +152,7 @@ def calc_molecular_dynamics_nvt_with_lammps( seed=4928459, dist="gaussian", lmp=None, - output=OutputMolecularDynamics.get_keys(), + output_keys=OutputMolecularDynamics.keys(), **kwargs, ): init_str = ( @@ -188,7 +188,7 @@ def calc_molecular_dynamics_nvt_with_lammps( run_str=run_str, run=run, thermo=thermo, - output=output, + output_keys=output_keys, ) lammps_shutdown(lmp_instance=lmp_instance, close_instance=lmp is None) return result_dict @@ -209,7 +209,7 @@ def calc_molecular_dynamics_npt_with_lammps( seed=4928459, dist="gaussian", lmp=None, - output=OutputMolecularDynamics.get_keys(), + output_keys=OutputMolecularDynamics.keys(), **kwargs, ): init_str = ( @@ -248,7 +248,7 @@ def calc_molecular_dynamics_npt_with_lammps( run_str=run_str, run=run, thermo=thermo, - output=output, + output_keys=output_keys, ) lammps_shutdown(lmp_instance=lmp_instance, close_instance=lmp is None) return result_dict @@ -267,7 +267,7 @@ def calc_molecular_dynamics_nph_with_lammps( seed=4928459, dist="gaussian", lmp=None, - output=OutputMolecularDynamics.get_keys(), + output_keys=OutputMolecularDynamics.keys(), **kwargs, ): init_str = ( @@ -303,7 +303,7 @@ def calc_molecular_dynamics_nph_with_lammps( run_str=run_str, run=run, thermo=thermo, - output=output, + output_keys=output_keys, ) lammps_shutdown(lmp_instance=lmp_instance, close_instance=lmp is None) return result_dict @@ -321,7 +321,7 @@ def calc_molecular_dynamics_langevin_with_lammps( seed=4928459, dist="gaussian", lmp=None, - output=OutputMolecularDynamics.get_keys(), + output_keys=OutputMolecularDynamics.keys(), **kwargs, ): init_str = ( @@ -359,7 +359,7 @@ def calc_molecular_dynamics_langevin_with_lammps( run_str=run_str, run=run, thermo=thermo, - output=output, + output_keys=output_keys, ) lammps_shutdown(lmp_instance=lmp_instance, close_instance=lmp is None) return result_dict @@ -381,7 +381,7 @@ def calc_molecular_dynamics_thermal_expansion_with_lammps( seed=4928459, dist="gaussian", lmp=None, - output=OutputThermalExpansion.get_keys(), + output_keys=OutputThermalExpansion.keys(), **kwargs, ): init_str = ( @@ -412,7 +412,7 @@ def calc_molecular_dynamics_thermal_expansion_with_lammps( seed=seed, dist=dist, lmp=lmp, - output=output, + output_keys=output_keys, **kwargs, ) @@ -458,7 +458,7 @@ def evaluate_with_lammps_library( structure=structure, potential_dataframe=potential_dataframe, lmp=lmp, - output=get_quantities_from_tasks(tasks=tasks), + output_keys=get_quantities_from_tasks(tasks=tasks), ) else: raise ValueError("The LAMMPS calculator does not implement:", tasks) diff --git a/atomistics/calculators/lammps/helpers.py b/atomistics/calculators/lammps/helpers.py index f80fd496..a1f32d78 100644 --- a/atomistics/calculators/lammps/helpers.py +++ b/atomistics/calculators/lammps/helpers.py @@ -47,11 +47,11 @@ def lammps_calc_md_step( lmp_instance, run_str, run, - output=OutputMolecularDynamics.get_keys(), + output_keys=OutputMolecularDynamics.keys(), ): run_str_rendered = Template(run_str).render(run=run) lmp_instance.interactive_lib_command(run_str_rendered) - return LammpsOutput(lmp=lmp_instance).get_output(output=output) + return LammpsOutput(lmp=lmp_instance).get_output(output_keys=output_keys) def lammps_calc_md( @@ -59,18 +59,18 @@ def lammps_calc_md( run_str, run, thermo, - output=OutputMolecularDynamics.get_keys(), + output_keys=OutputMolecularDynamics.keys(), ): results_lst = [ lammps_calc_md_step( lmp_instance=lmp_instance, run_str=run_str, run=thermo, - output=output, + output_keys=output_keys, ) for _ in range(run // thermo) ] - return {q: np.array([d[q] for d in results_lst]) for q in output} + return {q: np.array([d[q] for d in results_lst]) for q in output_keys} def lammps_thermal_expansion_loop( @@ -89,7 +89,7 @@ def lammps_thermal_expansion_loop( seed=4928459, dist="gaussian", lmp=None, - output=OutputThermalExpansion.get_keys(), + output_keys=OutputThermalExpansion.keys(), **kwargs, ): lmp_instance = lammps_run( @@ -123,7 +123,9 @@ def lammps_thermal_expansion_loop( temperature_md_lst.append(lmp_instance.interactive_temperatures_getter()) lammps_shutdown(lmp_instance=lmp_instance, close_instance=lmp is None) return get_thermal_expansion_output( - temperatures_lst=temperature_md_lst, volumes_lst=volume_md_lst, output=output + temperatures_lst=temperature_md_lst, + volumes_lst=volume_md_lst, + output_keys=output_keys, ) diff --git a/atomistics/calculators/qe.py b/atomistics/calculators/qe.py index 7e509ed3..0b2fe40e 100644 --- a/atomistics/calculators/qe.py +++ b/atomistics/calculators/qe.py @@ -181,7 +181,7 @@ def calc_static_with_qe( pseudopotentials=None, tstress=True, tprnfor=True, - output=OutputStatic.get_keys(), + output_keys=OutputStatic.keys(), **kwargs, ): input_file_name = os.path.join(working_directory, calculation_name + ".pwi") @@ -207,7 +207,7 @@ def calc_static_with_qe( call_qe_via_ase_command( calculation_name=calculation_name, working_directory=working_directory ) - return QEStaticParser(filename=output_file_name).get_output(output=output) + return QEStaticParser(filename=output_file_name).get_output(output_keys=output_keys) @as_task_dict_evaluator @@ -245,7 +245,7 @@ def evaluate_with_qe( pseudopotentials=pseudopotentials, tstress=tstress, tprnfor=tprnfor, - output=get_quantities_from_tasks(tasks=tasks), + output_keys=get_quantities_from_tasks(tasks=tasks), **kwargs, ) else: diff --git a/atomistics/shared/output.py b/atomistics/shared/output.py index fba66203..9a439854 100644 --- a/atomistics/shared/output.py +++ b/atomistics/shared/output.py @@ -2,11 +2,11 @@ class Output: - def get_output(self, output): - return {q: getattr(self, q)() for q in output} + def get_output(self, output_keys): + return {q: getattr(self, q)() for q in output_keys} @classmethod - def get_keys(cls): + def keys(cls): return tuple( [ k diff --git a/atomistics/shared/thermal_expansion.py b/atomistics/shared/thermal_expansion.py index 3b65628e..9e768dde 100644 --- a/atomistics/shared/thermal_expansion.py +++ b/atomistics/shared/thermal_expansion.py @@ -13,7 +13,7 @@ def temperatures(self): return self._temperatures_lst -def get_thermal_expansion_output(temperatures_lst, volumes_lst, output): +def get_thermal_expansion_output(temperatures_lst, volumes_lst, output_keys): return ThermalExpansionProperties( temperatures_lst=temperatures_lst, volumes_lst=volumes_lst - ).get_output(output=output) + ).get_output(output_keys=output_keys) diff --git a/atomistics/workflows/elastic/workflow.py b/atomistics/workflows/elastic/workflow.py index 7f90f81e..57677b00 100644 --- a/atomistics/workflows/elastic/workflow.py +++ b/atomistics/workflows/elastic/workflow.py @@ -39,12 +39,12 @@ def generate_structures(self): ) return {"calc_energy": self._structure_dict} - def analyse_structures(self, output_dict, output=OutputElastic.get_keys()): + def analyse_structures(self, output_dict, output_keys=OutputElastic.keys()): """ Args: output_dict (dict): - output (tuple): + output_keys (tuple): Returns: @@ -62,5 +62,5 @@ def analyse_structures(self, output_dict, output=OutputElastic.get_keys()): self._data["e0"] = ene0 self._data["A2"] = A2 return ElasticProperties(elastic_matrix=elastic_matrix).get_output( - output=output + output_keys=output_keys ) diff --git a/atomistics/workflows/evcurve/debye.py b/atomistics/workflows/evcurve/debye.py index ccc1dc34..37fdaabb 100644 --- a/atomistics/workflows/evcurve/debye.py +++ b/atomistics/workflows/evcurve/debye.py @@ -227,7 +227,7 @@ def get_thermal_properties( temperatures=None, constant_volume=False, num_steps=50, - output=OutputThermodynamic.get_keys(), + output_keys=OutputThermodynamic.keys(), ): return DebyeThermalProperties( fit_dict=fit_dict, @@ -238,4 +238,4 @@ def get_thermal_properties( temperatures=temperatures, constant_volume=constant_volume, num_steps=num_steps, - ).get_output(output=output) + ).get_output(output_keys=output_keys) diff --git a/atomistics/workflows/evcurve/workflow.py b/atomistics/workflows/evcurve/workflow.py index 67904786..614da134 100644 --- a/atomistics/workflows/evcurve/workflow.py +++ b/atomistics/workflows/evcurve/workflow.py @@ -173,7 +173,7 @@ def generate_structures(self): return {"calc_energy": self._structure_dict} def analyse_structures( - self, output_dict, output=OutputEnergyVolumeCurve.get_keys() + self, output_dict, output_keys=OutputEnergyVolumeCurve.keys() ): self._fit_dict = EnergyVolumeCurveProperties( fit_module=fit_ev_curve_internal( @@ -184,7 +184,7 @@ def analyse_structures( fit_type=self.fit_type, fit_order=self.fit_order, ) - ).get_output(output=output) + ).get_output(output_keys=output_keys) return self.fit_dict def get_volume_lst(self): @@ -202,7 +202,7 @@ def get_thermal_expansion( t_step=t_step, temperatures=temperatures, constant_volume=False, - output=["temperatures", "volumes"], + output_keys=["temperatures", "volumes"], ) return ( thermal_properties_dict["temperatures"], @@ -216,7 +216,7 @@ def get_thermal_properties( t_step=50, temperatures=None, constant_volume=False, - output=OutputThermodynamic.get_keys(), + output_keys=OutputThermodynamic.keys(), ): return get_thermal_properties( fit_dict=self.fit_dict, @@ -226,5 +226,5 @@ def get_thermal_properties( t_step=t_step, temperatures=temperatures, constant_volume=constant_volume, - output=output, + output_keys=output_keys, ) diff --git a/atomistics/workflows/phonons/workflow.py b/atomistics/workflows/phonons/workflow.py index e91f28de..e54337d5 100644 --- a/atomistics/workflows/phonons/workflow.py +++ b/atomistics/workflows/phonons/workflow.py @@ -226,7 +226,7 @@ def _restore_magmoms(self, structure): structure.set_initial_magnetic_moments(magmoms) return structure - def analyse_structures(self, output_dict, output=OutputPhonons.get_keys()): + def analyse_structures(self, output_dict, output_keys=OutputPhonons.keys()): """ Returns: @@ -252,7 +252,7 @@ def analyse_structures(self, output_dict, output=OutputPhonons.get_keys()): freq_pitch=None, use_tetrahedron_method=True, npoints=101, - ).get_output(output=output) + ).get_output(output_keys=output_keys) return self._phonopy_dict def get_thermal_properties( @@ -265,7 +265,7 @@ def get_thermal_properties( pretend_real=False, band_indices=None, is_projection=False, - output=OutputThermodynamic.get_keys(), + output_keys=OutputThermodynamic.keys(), ): """ Returns thermal properties at constant volume in the given temperature range. Can only be called after job @@ -292,7 +292,7 @@ def get_thermal_properties( is_projection=is_projection, ) return PhonopyThermalProperties(phonopy_instance=self.phonopy).get_output( - output=output + output_keys=output_keys ) def get_dynamical_matrix(self, npoints=101): diff --git a/atomistics/workflows/quasiharmonic.py b/atomistics/workflows/quasiharmonic.py index 0f3929f7..559b9bdd 100644 --- a/atomistics/workflows/quasiharmonic.py +++ b/atomistics/workflows/quasiharmonic.py @@ -36,7 +36,7 @@ def get_thermal_properties( band_indices=None, is_projection=False, quantum_mechanical=True, - output=OutputThermodynamic.get_keys(), + output_keys=OutputThermodynamic.keys(), ): """ Returns thermal properties at constant volume in the given temperature range. Can only be called after job @@ -64,7 +64,7 @@ def get_thermal_properties( pretend_real=pretend_real, band_indices=band_indices, is_projection=is_projection, - output=output, + output_keys=output_keys, ) else: if is_projection: @@ -111,14 +111,14 @@ def get_thermal_properties( if ( not quantum_mechanical ): # heat capacity and entropy are not yet implemented for the classical approach. - output = ["free_energy", "temperatures", "volumes"] + output_keys = ["free_energy", "temperatures", "volumes"] return QuasiHarmonicThermalProperties( temperatures=temperatures, thermal_properties_dict=tp_collect_dict, strain_lst=strain_lst, volumes_lst=volume_lst, volumes_selected_lst=vol_lst, - ).get_output(output=output) + ).get_output(output_keys=output_keys) def _get_thermal_properties_quantum_mechanical( @@ -132,7 +132,7 @@ def _get_thermal_properties_quantum_mechanical( pretend_real=False, band_indices=None, is_projection=False, - output=OutputThermodynamic.get_keys(), + output_keys=OutputThermodynamic.keys(), ): """ Returns thermal properties at constant volume in the given temperature range. Can only be called after job @@ -161,7 +161,7 @@ def _get_thermal_properties_quantum_mechanical( pretend_real=pretend_real, band_indices=band_indices, is_projection=is_projection, - output=output, + output_keys=output_keys, ).items() } return tp_collect_dict @@ -344,14 +344,16 @@ def generate_structures(self): ) return task_dict - def analyse_structures(self, output_dict, output=("force_constants", "mesh_dict")): + def analyse_structures( + self, output_dict, output_keys=("force_constants", "mesh_dict") + ): self._eng_internal_dict = output_dict["energy"] phonopy_collect_dict = { strain: phono.analyse_structures( output_dict={ k: v for k, v in output_dict["forces"].items() if strain in k }, - output=output, + output_keys=output_keys, ) for strain, phono in self._phonopy_dict.items() } @@ -368,7 +370,7 @@ def get_thermal_properties( band_indices=None, is_projection=False, quantum_mechanical=True, - output=OutputThermodynamic.get_keys(), + output_keys=OutputThermodynamic.keys(), ): """ Returns thermal properties at constant volume in the given temperature range. Can only be called after job @@ -404,7 +406,7 @@ def get_thermal_properties( band_indices=band_indices, is_projection=is_projection, quantum_mechanical=quantum_mechanical, - output=OutputThermodynamic.get_keys(), + output_keys=OutputThermodynamic.keys(), ) def get_thermal_expansion( @@ -432,6 +434,6 @@ def get_thermal_expansion( band_indices=band_indices, is_projection=is_projection, quantum_mechanical=quantum_mechanical, - output=["free_energy", "temperatures", "volumes"], + output_keys=["free_energy", "temperatures", "volumes"], ) return tp_collect_dict["temperatures"], tp_collect_dict["volumes"] diff --git a/docs/source/workflows.md b/docs/source/workflows.md index c4a87909..87df8ab8 100644 --- a/docs/source/workflows.md +++ b/docs/source/workflows.md @@ -148,7 +148,7 @@ result_dict = calc_molecular_dynamics_langevin_with_lammps( timestep=0.001, seed=4928459, dist="gaussian", - output=("positions", "cell", "forces", "temperature", "energy_pot", "energy_tot", "pressure", "velocities"), + output_keys=("positions", "cell", "forces", "temperature", "energy_pot", "energy_tot", "pressure", "velocities"), ) ``` In addition to the typical LAMMPS input parameters like the atomistic structure `structure` as `ase.atoms.Atoms` object @@ -189,7 +189,7 @@ result_dict = calc_molecular_dynamics_nvt_with_lammps( timestep=0.001, seed=4928459, dist="gaussian", - output=("positions", "cell", "forces", "temperature", "energy_pot", "energy_tot", "pressure", "velocities"), + output_keys=("positions", "cell", "forces", "temperature", "energy_pot", "energy_tot", "pressure", "velocities"), ) ``` In addition to the typical LAMMPS input parameters like the atomistic structure `structure` as `ase.atoms.Atoms` object @@ -232,7 +232,7 @@ result_dict = calc_molecular_dynamics_npt_with_lammps( Pdamp=1.0, seed=4928459, dist="gaussian", - output=("positions", "cell", "forces", "temperature", "energy_pot", "energy_tot", "pressure", "velocities"), + output_keys=("positions", "cell", "forces", "temperature", "energy_pot", "energy_tot", "pressure", "velocities"), ) ``` The input parameters for the isothermal-isobaric ensemble (npt) are the same as for the canonical ensemble (nvt) plus: @@ -264,7 +264,7 @@ result_dict = calc_molecular_dynamics_nph_with_lammps( Pdamp=1.0, seed=4928459, dist="gaussian", - output=("positions", "cell", "forces", "temperature", "energy_pot", "energy_tot", "pressure", "velocities"), + output_keys=("positions", "cell", "forces", "temperature", "energy_pot", "energy_tot", "pressure", "velocities"), ) ``` diff --git a/notebooks/lammps_workflows.ipynb b/notebooks/lammps_workflows.ipynb index 292f41b3..1dd1e43e 100644 --- a/notebooks/lammps_workflows.ipynb +++ b/notebooks/lammps_workflows.ipynb @@ -348,7 +348,7 @@ " timestep=0.001,\n", " seed=4928459,\n", " dist=\"gaussian\",\n", - " output=(\"positions\", \"cell\", \"forces\", \"temperature\", \"energy_pot\", \"energy_tot\", \"pressure\", \"velocities\"),\n", + " output_keys=(\"positions\", \"cell\", \"forces\", \"temperature\", \"energy_pot\", \"energy_tot\", \"pressure\", \"velocities\"),\n", ")" ] }, @@ -409,7 +409,7 @@ " timestep=0.001,\n", " seed=4928459,\n", " dist=\"gaussian\",\n", - " output=(\"positions\", \"cell\", \"forces\", \"temperature\", \"energy_pot\", \"energy_tot\", \"pressure\"),\n", + " output_keys=(\"positions\", \"cell\", \"forces\", \"temperature\", \"energy_pot\", \"energy_tot\", \"pressure\"),\n", ")" ] }, @@ -472,7 +472,7 @@ " Pdamp=1.0,\n", " seed=4928459,\n", " dist=\"gaussian\",\n", - " output=(\"positions\", \"cell\", \"forces\", \"temperature\", \"energy_pot\", \"energy_tot\", \"pressure\"),\n", + " output_keys=(\"positions\", \"cell\", \"forces\", \"temperature\", \"energy_pot\", \"energy_tot\", \"pressure\"),\n", ")" ] }, @@ -524,7 +524,7 @@ " Pdamp=1.0,\n", " seed=4928459,\n", " dist=\"gaussian\",\n", - " output=(\"positions\", \"cell\", \"forces\", \"temperature\", \"energy_pot\", \"energy_tot\", \"pressure\"),\n", + " output_keys=(\"positions\", \"cell\", \"forces\", \"temperature\", \"energy_pot\", \"energy_tot\", \"pressure\"),\n", ")" ] }, diff --git a/tests/test_lammps_md.py b/tests/test_lammps_md.py index 391591c7..772f0282 100644 --- a/tests/test_lammps_md.py +++ b/tests/test_lammps_md.py @@ -71,7 +71,7 @@ def test_lammps_md_nvt_select(self): seed=4928459, dist="gaussian", lmp=None, - output=("temperature",), + output_keys=("temperature",), ) self.assertEqual(len(result_dict.keys()), 1) self.assertEqual(result_dict["temperature"].shape, (10, )) diff --git a/tests/test_phonons_lammps.py b/tests/test_phonons_lammps.py index 221ba14f..7e1b451e 100644 --- a/tests/test_phonons_lammps.py +++ b/tests/test_phonons_lammps.py @@ -94,7 +94,7 @@ def test_calc_phonons(self): pretend_real=False, band_indices=None, is_projection=False, - output=["temperatures", "free_energy"] + output_keys=["temperatures", "free_energy"] ) self.assertEqual(len(thermal_dict.keys()), 2) self.assertEqual(thermal_dict["temperatures"][0], 1.0) From fc1234941b4a4f90e24d5089e34eb0a8a3e66213 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Sat, 6 Jan 2024 08:08:33 +0100 Subject: [PATCH 23/31] Update output.py --- atomistics/shared/output.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/atomistics/shared/output.py b/atomistics/shared/output.py index 9a439854..4ae7603d 100644 --- a/atomistics/shared/output.py +++ b/atomistics/shared/output.py @@ -11,7 +11,7 @@ def keys(cls): [ k for k in cls.__dict__.keys() - if k[0] != "_" and k not in ["get_output", "get_keys"] + if k[0] != "_" and k not in ["get_output", "keys"] ] ) From f100af09e188378b7dc26066e24e0b4f8e52b3f9 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Sat, 6 Jan 2024 08:10:01 +0100 Subject: [PATCH 24/31] fix test --- tests/test_shared_output.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_shared_output.py b/tests/test_shared_output.py index 80f1741c..48108119 100644 --- a/tests/test_shared_output.py +++ b/tests/test_shared_output.py @@ -32,5 +32,5 @@ def __init__(self): dm = Demo() for func in dir(dm): - if func[0] != "_" and func not in ['get_keys', 'get_output']: + if func[0] != "_" and func not in ['keys', 'get_output']: self.assertIsNone(getattr(dm, func)()) From cb2c5c6aba662c6eb38de2c5c751d43291b62607 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Sat, 6 Jan 2024 08:22:22 +0100 Subject: [PATCH 25/31] fix signature test --- tests/test_lammps_md.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_lammps_md.py b/tests/test_lammps_md.py index 772f0282..63d7acc6 100644 --- a/tests/test_lammps_md.py +++ b/tests/test_lammps_md.py @@ -174,7 +174,7 @@ def test_lammps_md_langevin_all(self): def test_calc_molecular_dynamics_signature(self): self.assertEqual( - inspect.signature(calc_molecular_dynamics_nvt_with_lammps).parameters["output"].default, + inspect.signature(calc_molecular_dynamics_nvt_with_lammps).parameters["output_keys"].default, ( "positions", "cell", From c23292acddce46c0f1d2d80399bec46c987783e6 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Sat, 6 Jan 2024 08:22:34 +0100 Subject: [PATCH 26/31] use type hints in abstract class --- atomistics/shared/output.py | 98 +++++++++++++++++++------------------ 1 file changed, 50 insertions(+), 48 deletions(-) diff --git a/atomistics/shared/output.py b/atomistics/shared/output.py index 4ae7603d..c1d0e8b3 100644 --- a/atomistics/shared/output.py +++ b/atomistics/shared/output.py @@ -1,5 +1,7 @@ from abc import ABC, abstractmethod +import numpy as np + class Output: def get_output(self, output_keys): @@ -18,205 +20,205 @@ def keys(cls): class OutputStatic(ABC, Output): @abstractmethod - def forces(self): # np.ndarray (n, 3) [eV / Ang^2] + def forces(self) -> np.ndarray: # (n, 3) [eV / Ang^2] pass @abstractmethod - def energy(self): # float [eV] + def energy(self) -> float: # [eV] pass @abstractmethod - def stress(self): # np.ndarray (3, 3) [GPa] + def stress(self) -> np.ndarray: # (3, 3) [GPa] pass @abstractmethod - def volume(self): # float [Ang^3] + def volume(self) -> float: # [Ang^3] pass class OutputMolecularDynamics(ABC, Output): @abstractmethod - def positions(self): # np.ndarray (t, n, 3) [Ang] + def positions(self) -> np.ndarray: # (t, n, 3) [Ang] pass @abstractmethod - def cell(self): # np.ndarray (t, 3, 3) [Ang] + def cell(self) -> np.ndarray: # (t, 3, 3) [Ang] pass @abstractmethod - def forces(self): # np.ndarray (n, 3) [eV / Ang^2] + def forces(self) -> np.ndarray: # (n, 3) [eV / Ang^2] pass @abstractmethod - def temperature(self): # np.ndarray (t) [K] + def temperature(self) -> np.ndarray: # (t) [K] pass @abstractmethod - def energy_pot(self): # np.ndarray (t) [eV] + def energy_pot(self) -> np.ndarray: # (t) [eV] pass @abstractmethod - def energy_tot(self): # np.ndarray (t) [eV] + def energy_tot(self) -> np.ndarray: # (t) [eV] pass @abstractmethod - def pressure(self): # np.ndarray (t, 3, 3) [GPa] + def pressure(self) -> np.ndarray: # (t, 3, 3) [GPa] pass @abstractmethod - def velocities(self): # np.ndarray (t, n, 3) [eV / Ang] + def velocities(self) -> np.ndarray: # (t, n, 3) [eV / Ang] pass @abstractmethod - def volume(self): # np.ndarray (t) [Ang^3] + def volume(self) -> np.ndarray: # (t) [Ang^3] pass class OutputThermalExpansion(ABC, Output): @abstractmethod - def temperatures(self): # np.ndarray (T) [K] + def temperatures(self) -> np.ndarray: # (T) [K] pass @abstractmethod - def volumes(self): # np.ndarray (T) [Ang^3] + def volumes(self) -> np.ndarray: # (T) [Ang^3] pass class OutputThermodynamic(ABC, Output): @abstractmethod - def temperatures(self): # np.ndarray (T) [K] + def temperatures(self) -> np.ndarray: # (T) [K] pass @abstractmethod - def volumes(self): # np.ndarray (T) [Ang^3] + def volumes(self) -> np.ndarray: # (T) [Ang^3] pass @abstractmethod - def free_energy(self): # np.ndarray (T) [eV] + def free_energy(self) -> np.ndarray: # (T) [eV] pass @abstractmethod - def entropy(self): # np.ndarray (T) [eV] + def entropy(self) -> np.ndarray: # (T) [eV] pass @abstractmethod - def heat_capacity(self): # np.ndarray (T) [eV] + def heat_capacity(self) -> np.ndarray: # (T) [eV] pass class OutputEnergyVolumeCurve(ABC, Output): @abstractmethod - def energy_eq(self): # float [eV] + def energy_eq(self) -> float: # float [eV] pass @abstractmethod - def volume_eq(self): # float [Ang^3] + def volume_eq(self) -> float: # float [Ang^3] pass @abstractmethod - def bulkmodul_eq(self): # float [GPa] + def bulkmodul_eq(self) -> float: # float [GPa] pass @abstractmethod - def b_prime_eq(self): # float + def b_prime_eq(self) -> float: # float pass @abstractmethod - def fit_dict(self): # dict + def fit_dict(self) -> dict: # dict pass @abstractmethod - def energy(self): # np.ndarray (V) [eV] + def energy(self) -> np.ndarray: # (V) [eV] pass @abstractmethod - def volume(self): # np.ndarray (V) [Ang^3] + def volume(self) -> np.ndarray: # (V) [Ang^3] pass class OutputElastic(ABC, Output): @abstractmethod - def elastic_matrix(self): # np.ndarray (6,6) [GPa] + def elastic_matrix(self) -> np.ndarray: # (6,6) [GPa] pass @abstractmethod - def elastic_matrix_inverse(self): # np.ndarray (6,6) [GPa] + def elastic_matrix_inverse(self) -> np.ndarray: # (6,6) [GPa] pass @abstractmethod - def bulkmodul_voigt(self): # float [GPa] + def bulkmodul_voigt(self) -> float: # [GPa] pass @abstractmethod - def bulkmodul_reuss(self): # float [GPa] + def bulkmodul_reuss(self) -> float: # [GPa] pass @abstractmethod - def bulkmodul_hill(self): # float [GPa] + def bulkmodul_hill(self) -> float: # [GPa] pass @abstractmethod - def shearmodul_voigt(self): # float [GPa] + def shearmodul_voigt(self) -> float: # [GPa] pass @abstractmethod - def shearmodul_reuss(self): # float [GPa] + def shearmodul_reuss(self) -> float: # [GPa] pass @abstractmethod - def shearmodul_hill(self): # float [GPa] + def shearmodul_hill(self) -> float: # [GPa] pass @abstractmethod - def youngsmodul_voigt(self): # float [GPa] + def youngsmodul_voigt(self) -> float: # [GPa] pass @abstractmethod - def youngsmodul_reuss(self): # float [GPa] + def youngsmodul_reuss(self) -> float: # [GPa] pass @abstractmethod - def youngsmodul_hill(self): # float [GPa] + def youngsmodul_hill(self) -> float: # [GPa] pass @abstractmethod - def poissonsratio_voigt(self): # float + def poissonsratio_voigt(self) -> float: pass @abstractmethod - def poissonsratio_reuss(self): # float + def poissonsratio_reuss(self) -> float: pass @abstractmethod - def poissonsratio_hill(self): # float + def poissonsratio_hill(self) -> float: pass @abstractmethod - def AVR(self): # float + def AVR(self) -> float: pass @abstractmethod - def elastic_matrix_eigval(self): # np.ndarray (6,6) [GPa] + def elastic_matrix_eigval(self) -> np.ndarray: # (6,6) [GPa] pass class OutputPhonons(ABC, Output): @abstractmethod - def mesh_dict(self): # dict + def mesh_dict(self) -> dict: pass @abstractmethod - def band_structure_dict(self): # dict + def band_structure_dict(self) -> dict: pass @abstractmethod - def total_dos_dict(self): # dict + def total_dos_dict(self) -> dict: pass @abstractmethod - def dynamical_matrix(self): # dict + def dynamical_matrix(self) -> dict: pass @abstractmethod - def force_constants(self): # dict + def force_constants(self) -> dict: pass From 2b2681c7ad8cf2fcfaf6c44223e1bc06715182d7 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Sat, 6 Jan 2024 10:21:03 +0100 Subject: [PATCH 27/31] updates --- docs/source/materialproperties.md | 6 +++--- docs/source/workflows.md | 4 ++-- notebooks/lammps_workflows.ipynb | 4 ++-- notebooks/thermal_expansion_with_lammps.ipynb | 6 +++--- tests/test_evcurve_ase_abinit.py | 2 +- tests/test_evcurve_ase_emt.py | 2 +- tests/test_evcurve_ase_gpaw.py | 2 +- tests/test_evcurve_ase_lammps.py | 2 +- tests/test_evcurve_ase_matgl.py | 2 +- tests/test_evcurve_lammps.py | 2 +- tests/test_quasiharmonic_ase_emt.py | 4 ++-- tests/test_quasiharmonic_ase_matgl.py | 4 ++-- tests/test_quasiharmonic_lammps.py | 4 ++-- 13 files changed, 22 insertions(+), 22 deletions(-) diff --git a/docs/source/materialproperties.md b/docs/source/materialproperties.md index adc6dfc8..f41feeea 100644 --- a/docs/source/materialproperties.md +++ b/docs/source/materialproperties.md @@ -382,7 +382,7 @@ import numpy as np workflow_ev.analyse_structures(output_dict=result_dict) thermal_properties_dict = workflow_ev.get_thermal_properties( temperatures=np.arange(1, 1500, 50), - output=["temperatures", "volumes"], + output_keys=["temperatures", "volumes"], ) temperatures_ev, volume_ev = thermal_properties_dict["temperatures"], thermal_properties_dict["volumes"] ``` @@ -461,7 +461,7 @@ and `get_thermal_properties()` functions: workflow_qh.analyse_structures(output_dict=result_dict) thermal_properties_dict_qm = workflow_qh.get_thermal_properties( temperatures=np.arange(1, 1500, 50), - output=["temperatures", "volumes"], + output_keys=["temperatures", "volumes"], quantum_mechanical=True ) temperatures_qh_qm, volume_qh_qm = thermal_properties_dict_qm["temperatures"], thermal_properties_dict_qm["volumes"] @@ -471,7 +471,7 @@ Here the extension `_qm` indicates that the quantum-mechanical harmonic oszillat ``` thermal_properties_dict_cl = workflow_qh.get_thermal_properties( temperatures=np.arange(1, 1500, 50), - output=["temperatures", "volumes"], + output_keys=["temperatures", "volumes"], quantum_mechanical=False, ) temperatures_qh_cl, volume_qh_cl = thermal_properties_dict_cl["temperatures"], thermal_properties_dict_cl["volumes"] diff --git a/docs/source/workflows.md b/docs/source/workflows.md index 4726ba4e..985af6e3 100644 --- a/docs/source/workflows.md +++ b/docs/source/workflows.md @@ -109,7 +109,7 @@ thermal_properties_dict = workflow.get_thermal_properties( t_step=50, temperatures=None, constant_volume=False, - output=["temperatures", "volumes"], + output_keys=["temperatures", "volumes"], ) temperatures, volumes = thermal_properties_dict["temperatures"], thermal_properties_dict["volumes"] ``` @@ -639,7 +639,7 @@ tp_dict = workflow.get_thermal_properties( band_indices=None, is_projection=False, quantum_mechanical=True, - output=["temperatures", "volumes"], + output_keys=["temperatures", "volumes"], ) temperatures, volumes = tp_dict["temperatures"], tp_dict["volumes"] ``` diff --git a/notebooks/lammps_workflows.ipynb b/notebooks/lammps_workflows.ipynb index 6892ac16..8e0f779d 100644 --- a/notebooks/lammps_workflows.ipynb +++ b/notebooks/lammps_workflows.ipynb @@ -277,7 +277,7 @@ " t_max=1500, \n", " t_step=50, \n", " constant_volume=False,\n", - " output=[\"temperatures\", \"volumes\"],\n", + " output_keys=[\"temperatures\", \"volumes\"],\n", ")\n", "temperatures, volumes = thermal_properties_dict[\"temperatures\"], thermal_properties_dict[\"volumes\"]" ] @@ -3078,7 +3078,7 @@ " band_indices=None,\n", " is_projection=False,\n", " quantum_mechanical=True,\n", - " output=[\"temperatures\", \"volumes\"],\n", + " output_keys=[\"temperatures\", \"volumes\"],\n", ")\n", "temperatures, volumes = tp_dict[\"temperatures\"], tp_dict[\"volumes\"]" ] diff --git a/notebooks/thermal_expansion_with_lammps.ipynb b/notebooks/thermal_expansion_with_lammps.ipynb index fae701bb..3290f43f 100644 --- a/notebooks/thermal_expansion_with_lammps.ipynb +++ b/notebooks/thermal_expansion_with_lammps.ipynb @@ -350,7 +350,7 @@ "workflow_ev.analyse_structures(output_dict=result_dict)\n", "thermal_properties_dict = workflow_ev.get_thermal_properties(\n", " temperatures=np.arange(1, 1500, 50),\n", - " output=[\"temperatures\", \"volumes\"],\n", + " output_keys=[\"temperatures\", \"volumes\"],\n", ")\n", "temperatures_ev, volume_ev = thermal_properties_dict[\"temperatures\"], thermal_properties_dict[\"volumes\"]" ] @@ -504,7 +504,7 @@ "workflow_qh.analyse_structures(output_dict=result_dict)\n", "thermal_properties_dict_qm = workflow_qh.get_thermal_properties(\n", " temperatures=np.arange(1, 1500, 50),\n", - " output=[\"temperatures\", \"volumes\"],\n", + " output_keys=[\"temperatures\", \"volumes\"],\n", " quantum_mechanical=True\n", ")\n", "temperatures_qh_qm, volume_qh_qm = thermal_properties_dict_qm[\"temperatures\"], thermal_properties_dict_qm[\"volumes\"]" @@ -527,7 +527,7 @@ "source": [ "thermal_properties_dict_cl = workflow_qh.get_thermal_properties(\n", " temperatures=np.arange(1, 1500, 50),\n", - " output=[\"temperatures\", \"volumes\"],\n", + " output_keys=[\"temperatures\", \"volumes\"],\n", " quantum_mechanical=False,\n", ")\n", "temperatures_qh_cl, volume_qh_cl = thermal_properties_dict_cl[\"temperatures\"], thermal_properties_dict_cl[\"volumes\"]" diff --git a/tests/test_evcurve_ase_abinit.py b/tests/test_evcurve_ase_abinit.py index 559f9095..2cee3879 100644 --- a/tests/test_evcurve_ase_abinit.py +++ b/tests/test_evcurve_ase_abinit.py @@ -58,7 +58,7 @@ def test_calc_evcurve(self): fit_dict = workflow.analyse_structures(output_dict=result_dict) thermal_properties_dict = workflow.get_thermal_properties( temperatures=[100, 1000], - output=["temperatures", "volumes"] + output_keys=["temperatures", "volumes"] ) temperatures_ev, volumes_ev = thermal_properties_dict["temperatures"], thermal_properties_dict["volumes"] self.assertTrue(all(validate_fitdict(fit_dict=fit_dict))) diff --git a/tests/test_evcurve_ase_emt.py b/tests/test_evcurve_ase_emt.py index 4d24a39e..015cb630 100644 --- a/tests/test_evcurve_ase_emt.py +++ b/tests/test_evcurve_ase_emt.py @@ -32,7 +32,7 @@ def test_calc_evcurve(self): fit_dict = workflow.analyse_structures(output_dict=result_dict) thermal_properties_dict = workflow.get_thermal_properties( temperatures=[100, 1000], - output=["temperatures", "volumes"] + output_keys=["temperatures", "volumes"] ) temperatures_ev, volumes_ev = thermal_properties_dict["temperatures"], thermal_properties_dict["volumes"] self.assertTrue(np.isclose(fit_dict['volume_eq'], 63.72747170239313)) diff --git a/tests/test_evcurve_ase_gpaw.py b/tests/test_evcurve_ase_gpaw.py index 0016d14c..f124a055 100644 --- a/tests/test_evcurve_ase_gpaw.py +++ b/tests/test_evcurve_ase_gpaw.py @@ -40,7 +40,7 @@ def test_calc_evcurve(self): fit_dict = workflow.analyse_structures(output_dict=result_dict) thermal_properties_dict = workflow.get_thermal_properties( temperatures=[100, 1000], - output=["temperatures", "volumes"] + output_keys=["temperatures", "volumes"] ) temperatures_ev, volumes_ev = thermal_properties_dict["temperatures"], thermal_properties_dict["volumes"] self.assertTrue(np.isclose(fit_dict['volume_eq'], 66.44252286131331, atol=1e-04)) diff --git a/tests/test_evcurve_ase_lammps.py b/tests/test_evcurve_ase_lammps.py index fb319237..2caaef5a 100644 --- a/tests/test_evcurve_ase_lammps.py +++ b/tests/test_evcurve_ase_lammps.py @@ -58,7 +58,7 @@ def test_calc_evcurve(self): fit_dict = workflow.analyse_structures(output_dict=result_dict) thermal_properties_dict = workflow.get_thermal_properties( temperatures=[100, 1000], - output=["temperatures", "volumes"] + output_keys=["temperatures", "volumes"] ) temperatures_ev, volumes_ev = thermal_properties_dict["temperatures"], thermal_properties_dict["volumes"] self.assertTrue(np.isclose(fit_dict['volume_eq'], 66.29753110818122)) diff --git a/tests/test_evcurve_ase_matgl.py b/tests/test_evcurve_ase_matgl.py index f468d26b..12947c0f 100644 --- a/tests/test_evcurve_ase_matgl.py +++ b/tests/test_evcurve_ase_matgl.py @@ -47,7 +47,7 @@ def test_calc_evcurve(self): fit_dict = workflow.analyse_structures(output_dict=result_dict) thermal_properties_dict = workflow.get_thermal_properties( temperatures=[100, 1000], - output=["temperatures", "volumes"] + output_keys=["temperatures", "volumes"] ) temperatures_ev, volumes_ev = thermal_properties_dict["temperatures"], thermal_properties_dict["volumes"] self.assertTrue(np.isclose(fit_dict['volume_eq'], 66.56048874824006, atol=1e-04)) diff --git a/tests/test_evcurve_lammps.py b/tests/test_evcurve_lammps.py index 8b114fa0..a57da44d 100644 --- a/tests/test_evcurve_lammps.py +++ b/tests/test_evcurve_lammps.py @@ -49,7 +49,7 @@ def test_calc_evcurve(self): fit_dict = workflow.analyse_structures(output_dict=result_dict) thermal_properties_dict = workflow.get_thermal_properties( temperatures=[100, 1000], - output=["temperatures", "volumes"] + output_keys=["temperatures", "volumes"] ) temperatures_ev, volumes_ev = thermal_properties_dict["temperatures"], thermal_properties_dict["volumes"] self.assertTrue(np.isclose(fit_dict['volume_eq'], 66.43019853103964)) diff --git a/tests/test_quasiharmonic_ase_emt.py b/tests/test_quasiharmonic_ase_emt.py index 3eabff28..e8e6df64 100644 --- a/tests/test_quasiharmonic_ase_emt.py +++ b/tests/test_quasiharmonic_ase_emt.py @@ -38,13 +38,13 @@ def test_calc_phonons(self): tp_collect_dict = workflow.get_thermal_properties(t_min=1, t_max=1500, t_step=50, temperatures=None) thermal_properties_dict = workflow.get_thermal_properties( temperatures=[100, 1000], - output=["temperatures", "volumes"], + output_keys=["temperatures", "volumes"], quantum_mechanical=True ) temperatures_qh_qm, volumes_qh_qm = thermal_properties_dict["temperatures"], thermal_properties_dict["volumes"] thermal_properties_dict = workflow.get_thermal_properties( temperatures=[100, 1000], - output=["temperatures", "volumes"], + output_keys=["temperatures", "volumes"], quantum_mechanical=False ) temperatures_qh_cl, volumes_qh_cl = thermal_properties_dict["temperatures"], thermal_properties_dict["volumes"] diff --git a/tests/test_quasiharmonic_ase_matgl.py b/tests/test_quasiharmonic_ase_matgl.py index 110a3feb..910cf551 100644 --- a/tests/test_quasiharmonic_ase_matgl.py +++ b/tests/test_quasiharmonic_ase_matgl.py @@ -50,13 +50,13 @@ def test_calc_phonons(self): tp_collect_dict = workflow.get_thermal_properties(t_min=1, t_max=501, t_step=50, temperatures=None) thermal_properties_dict = workflow.get_thermal_properties( temperatures=[100, 500], - output=["temperatures", "volumes"], + output_keys=["temperatures", "volumes"], quantum_mechanical=True ) temperatures_qh_qm, volumes_qh_qm = thermal_properties_dict["temperatures"], thermal_properties_dict["volumes"] thermal_properties_dict = workflow.get_thermal_properties( temperatures=[100, 500], - output=["temperatures", "volumes"], + output_keys=["temperatures", "volumes"], quantum_mechanical=False ) temperatures_qh_cl, volumes_qh_cl = thermal_properties_dict["temperatures"], thermal_properties_dict["volumes"] diff --git a/tests/test_quasiharmonic_lammps.py b/tests/test_quasiharmonic_lammps.py index ebaff5a0..1d1380b2 100644 --- a/tests/test_quasiharmonic_lammps.py +++ b/tests/test_quasiharmonic_lammps.py @@ -71,13 +71,13 @@ def test_calc_phonons(self): self.assertTrue(tp_collect_dict["volumes"][0] > 66.7) thermal_properties_dict = workflow.get_thermal_properties( temperatures=[100, 1000], - output=["temperatures", "volumes"], + output_keys=["temperatures", "volumes"], quantum_mechanical=True ) temperatures_qh_qm, volumes_qh_qm = thermal_properties_dict["temperatures"], thermal_properties_dict["volumes"] thermal_properties_dict = workflow.get_thermal_properties( temperatures=[100, 1000], - output=["temperatures", "volumes"], + output_keys=["temperatures", "volumes"], quantum_mechanical=False ) temperatures_qh_cl, volumes_qh_cl = thermal_properties_dict["temperatures"], thermal_properties_dict["volumes"] From c183384661ce3b894604d41598a763fd2af59a76 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Sun, 7 Jan 2024 10:44:45 +0100 Subject: [PATCH 28/31] introduce output properties and rename output classes --- atomistics/calculators/ase.py | 17 +++- atomistics/calculators/lammps/output.py | 11 +++ atomistics/calculators/qe.py | 8 +- atomistics/shared/output.py | 50 +++++++++++- atomistics/shared/thermal_expansion.py | 6 +- .../workflows/elastic/elastic_moduli.py | 79 ++++++++++--------- atomistics/workflows/elastic/workflow.py | 4 +- atomistics/workflows/evcurve/debye.py | 9 ++- atomistics/workflows/evcurve/workflow.py | 11 ++- atomistics/workflows/phonons/workflow.py | 18 ++++- atomistics/workflows/quasiharmonic.py | 9 ++- 11 files changed, 163 insertions(+), 59 deletions(-) diff --git a/atomistics/calculators/ase.py b/atomistics/calculators/ase.py index 133f7e68..118ddcb1 100644 --- a/atomistics/calculators/ase.py +++ b/atomistics/calculators/ase.py @@ -25,43 +25,54 @@ from atomistics.calculators.interface import TaskName -class ASEExecutor(OutputStatic, OutputMolecularDynamics): +class ASEOutput(OutputStatic, OutputMolecularDynamics): def __init__(self, ase_structure, ase_calculator): self.structure = ase_structure self.structure.calc = ase_calculator + @property def forces(self): return self.structure.get_forces() + @property def energy(self): return self.structure.get_potential_energy() + @property def energy_pot(self): return self.structure.get_potential_energy() + @property def energy_tot(self): return ( self.structure.get_potential_energy() + self.structure.get_kinetic_energy() ) + @property def stress(self): return self.structure.get_stress(voigt=False) + @property def pressure(self): return self.structure.get_stress(voigt=False) + @property def cell(self): return self.structure.get_cell() + @property def positions(self): return self.structure.get_positions() + @property def velocities(self): return self.structure.get_velocities() + @property def temperature(self): return self.structure.get_temperature() + @property def volume(self): return self.structure.get_volume() @@ -107,7 +118,7 @@ def calc_static_with_ase( ase_calculator, output_keys=OutputStatic.keys(), ): - return ASEExecutor( + return ASEOutput( ase_structure=structure, ase_calculator=ase_calculator ).get_output(output_keys=output_keys) @@ -120,7 +131,7 @@ def _calc_md_step_with_ase( cache = {q: [] for q in output_keys} for i in range(int(run / thermo)): dyn.run(thermo) - calc_dict = ASEExecutor( + calc_dict = ASEOutput( ase_structure=structure, ase_calculator=ase_calculator ).get_output(output_keys=output_keys) for k, v in calc_dict.items(): diff --git a/atomistics/calculators/lammps/output.py b/atomistics/calculators/lammps/output.py index 2a665126..13cf8776 100644 --- a/atomistics/calculators/lammps/output.py +++ b/atomistics/calculators/lammps/output.py @@ -5,35 +5,46 @@ class LammpsOutput(OutputMolecularDynamics, OutputStatic): def __init__(self, lmp): self._lmp = lmp + @property def forces(self): return self._lmp.interactive_forces_getter() + @property def energy(self): return self._lmp.interactive_energy_pot_getter() + @property def stress(self): return self._lmp.interactive_pressures_getter() + @property def volume(self): return self._lmp.interactive_volume_getter() + @property def positions(self): return self._lmp.interactive_positions_getter() + @property def cell(self): return self._lmp.interactive_cells_getter() + @property def temperature(self): return self._lmp.interactive_temperatures_getter() + @property def energy_pot(self): return self._lmp.interactive_energy_pot_getter() + @property def energy_tot(self): return self._lmp.interactive_energy_tot_getter() + @property def pressure(self): return self._lmp.interactive_pressures_getter() + @property def velocities(self): return self._lmp.interactive_velocities_getter() diff --git a/atomistics/calculators/qe.py b/atomistics/calculators/qe.py index 0b2fe40e..b4228b9c 100644 --- a/atomistics/calculators/qe.py +++ b/atomistics/calculators/qe.py @@ -9,19 +9,23 @@ from atomistics.calculators.wrapper import as_task_dict_evaluator -class QEStaticParser(OutputStatic): +class QEOutputStatic(OutputStatic): def __init__(self, filename): self.parser = io.read_pw_scf(filename=filename, use_alat=True) + @property def forces(self): return self.parser.forces + @property def energy(self): return self.parser.etot + @property def stress(self): return self.parser.stress + @property def volume(self): return self.parser.volume @@ -207,7 +211,7 @@ def calc_static_with_qe( call_qe_via_ase_command( calculation_name=calculation_name, working_directory=working_directory ) - return QEStaticParser(filename=output_file_name).get_output(output_keys=output_keys) + return QEOutputStatic(filename=output_file_name).get_output(output_keys=output_keys) @as_task_dict_evaluator diff --git a/atomistics/shared/output.py b/atomistics/shared/output.py index c1d0e8b3..bbf42f25 100644 --- a/atomistics/shared/output.py +++ b/atomistics/shared/output.py @@ -5,7 +5,7 @@ class Output: def get_output(self, output_keys): - return {q: getattr(self, q)() for q in output_keys} + return {q: getattr(self, q) for q in output_keys} @classmethod def keys(cls): @@ -19,206 +19,254 @@ def keys(cls): class OutputStatic(ABC, Output): + @property @abstractmethod def forces(self) -> np.ndarray: # (n, 3) [eV / Ang^2] pass + @property @abstractmethod def energy(self) -> float: # [eV] pass + @property @abstractmethod def stress(self) -> np.ndarray: # (3, 3) [GPa] pass + @property @abstractmethod def volume(self) -> float: # [Ang^3] pass class OutputMolecularDynamics(ABC, Output): + @property @abstractmethod def positions(self) -> np.ndarray: # (t, n, 3) [Ang] pass + @property @abstractmethod def cell(self) -> np.ndarray: # (t, 3, 3) [Ang] pass + @property @abstractmethod def forces(self) -> np.ndarray: # (n, 3) [eV / Ang^2] pass + @property @abstractmethod def temperature(self) -> np.ndarray: # (t) [K] pass + @property @abstractmethod def energy_pot(self) -> np.ndarray: # (t) [eV] pass + @property @abstractmethod def energy_tot(self) -> np.ndarray: # (t) [eV] pass + @property @abstractmethod def pressure(self) -> np.ndarray: # (t, 3, 3) [GPa] pass + @property @abstractmethod def velocities(self) -> np.ndarray: # (t, n, 3) [eV / Ang] pass + @property @abstractmethod def volume(self) -> np.ndarray: # (t) [Ang^3] pass class OutputThermalExpansion(ABC, Output): + @property @abstractmethod def temperatures(self) -> np.ndarray: # (T) [K] pass + @property @abstractmethod def volumes(self) -> np.ndarray: # (T) [Ang^3] pass class OutputThermodynamic(ABC, Output): + @property @abstractmethod def temperatures(self) -> np.ndarray: # (T) [K] pass + @property @abstractmethod def volumes(self) -> np.ndarray: # (T) [Ang^3] pass + @property @abstractmethod def free_energy(self) -> np.ndarray: # (T) [eV] pass + @property @abstractmethod def entropy(self) -> np.ndarray: # (T) [eV] pass + @property @abstractmethod def heat_capacity(self) -> np.ndarray: # (T) [eV] pass class OutputEnergyVolumeCurve(ABC, Output): + @property @abstractmethod def energy_eq(self) -> float: # float [eV] pass + @property @abstractmethod def volume_eq(self) -> float: # float [Ang^3] pass + @property @abstractmethod def bulkmodul_eq(self) -> float: # float [GPa] pass + @property @abstractmethod def b_prime_eq(self) -> float: # float pass + @property @abstractmethod def fit_dict(self) -> dict: # dict pass + @property @abstractmethod def energy(self) -> np.ndarray: # (V) [eV] pass + @property @abstractmethod def volume(self) -> np.ndarray: # (V) [Ang^3] pass class OutputElastic(ABC, Output): + @property @abstractmethod def elastic_matrix(self) -> np.ndarray: # (6,6) [GPa] pass + @property @abstractmethod def elastic_matrix_inverse(self) -> np.ndarray: # (6,6) [GPa] pass + @property @abstractmethod def bulkmodul_voigt(self) -> float: # [GPa] pass + @property @abstractmethod def bulkmodul_reuss(self) -> float: # [GPa] pass + @property @abstractmethod def bulkmodul_hill(self) -> float: # [GPa] pass + @property @abstractmethod def shearmodul_voigt(self) -> float: # [GPa] pass + @property @abstractmethod def shearmodul_reuss(self) -> float: # [GPa] pass + @property @abstractmethod def shearmodul_hill(self) -> float: # [GPa] pass + @property @abstractmethod def youngsmodul_voigt(self) -> float: # [GPa] pass + @property @abstractmethod def youngsmodul_reuss(self) -> float: # [GPa] pass + @property @abstractmethod def youngsmodul_hill(self) -> float: # [GPa] pass + @property @abstractmethod def poissonsratio_voigt(self) -> float: pass + @property @abstractmethod def poissonsratio_reuss(self) -> float: pass + @property @abstractmethod def poissonsratio_hill(self) -> float: pass + @property @abstractmethod def AVR(self) -> float: pass + @property @abstractmethod def elastic_matrix_eigval(self) -> np.ndarray: # (6,6) [GPa] pass class OutputPhonons(ABC, Output): + @property @abstractmethod def mesh_dict(self) -> dict: pass + @property @abstractmethod def band_structure_dict(self) -> dict: pass + @property @abstractmethod def total_dos_dict(self) -> dict: pass + @property @abstractmethod def dynamical_matrix(self) -> dict: pass + @property @abstractmethod def force_constants(self) -> dict: pass diff --git a/atomistics/shared/thermal_expansion.py b/atomistics/shared/thermal_expansion.py index 9e768dde..540d3000 100644 --- a/atomistics/shared/thermal_expansion.py +++ b/atomistics/shared/thermal_expansion.py @@ -1,19 +1,21 @@ from atomistics.shared.output import OutputThermalExpansion -class ThermalExpansionProperties(OutputThermalExpansion): +class ThermalExpansionOutputWrapper(OutputThermalExpansion): def __init__(self, temperatures_lst, volumes_lst): self._temperatures_lst = temperatures_lst self._volumes_lst = volumes_lst + @property def volumes(self): return self._volumes_lst + @property def temperatures(self): return self._temperatures_lst def get_thermal_expansion_output(temperatures_lst, volumes_lst, output_keys): - return ThermalExpansionProperties( + return ThermalExpansionOutputWrapper( temperatures_lst=temperatures_lst, volumes_lst=volumes_lst ).get_output(output_keys=output_keys) diff --git a/atomistics/workflows/elastic/elastic_moduli.py b/atomistics/workflows/elastic/elastic_moduli.py index aac10baf..ac8caec6 100644 --- a/atomistics/workflows/elastic/elastic_moduli.py +++ b/atomistics/workflows/elastic/elastic_moduli.py @@ -1,4 +1,4 @@ -from functools import cache +from functools import cached_property import numpy as np @@ -123,96 +123,97 @@ def _hill_approximation(voigt, reuss): return 0.50 * (voigt + reuss) -class ElasticProperties(OutputElastic): +class ElasticMatrixOutput(OutputElastic): def __init__(self, elastic_matrix): self._elastic_matrix = elastic_matrix + @property def elastic_matrix(self): return self._elastic_matrix - @cache + @cached_property def elastic_matrix_inverse(self): - return get_elastic_matrix_inverse(elastic_matrix=self.elastic_matrix()) + return get_elastic_matrix_inverse(elastic_matrix=self.elastic_matrix) - @cache + @cached_property def bulkmodul_voigt(self): - return get_bulkmodul_voigt(elastic_matrix=self.elastic_matrix()) + return get_bulkmodul_voigt(elastic_matrix=self.elastic_matrix) - @cache + @cached_property def shearmodul_voigt(self): - return get_shearmodul_voigt(elastic_matrix=self.elastic_matrix()) + return get_shearmodul_voigt(elastic_matrix=self.elastic_matrix) - @cache + @cached_property def bulkmodul_reuss(self): - return get_bulkmodul_reuss(elastic_matrix_inverse=self.elastic_matrix_inverse()) + return get_bulkmodul_reuss(elastic_matrix_inverse=self.elastic_matrix_inverse) - @cache + @cached_property def shearmodul_reuss(self): return get_shearmodul_reuss( - elastic_matrix_inverse=self.elastic_matrix_inverse() + elastic_matrix_inverse=self.elastic_matrix_inverse ) - @cache + @cached_property def bulkmodul_hill(self): return get_bulkmodul_hill( - bulkmodul_voigt=self.bulkmodul_voigt(), - bulkmodul_reuss=self.bulkmodul_reuss(), + bulkmodul_voigt=self.bulkmodul_voigt, + bulkmodul_reuss=self.bulkmodul_reuss, ) - @cache + @cached_property def shearmodul_hill(self): return get_shearmodul_hill( - shearmodul_voigt=self.shearmodul_voigt(), - shearmodul_reuss=self.shearmodul_reuss(), + shearmodul_voigt=self.shearmodul_voigt, + shearmodul_reuss=self.shearmodul_reuss, ) - @cache + @cached_property def youngsmodul_voigt(self): return get_youngsmodul_voigt( - bulkmodul_voigt=self.bulkmodul_voigt(), - shearmodul_voigt=self.shearmodul_voigt(), + bulkmodul_voigt=self.bulkmodul_voigt, + shearmodul_voigt=self.shearmodul_voigt, ) - @cache + @cached_property def poissonsratio_voigt(self): return get_poissonsratio_voigt( - bulkmodul_voigt=self.bulkmodul_voigt(), - shearmodul_voigt=self.shearmodul_voigt(), + bulkmodul_voigt=self.bulkmodul_voigt, + shearmodul_voigt=self.shearmodul_voigt, ) - @cache + @cached_property def youngsmodul_reuss(self): return get_youngsmodul_reuss( - bulkmodul_reuss=self.bulkmodul_reuss(), - shearmodul_reuss=self.shearmodul_reuss(), + bulkmodul_reuss=self.bulkmodul_reuss, + shearmodul_reuss=self.shearmodul_reuss, ) - @cache + @cached_property def poissonsratio_reuss(self): return get_poissonsratio_reuss( - bulkmodul_reuss=self.bulkmodul_reuss(), - shearmodul_reuss=self.shearmodul_reuss(), + bulkmodul_reuss=self.bulkmodul_reuss, + shearmodul_reuss=self.shearmodul_reuss, ) - @cache + @cached_property def youngsmodul_hill(self): return get_youngsmodul_hill( - bulkmodul_hill=self.bulkmodul_hill(), shearmodul_hill=self.shearmodul_hill() + bulkmodul_hill=self.bulkmodul_hill, shearmodul_hill=self.shearmodul_hill ) - @cache + @cached_property def poissonsratio_hill(self): return get_poissonsratio_hill( - bulkmodul_hill=self.bulkmodul_hill(), shearmodul_hill=self.shearmodul_hill() + bulkmodul_hill=self.bulkmodul_hill, shearmodul_hill=self.shearmodul_hill ) - @cache + @cached_property def AVR(self): return get_AVR( - shearmodul_voigt=self.shearmodul_voigt(), - shearmodul_reuss=self.shearmodul_reuss(), + shearmodul_voigt=self.shearmodul_voigt, + shearmodul_reuss=self.shearmodul_reuss, ) - @cache + @cached_property def elastic_matrix_eigval(self): - return get_elastic_matrix_eigval(elastic_matrix=self.elastic_matrix()) + return get_elastic_matrix_eigval(elastic_matrix=self.elastic_matrix) diff --git a/atomistics/workflows/elastic/workflow.py b/atomistics/workflows/elastic/workflow.py index 57677b00..6f408dd7 100644 --- a/atomistics/workflows/elastic/workflow.py +++ b/atomistics/workflows/elastic/workflow.py @@ -2,7 +2,7 @@ from atomistics.shared.output import OutputElastic from atomistics.workflows.interface import Workflow -from atomistics.workflows.elastic.elastic_moduli import ElasticProperties +from atomistics.workflows.elastic.elastic_moduli import ElasticMatrixOutput from atomistics.workflows.elastic.helper import ( generate_structures_helper, analyse_structures_helper, @@ -61,6 +61,6 @@ def analyse_structures(self, output_dict, output_keys=OutputElastic.keys()): self._data["strain_energy"] = strain_energy self._data["e0"] = ene0 self._data["A2"] = A2 - return ElasticProperties(elastic_matrix=elastic_matrix).get_output( + return ElasticMatrixOutput(elastic_matrix=elastic_matrix).get_output( output_keys=output_keys ) diff --git a/atomistics/workflows/evcurve/debye.py b/atomistics/workflows/evcurve/debye.py index 37fdaabb..0e8bb522 100644 --- a/atomistics/workflows/evcurve/debye.py +++ b/atomistics/workflows/evcurve/debye.py @@ -7,7 +7,7 @@ from atomistics.workflows.evcurve.thermo import get_thermo_bulk_model -class DebyeThermalProperties(OutputThermodynamic): +class DebyeOutputThermodynamic(OutputThermodynamic): def __init__( self, fit_dict, @@ -31,15 +31,18 @@ def __init__( ) self._constant_volume = constant_volume + @property def free_energy(self): return ( self._pes.get_free_energy_p() - self._debye_model.interpolate(volumes=self._pes.get_minimum_energy_path()) ) / self._pes.num_atoms + @property def temperatures(self): return self._temperatures + @property def entropy(self): if not self._constant_volume: return ( @@ -54,6 +57,7 @@ def entropy(self): * self._pes.get_entropy_v() ) + @property def heat_capacity(self): if not self._constant_volume: heat_capacity = ( @@ -69,6 +73,7 @@ def heat_capacity(self): ) return np.array(heat_capacity.tolist() + [np.nan, np.nan]) + @property def volumes(self): if not self._constant_volume: return self._pes.get_minimum_energy_path() @@ -229,7 +234,7 @@ def get_thermal_properties( num_steps=50, output_keys=OutputThermodynamic.keys(), ): - return DebyeThermalProperties( + return DebyeOutputThermodynamic( fit_dict=fit_dict, masses=masses, t_min=t_min, diff --git a/atomistics/workflows/evcurve/workflow.py b/atomistics/workflows/evcurve/workflow.py index e0459a69..88540c35 100644 --- a/atomistics/workflows/evcurve/workflow.py +++ b/atomistics/workflows/evcurve/workflow.py @@ -97,28 +97,35 @@ def fit_ev_curve(volume_lst, energy_lst, fit_type, fit_order): ).fit_dict -class EnergyVolumeCurveProperties(OutputEnergyVolumeCurve): +class EnergyVolumeCurveOutputWrapper(OutputEnergyVolumeCurve): def __init__(self, fit_module): self._fit_module = fit_module + @property def volume_eq(self): return self._fit_module.fit_dict["volume_eq"] + @property def energy_eq(self): return self._fit_module.fit_dict["energy_eq"] + @property def bulkmodul_eq(self): return self._fit_module.fit_dict["bulkmodul_eq"] + @property def b_prime_eq(self): return self._fit_module.fit_dict["b_prime_eq"] + @property def volume(self): return self._fit_module.fit_dict["volume"] + @property def energy(self): return self._fit_module.fit_dict["energy"] + @property def fit_dict(self): return { k: self._fit_module.fit_dict[k] @@ -175,7 +182,7 @@ def generate_structures(self): def analyse_structures( self, output_dict, output_keys=OutputEnergyVolumeCurve.keys() ): - self._fit_dict = EnergyVolumeCurveProperties( + self._fit_dict = EnergyVolumeCurveOutputWrapper( fit_module=fit_ev_curve_internal( volume_lst=get_volume_lst(structure_dict=self._structure_dict), energy_lst=get_energy_lst( diff --git a/atomistics/workflows/phonons/workflow.py b/atomistics/workflows/phonons/workflow.py index e54337d5..802b5e99 100644 --- a/atomistics/workflows/phonons/workflow.py +++ b/atomistics/workflows/phonons/workflow.py @@ -21,7 +21,7 @@ from atomistics.workflows.phonons.units import VaspToTHz, kJ_mol_to_eV -class PhonopyProperties(OutputPhonons): +class PhonopyOutput(OutputPhonons): def __init__( self, phonopy_instance, @@ -76,6 +76,7 @@ def _calc_force_constants(self): ) self._force_constants = self._phonopy.force_constants + @property def mesh_dict(self): if self._force_constants is None: self._calc_force_constants() @@ -92,11 +93,13 @@ def mesh_dict(self): self._mesh_dict = self._phonopy.get_mesh_dict() return self._mesh_dict + @property def band_structure_dict(self): if self._band_structure_dict is None: self._calc_band_structure() return self._band_structure_dict + @property def total_dos_dict(self): if self._total_dos is None: self._phonopy.run_total_dos( @@ -109,34 +112,41 @@ def total_dos_dict(self): self._total_dos = self._phonopy.get_total_dos_dict() return self._total_dos + @property def dynamical_matrix(self): if self._band_structure_dict is None: self._calc_band_structure() return self._phonopy.dynamical_matrix.dynamical_matrix + @property def force_constants(self): if self._force_constants is None: self._calc_force_constants() return self._force_constants -class PhonopyThermalProperties(OutputThermodynamic): +class PhonopyOutputThermodynamic(OutputThermodynamic): def __init__(self, phonopy_instance): self._phonopy = phonopy_instance self._thermal_properties = phonopy_instance.get_thermal_properties_dict() + @property def free_energy(self): return self._thermal_properties["free_energy"] * kJ_mol_to_eV + @property def temperatures(self): return self._thermal_properties["temperatures"] + @property def entropy(self): return self._thermal_properties["entropy"] + @property def heat_capacity(self): return self._thermal_properties["heat_capacity"] + @property def volumes(self): return np.array( [self._phonopy.unitcell.get_volume()] @@ -236,7 +246,7 @@ def analyse_structures(self, output_dict, output_keys=OutputPhonons.keys()): output_dict = output_dict["forces"] forces_lst = [output_dict[k] for k in sorted(output_dict.keys())] self.phonopy.forces = forces_lst - self._phonopy_dict = PhonopyProperties( + self._phonopy_dict = PhonopyOutput( phonopy_instance=self.phonopy, dos_mesh=self._dos_mesh, shift=None, @@ -291,7 +301,7 @@ def get_thermal_properties( band_indices=band_indices, is_projection=is_projection, ) - return PhonopyThermalProperties(phonopy_instance=self.phonopy).get_output( + return PhonopyOutputThermodynamic(phonopy_instance=self.phonopy).get_output( output_keys=output_keys ) diff --git a/atomistics/workflows/quasiharmonic.py b/atomistics/workflows/quasiharmonic.py index a78697ac..061bd439 100644 --- a/atomistics/workflows/quasiharmonic.py +++ b/atomistics/workflows/quasiharmonic.py @@ -112,7 +112,7 @@ def get_thermal_properties( not quantum_mechanical ): # heat capacity and entropy are not yet implemented for the classical approach. output_keys = ["free_energy", "temperatures", "volumes"] - return QuasiHarmonicThermalProperties( + return QuasiHarmonicOutputThermodynamic( temperatures=temperatures, thermal_properties_dict=tp_collect_dict, strain_lst=strain_lst, @@ -224,7 +224,7 @@ def _get_thermal_properties_classical( return tp_collect_dict -class QuasiHarmonicThermalProperties(OutputThermodynamic): +class QuasiHarmonicOutputThermodynamic(OutputThermodynamic): def __init__( self, temperatures, @@ -255,18 +255,23 @@ def get_property(self, thermal_property): ] ) + @property def free_energy(self): return self.get_property(thermal_property="free_energy") + @property def temperatures(self): return self._temperatures + @property def entropy(self): return self.get_property(thermal_property="entropy") + @property def heat_capacity(self): return self.get_property(thermal_property="heat_capacity") + @property def volumes(self): return self._volumes_selected_lst From d39057979ee90e621525ad63d63fa6184f85efff Mon Sep 17 00:00:00 2001 From: pyiron-runner Date: Sun, 7 Jan 2024 09:46:06 +0000 Subject: [PATCH 29/31] Format black --- atomistics/calculators/ase.py | 6 +++--- atomistics/workflows/elastic/elastic_moduli.py | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/atomistics/calculators/ase.py b/atomistics/calculators/ase.py index 118ddcb1..2b602b70 100644 --- a/atomistics/calculators/ase.py +++ b/atomistics/calculators/ase.py @@ -118,9 +118,9 @@ def calc_static_with_ase( ase_calculator, output_keys=OutputStatic.keys(), ): - return ASEOutput( - ase_structure=structure, ase_calculator=ase_calculator - ).get_output(output_keys=output_keys) + return ASEOutput(ase_structure=structure, ase_calculator=ase_calculator).get_output( + output_keys=output_keys + ) def _calc_md_step_with_ase( diff --git a/atomistics/workflows/elastic/elastic_moduli.py b/atomistics/workflows/elastic/elastic_moduli.py index ac8caec6..14eedb6b 100644 --- a/atomistics/workflows/elastic/elastic_moduli.py +++ b/atomistics/workflows/elastic/elastic_moduli.py @@ -149,9 +149,7 @@ def bulkmodul_reuss(self): @cached_property def shearmodul_reuss(self): - return get_shearmodul_reuss( - elastic_matrix_inverse=self.elastic_matrix_inverse - ) + return get_shearmodul_reuss(elastic_matrix_inverse=self.elastic_matrix_inverse) @cached_property def bulkmodul_hill(self): From d0f55edfd933afee9b8b119e42a3eacd3d28e31d Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Sun, 7 Jan 2024 10:49:12 +0100 Subject: [PATCH 30/31] fix test --- tests/test_shared_output.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_shared_output.py b/tests/test_shared_output.py index 48108119..cda97425 100644 --- a/tests/test_shared_output.py +++ b/tests/test_shared_output.py @@ -33,4 +33,4 @@ def __init__(self): for func in dir(dm): if func[0] != "_" and func not in ['keys', 'get_output']: - self.assertIsNone(getattr(dm, func)()) + self.assertIsNone(getattr(dm, func)) From 803c889c70656758657654b7ae2f7512360967c6 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Sun, 7 Jan 2024 11:16:53 +0100 Subject: [PATCH 31/31] Add Docstrings to output class --- atomistics/shared/output.py | 75 ++++++++++++++++++++++++++++++++----- 1 file changed, 65 insertions(+), 10 deletions(-) diff --git a/atomistics/shared/output.py b/atomistics/shared/output.py index bbf42f25..ad1d4cfb 100644 --- a/atomistics/shared/output.py +++ b/atomistics/shared/output.py @@ -1,14 +1,41 @@ +""" +The output module defines the abstract output classes for the different types of outputs defined by the atomistics +package. All output classes are abstract classes, which define the output as abstract properties and are derived from +the atomistics.shared.output.AbstractOutput class. +""" + from abc import ABC, abstractmethod import numpy as np -class Output: - def get_output(self, output_keys): +class AbstractOutput: + """ + Abstract Base class used for the implementation of the individual output classes. + """ + + def get_output(self, output_keys) -> dict: + """ + Evaluate multiple properties with a single function call by providing a list of output keys each referencing one + property as input and returning a dictionary with the property names as keys and the corresponding results as + values. + + Args: + output_keys (tuple): Tuple of output property names as strings to be evaluated + + Returns: + dict: dictionary with the property names as keys and the corresponding results as values. + """ return {q: getattr(self, q) for q in output_keys} @classmethod - def keys(cls): + def keys(cls) -> tuple: + """ + Return all public functions and properties defined in a given class. + + Returns: + tuple: Tuple of names all public functions and properties defined in the derived class as strings. + """ return tuple( [ k @@ -18,7 +45,11 @@ def keys(cls): ) -class OutputStatic(ABC, Output): +class OutputStatic(ABC, AbstractOutput): + """ + Output class for a static calculation of a supercell with n atoms. + """ + @property @abstractmethod def forces(self) -> np.ndarray: # (n, 3) [eV / Ang^2] @@ -40,7 +71,11 @@ def volume(self) -> float: # [Ang^3] pass -class OutputMolecularDynamics(ABC, Output): +class OutputMolecularDynamics(ABC, AbstractOutput): + """ + Output class for a molecular dynamics calculation with t steps of a supercell with n atoms. + """ + @property @abstractmethod def positions(self) -> np.ndarray: # (t, n, 3) [Ang] @@ -87,7 +122,11 @@ def volume(self) -> np.ndarray: # (t) [Ang^3] pass -class OutputThermalExpansion(ABC, Output): +class OutputThermalExpansion(ABC, AbstractOutput): + """ + Output class for a thermal expansion calculation iterating over T temperature steps. + """ + @property @abstractmethod def temperatures(self) -> np.ndarray: # (T) [K] @@ -99,7 +138,11 @@ def volumes(self) -> np.ndarray: # (T) [Ang^3] pass -class OutputThermodynamic(ABC, Output): +class OutputThermodynamic(ABC, AbstractOutput): + """ + Output class for the calculation of the temperature dependence in T temperature steps of thermodynamic properties + """ + @property @abstractmethod def temperatures(self) -> np.ndarray: # (T) [K] @@ -126,7 +169,11 @@ def heat_capacity(self) -> np.ndarray: # (T) [eV] pass -class OutputEnergyVolumeCurve(ABC, Output): +class OutputEnergyVolumeCurve(ABC, AbstractOutput): + """ + Output class for the calculation on an energy volume curve calculation based on V strained cells. + """ + @property @abstractmethod def energy_eq(self) -> float: # float [eV] @@ -163,7 +210,11 @@ def volume(self) -> np.ndarray: # (V) [Ang^3] pass -class OutputElastic(ABC, Output): +class OutputElastic(ABC, AbstractOutput): + """ + Output class for the calculation of elastic moduli from the elastic matrix of the elastic constants. + """ + @property @abstractmethod def elastic_matrix(self) -> np.ndarray: # (6,6) [GPa] @@ -245,7 +296,11 @@ def elastic_matrix_eigval(self) -> np.ndarray: # (6,6) [GPa] pass -class OutputPhonons(ABC, Output): +class OutputPhonons(ABC, AbstractOutput): + """ + Output class for the calculation of phonons using the finite displacement method + """ + @property @abstractmethod def mesh_dict(self) -> dict: