From fb59a8b41ca855a34c8bc49fea6422c27db57dab Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Mon, 13 Nov 2023 17:10:31 +0100 Subject: [PATCH] Implement structure optimization protocol --- atomistics/calculators/ase.py | 37 ++++++++++-- atomistics/calculators/interface.py | 4 ++ atomistics/calculators/lammps.py | 58 +++++++++++++++++-- atomistics/calculators/wrapper.py | 13 +++-- .../structure_optimization/__init__.py | 0 .../structure_optimization/workflow.py | 6 ++ tests/test_elastic_emt.py | 6 +- tests/test_elastic_gpaw.py | 4 +- tests/test_elastic_lammps.py | 16 +++-- tests/test_evcurve_abinit.py | 4 +- tests/test_evcurve_emt.py | 4 +- tests/test_evcurve_gpaw.py | 4 +- tests/test_evcurve_lammps.py | 12 +++- tests/test_evcurve_qe.py | 4 +- tests/test_evcurve_siesta.py | 4 +- tests/test_optimize_positions_emt.py | 31 ++++++++++ tests/test_optimize_positions_lammps.py | 48 +++++++++++++++ tests/test_phonons_emt.py | 4 +- tests/test_phonons_gpaw.py | 4 +- tests/test_phonons_lammps.py | 10 +++- tests/test_quasiharmonic_emt.py | 4 +- tests/test_quasiharmonic_lammps.py | 8 ++- 22 files changed, 237 insertions(+), 48 deletions(-) create mode 100644 atomistics/workflows/structure_optimization/__init__.py create mode 100644 atomistics/workflows/structure_optimization/workflow.py create mode 100644 tests/test_optimize_positions_emt.py create mode 100644 tests/test_optimize_positions_lammps.py diff --git a/atomistics/calculators/ase.py b/atomistics/calculators/ase.py index 590cb38f..fa1d5ac3 100644 --- a/atomistics/calculators/ase.py +++ b/atomistics/calculators/ase.py @@ -7,17 +7,42 @@ if TYPE_CHECKING: from ase import Atoms from ase.calculators.calculator import Calculator as ASECalculator + from ase.optimize.optimize import Optimizer from atomistics.calculators.interface import TaskName +def ase_optimize_structure( + structure, ase_calculator, ase_optimizer, ase_optimizer_kwargs +): + structure_optimized = structure.copy() + structure_optimized.calc = ase_calculator + ase_optimizer_obj = ase_optimizer(structure_optimized) + ase_optimizer_obj.run(**ase_optimizer_kwargs) + return structure_optimized + + @as_task_dict_evaluator def evaluate_with_ase( - structure: Atoms, tasks: list[TaskName], ase_calculator: ASECalculator + structure: Atoms, + tasks: list[TaskName], + ase_calculator: ASECalculator, + ase_optimizer: Optimizer = None, + ase_optimizer_kwargs: dict = None, ): - structure.calc = ase_calculator results = {} - if "calc_energy" in tasks: - results["energy"] = structure.get_potential_energy() - if "calc_forces" in tasks: - results["forces"] = structure.get_forces() + if "optimize_positions" in tasks: + results["structure_with_optimized_positions"] = ase_optimize_structure( + structure=structure, + ase_calculator=ase_calculator, + ase_optimizer=ase_optimizer, + ase_optimizer_kwargs=ase_optimizer_kwargs, + ) + elif "calc_energy" in tasks or "calc_forces" in tasks: + structure.calc = ase_calculator + if "calc_energy" in tasks: + results["energy"] = structure.get_potential_energy() + if "calc_forces" in tasks: + results["forces"] = structure.get_forces() + else: + raise ValueError("The ASE calculator does not implement:", tasks) return results diff --git a/atomistics/calculators/interface.py b/atomistics/calculators/interface.py index 7ec7287d..76289de3 100644 --- a/atomistics/calculators/interface.py +++ b/atomistics/calculators/interface.py @@ -17,11 +17,15 @@ def __str__(self): class TaskEnum(StrEnum): calc_energy = "calc_energy" calc_forces = "calc_forces" + optimize_positions = "optimize_positions" + optimize_positions_and_volume = "optimize_positions_and_volume" class TaskOutputEnum(Enum): energy = "calc_energy" forces = "calc_forces" + structure_with_optimized_positions = "optimize_positions" + structure_with_optimized_positions_and_volume = "optimize_positions_and_volume" if TYPE_CHECKING: diff --git a/atomistics/calculators/lammps.py b/atomistics/calculators/lammps.py index 6cd4997e..cceb7cbc 100644 --- a/atomistics/calculators/lammps.py +++ b/atomistics/calculators/lammps.py @@ -17,13 +17,30 @@ from atomistics.calculators.interface import TaskName -LAMMPS_INPUT_TEMPLATE = """\ +LAMMPS_STATIC_RUN_INPUT_TEMPLATE = """\ thermo_style custom step temp pe etotal pxx pxy pxz pyy pyz pzz vol thermo_modify format float %20.15g thermo 100 run 0""" +LAMMPS_MINIMIZE_POSITIONS_INPUT_TEMPLATE = """\ +thermo_style custom step temp pe etotal pxx pxy pxz pyy pyz pzz vol +thermo_modify format float %20.15g +thermo 10 +min_style cg +minimize 0.0 0.0001 100000 10000000""" + + +LAMMPS_MINIMIZE_POSITIONS_AND_VOLUME_INPUT_TEMPLATE = """\ +fix ensemble all box/relax iso 0.0 +thermo_style custom step temp pe etotal pxx pxy pxz pyy pyz pzz vol +thermo_modify format float %20.15g +thermo 10 +min_style cg +minimize 0.0 0.0001 100000 10000000""" + + @as_task_dict_evaluator def evaluate_with_lammps_library( structure: Atoms, @@ -31,12 +48,41 @@ def evaluate_with_lammps_library( potential_dataframe: DataFrame, lmp: LammpsASELibrary, ): - lmp = _run_simulation(structure, potential_dataframe, LAMMPS_INPUT_TEMPLATE, lmp) results = {} - if "calc_energy" in tasks: - results["energy"] = lmp.interactive_energy_pot_getter() - if "calc_forces" in tasks: - results["forces"] = lmp.interactive_forces_getter() + if "optimize_positions_and_volume" in tasks: + lmp = _run_simulation( + structure=structure, + potential_dataframe=potential_dataframe, + input_template=LAMMPS_MINIMIZE_POSITIONS_AND_VOLUME_INPUT_TEMPLATE, + lmp=lmp, + ) + structure_copy = structure.copy() + structure_copy.set_cell(lmp.interactive_cells_getter(), scale_atoms=True) + structure_copy.positions = lmp.interactive_positions_getter() + results["structure_with_optimized_positions_and_volume"] = structure_copy + elif "optimize_positions" in tasks: + lmp = _run_simulation( + structure=structure, + potential_dataframe=potential_dataframe, + input_template=LAMMPS_MINIMIZE_POSITIONS_INPUT_TEMPLATE, + lmp=lmp, + ) + structure_copy = structure.copy() + structure_copy.positions = lmp.interactive_positions_getter() + results["structure_with_optimized_positions"] = structure_copy + elif "calc_energy" in tasks or "calc_forces" in tasks: + lmp = _run_simulation( + structure=structure, + potential_dataframe=potential_dataframe, + input_template=LAMMPS_STATIC_RUN_INPUT_TEMPLATE, + lmp=lmp, + ) + if "calc_energy" in tasks: + results["energy"] = lmp.interactive_energy_pot_getter() + if "calc_forces" in tasks: + results["forces"] = lmp.interactive_forces_getter() + else: + raise ValueError("The LAMMPS calculator does not implement:", tasks) lmp.interactive_lib_command("clear") return results diff --git a/atomistics/calculators/wrapper.py b/atomistics/calculators/wrapper.py index d97dc287..fe48a179 100644 --- a/atomistics/calculators/wrapper.py +++ b/atomistics/calculators/wrapper.py @@ -33,6 +33,8 @@ def _convert_task_dict(old_task_dict: dict[TaskName, dict[str, Atoms]]) -> TaskD """ task_dict = {} for method_name, subdict in old_task_dict.items(): + if not isinstance(subdict, dict): + subdict = {"label_hidden": subdict} for label, structure in subdict.items(): try: task_dict[label][1].append(method_name) @@ -72,10 +74,13 @@ def evaluate_with_calculator( output = calculate(structure, tasks, *calculate_args, **calculate_kwargs) for task_name in tasks: result_name = TaskOutputEnum(task_name).name - try: - results_dict[result_name][label] = output[result_name] - except KeyError: - results_dict[result_name] = {label: output[result_name]} + if label != "label_hidden": + try: + results_dict[result_name][label] = output[result_name] + except KeyError: + results_dict[result_name] = {label: output[result_name]} + else: + results_dict[result_name] = output[result_name] return results_dict return evaluate_with_calculator diff --git a/atomistics/workflows/structure_optimization/__init__.py b/atomistics/workflows/structure_optimization/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/atomistics/workflows/structure_optimization/workflow.py b/atomistics/workflows/structure_optimization/workflow.py new file mode 100644 index 00000000..6b8f30d8 --- /dev/null +++ b/atomistics/workflows/structure_optimization/workflow.py @@ -0,0 +1,6 @@ +def optimize_positions_and_volume(structure): + return {"optimize_positions_and_volume": structure} + + +def optimize_positions(structure): + return {"optimize_positions": structure} diff --git a/tests/test_elastic_emt.py b/tests/test_elastic_emt.py index c6460153..7c6d0ccf 100644 --- a/tests/test_elastic_emt.py +++ b/tests/test_elastic_emt.py @@ -9,15 +9,15 @@ class TestElastic(unittest.TestCase): def test_calc_elastic(self): - calculator = calculator = ElasticMatrixWorkflow( + calculator = ElasticMatrixWorkflow( structure=bulk("Al", a=4.0, cubic=True), num_of_point=5, eps_range=0.005, sqrt_eta=True, fit_order=2 ) - structure_dict = calculator.generate_structures() - result_dict = evaluate_with_ase(structure_dict, ase_calculator=EMT()) + task_dict = calculator.generate_structures() + result_dict = evaluate_with_ase(task_dict=task_dict, ase_calculator=EMT()) elastic_dict = calculator.analyse_structures(output_dict=result_dict) self.assertTrue(np.isclose(elastic_dict["C"][0, 0], 52.62435421)) self.assertTrue(np.isclose(elastic_dict["C"][0, 1], 32.6743838)) diff --git a/tests/test_elastic_gpaw.py b/tests/test_elastic_gpaw.py index 5b430048..57b620f0 100644 --- a/tests/test_elastic_gpaw.py +++ b/tests/test_elastic_gpaw.py @@ -25,9 +25,9 @@ def test_calc_elastic(self): sqrt_eta=True, fit_order=2 ) - structure_dict = calculator.generate_structures() + task_dict = calculator.generate_structures() result_dict = evaluate_with_ase( - task_dict=structure_dict, + task_dict=task_dict, ase_calculator=GPAW( xc="PBE", mode=PW(300), diff --git a/tests/test_elastic_lammps.py b/tests/test_elastic_lammps.py index 0f4568b2..6d12ad23 100644 --- a/tests/test_elastic_lammps.py +++ b/tests/test_elastic_lammps.py @@ -5,6 +5,7 @@ import unittest from atomistics.workflows.elastic.workflow import ElasticMatrixWorkflow +from atomistics.workflows.structure_optimization.workflow import optimize_positions_and_volume try: from atomistics.calculators.lammps import ( @@ -23,25 +24,30 @@ class TestElastic(unittest.TestCase): def test_calc_elastic(self): potential = '1999--Mishin-Y--Al--LAMMPS--ipr1' resource_path = os.path.join(os.path.dirname(__file__), "static", "lammps") - structure = bulk("Al", a=4.05, cubic=True) + structure = bulk("Al", cubic=True) df_pot = get_potential_dataframe( structure=structure, resource_path=resource_path ) df_pot_selected = df_pot[df_pot.Name == potential].iloc[0] + task_dict = optimize_positions_and_volume(structure=structure) + result_dict = evaluate_with_lammps( + task_dict=task_dict, + potential_dataframe=df_pot_selected, + ) calculator = ElasticMatrixWorkflow( - structure=structure, + structure=result_dict["structure_with_optimized_positions_and_volume"], num_of_point=5, eps_range=0.005, sqrt_eta=True, fit_order=2 ) - structure_dict = calculator.generate_structures() + task_dict = calculator.generate_structures() result_dict = evaluate_with_lammps( - task_dict=structure_dict, + task_dict=task_dict, potential_dataframe=df_pot_selected, ) elastic_dict = calculator.analyse_structures(output_dict=result_dict) self.assertTrue(np.isclose(elastic_dict["C"][0, 0], 114.10393023)) self.assertTrue(np.isclose(elastic_dict["C"][0, 1], 60.51098897)) - self.assertTrue(np.isclose(elastic_dict["C"][3, 3], 51.23931149)) + self.assertTrue(np.isclose(elastic_dict["C"][3, 3], 51.23853765)) diff --git a/tests/test_evcurve_abinit.py b/tests/test_evcurve_abinit.py index 9e65cf48..704c294a 100644 --- a/tests/test_evcurve_abinit.py +++ b/tests/test_evcurve_abinit.py @@ -43,9 +43,9 @@ def test_calc_evcurve(self): axes=['x', 'y', 'z'], strains=None, ) - structure_dict = calculator.generate_structures() + task_dict = calculator.generate_structures() result_dict = evaluate_with_ase( - task_dict=structure_dict, + task_dict=task_dict, ase_calculator=Abinit( label='abinit_evcurve', nbands=32, diff --git a/tests/test_evcurve_emt.py b/tests/test_evcurve_emt.py index f749e64d..558b69b1 100644 --- a/tests/test_evcurve_emt.py +++ b/tests/test_evcurve_emt.py @@ -18,8 +18,8 @@ def test_calc_evcurve(self): axes=['x', 'y', 'z'], strains=None, ) - structure_dict = calculator.generate_structures() - result_dict = evaluate_with_ase(task_dict=structure_dict, ase_calculator=EMT()) + task_dict = calculator.generate_structures() + result_dict = evaluate_with_ase(task_dict=task_dict, ase_calculator=EMT()) fit_dict = calculator.analyse_structures(output_dict=result_dict) self.assertTrue(np.isclose(fit_dict['volume_eq'], 63.72615218844302)) self.assertTrue(np.isclose(fit_dict['bulkmodul_eq'], 39.544084907317895)) diff --git a/tests/test_evcurve_gpaw.py b/tests/test_evcurve_gpaw.py index c8c76753..3171438b 100644 --- a/tests/test_evcurve_gpaw.py +++ b/tests/test_evcurve_gpaw.py @@ -28,9 +28,9 @@ def test_calc_evcurve(self): axes=['x', 'y', 'z'], strains=None, ) - structure_dict = calculator.generate_structures() + task_dict = calculator.generate_structures() result_dict = evaluate_with_ase( - task_dict=structure_dict, + task_dict=task_dict, ase_calculator=GPAW( xc="PBE", mode=PW(300), diff --git a/tests/test_evcurve_lammps.py b/tests/test_evcurve_lammps.py index e7c332d6..ef787abf 100644 --- a/tests/test_evcurve_lammps.py +++ b/tests/test_evcurve_lammps.py @@ -5,6 +5,7 @@ import unittest from atomistics.workflows.evcurve.workflow import EnergyVolumeCurveWorkflow +from atomistics.workflows.structure_optimization.workflow import optimize_positions_and_volume try: @@ -24,14 +25,19 @@ class TestEvCurve(unittest.TestCase): def test_calc_evcurve(self): potential = '1999--Mishin-Y--Al--LAMMPS--ipr1' resource_path = os.path.join(os.path.dirname(__file__), "static", "lammps") - structure = bulk("Al", a=4.05, cubic=True) + structure = bulk("Al", cubic=True) df_pot = get_potential_dataframe( structure=structure, resource_path=resource_path ) df_pot_selected = df_pot[df_pot.Name == potential].iloc[0] + task_dict = optimize_positions_and_volume(structure=structure) + result_dict = evaluate_with_lammps( + task_dict=task_dict, + potential_dataframe=df_pot_selected, + ) calculator = EnergyVolumeCurveWorkflow( - structure=structure, + structure=result_dict["structure_with_optimized_positions_and_volume"], num_points=11, fit_type='polynomial', fit_order=3, @@ -47,4 +53,4 @@ def test_calc_evcurve(self): fit_dict = calculator.analyse_structures(output_dict=result_dict) self.assertTrue(np.isclose(fit_dict['volume_eq'], 66.43019853103964)) self.assertTrue(np.isclose(fit_dict['bulkmodul_eq'], 77.7250135953191)) - self.assertTrue(np.isclose(fit_dict['b_prime_eq'], 1.279502459079921)) + self.assertTrue(np.isclose(fit_dict['b_prime_eq'], 1.2795467367276832)) diff --git a/tests/test_evcurve_qe.py b/tests/test_evcurve_qe.py index 87187277..359eb3c9 100644 --- a/tests/test_evcurve_qe.py +++ b/tests/test_evcurve_qe.py @@ -46,9 +46,9 @@ def test_calc_evcurve(self): axes=['x', 'y', 'z'], strains=None, ) - structure_dict = calculator.generate_structures() + task_dict = calculator.generate_structures() result_dict = evaluate_with_ase( - task_dict=structure_dict, + task_dict=task_dict, ase_calculator=Espresso( pseudopotentials=pseudopotentials, tstress=True, diff --git a/tests/test_evcurve_siesta.py b/tests/test_evcurve_siesta.py index ef1eb456..a269e2f3 100644 --- a/tests/test_evcurve_siesta.py +++ b/tests/test_evcurve_siesta.py @@ -47,9 +47,9 @@ def test_calc_evcurve(self): axes=['x', 'y', 'z'], strains=None, ) - structure_dict = calculator.generate_structures() + task_dict = calculator.generate_structures() result_dict = evaluate_with_ase( - task_dict=structure_dict, + task_dict=task_dict, ase_calculator=Siesta( label="siesta", xc="PBE", diff --git a/tests/test_optimize_positions_emt.py b/tests/test_optimize_positions_emt.py new file mode 100644 index 00000000..6afe982b --- /dev/null +++ b/tests/test_optimize_positions_emt.py @@ -0,0 +1,31 @@ +import numpy as np +from ase.build import bulk +from ase.calculators.emt import EMT +from ase.optimize import BFGS +import unittest + +from atomistics.calculators.ase import evaluate_with_ase +from atomistics.workflows.structure_optimization.workflow import optimize_positions + + +class TestOptimizePositions(unittest.TestCase): + def test_optimize_positions(self): + structure = bulk("Al", a=4.0, cubic=True) + positions_before_displacement = structure.positions.copy() + structure.positions[0] += [0.01, 0.01, 0.01] + task_dict = optimize_positions(structure=structure) + result_dict = evaluate_with_ase( + task_dict=task_dict, + ase_calculator=EMT(), + ase_optimizer=BFGS, + ase_optimizer_kwargs={"fmax": 0.00005} + ) + structure_optimized = result_dict["structure_with_optimized_positions"] + self.assertTrue( + all(np.isclose( + positions_before_displacement, + structure_optimized.positions-structure_optimized.positions[0], + atol=1e-5 + ).flatten()) + ) + diff --git a/tests/test_optimize_positions_lammps.py b/tests/test_optimize_positions_lammps.py new file mode 100644 index 00000000..be2306b7 --- /dev/null +++ b/tests/test_optimize_positions_lammps.py @@ -0,0 +1,48 @@ +import os + +from ase.build import bulk +import numpy as np +import unittest + +from atomistics.workflows.structure_optimization.workflow import optimize_positions + + +try: + from atomistics.calculators.lammps import ( + evaluate_with_lammps, get_potential_dataframe + ) + + skip_lammps_test = False +except ImportError: + skip_lammps_test = True + + +@unittest.skipIf( + skip_lammps_test, "LAMMPS is not installed, so the LAMMPS tests are skipped." +) +class TestOptimizePositions(unittest.TestCase): + def test_optimize_positions(self): + potential = '1999--Mishin-Y--Al--LAMMPS--ipr1' + resource_path = os.path.join(os.path.dirname(__file__), "static", "lammps") + structure = bulk("Al", cubic=True) + positions_before_displacement = structure.positions.copy() + structure.positions[0] += [0.01, 0.01, 0.01] + df_pot = get_potential_dataframe( + structure=structure, + resource_path=resource_path + ) + df_pot_selected = df_pot[df_pot.Name == potential].iloc[0] + task_dict = optimize_positions(structure=structure) + result_dict = evaluate_with_lammps( + task_dict=task_dict, + potential_dataframe=df_pot_selected, + ) + structure_optimized = result_dict["structure_with_optimized_positions"] + self.assertTrue( + all(np.isclose( + positions_before_displacement, + structure_optimized.positions-structure_optimized.positions[0], + atol=1e-5 + ).flatten()) + ) + diff --git a/tests/test_phonons_emt.py b/tests/test_phonons_emt.py index 5bed842e..1cffe371 100644 --- a/tests/test_phonons_emt.py +++ b/tests/test_phonons_emt.py @@ -18,8 +18,8 @@ def test_calc_phonons(self): primitive_matrix=None, number_of_snapshots=None, ) - structure_dict = calculator.generate_structures() - result_dict = evaluate_with_ase(task_dict=structure_dict, ase_calculator=EMT()) + task_dict = calculator.generate_structures() + result_dict = evaluate_with_ase(task_dict=task_dict, ase_calculator=EMT()) mesh_dict, dos_dict = calculator.analyse_structures(output_dict=result_dict) self.assertEqual((324, 324), calculator.get_hesse_matrix().shape) self.assertTrue('qpoints' in mesh_dict.keys()) diff --git a/tests/test_phonons_gpaw.py b/tests/test_phonons_gpaw.py index ba0a1d2c..1bf46be0 100644 --- a/tests/test_phonons_gpaw.py +++ b/tests/test_phonons_gpaw.py @@ -27,9 +27,9 @@ def test_calc_phonons(self): primitive_matrix=None, number_of_snapshots=None, ) - structure_dict = calculator.generate_structures() + task_dict = calculator.generate_structures() result_dict = evaluate_with_ase( - task_dict=structure_dict, + task_dict=task_dict, ase_calculator=GPAW( xc="PBE", mode=PW(300), diff --git a/tests/test_phonons_lammps.py b/tests/test_phonons_lammps.py index d74fa4f5..fa7d6682 100644 --- a/tests/test_phonons_lammps.py +++ b/tests/test_phonons_lammps.py @@ -5,6 +5,7 @@ import unittest from atomistics.workflows.phonons.workflow import PhonopyWorkflow +from atomistics.workflows.structure_optimization.workflow import optimize_positions_and_volume try: from atomistics.calculators.lammps import ( @@ -23,14 +24,19 @@ class TestPhonons(unittest.TestCase): def test_calc_phonons(self): potential = '1999--Mishin-Y--Al--LAMMPS--ipr1' resource_path = os.path.join(os.path.dirname(__file__), "static", "lammps") - structure = bulk("Al", a=4.05, cubic=True) + structure = bulk("Al", cubic=True) df_pot = get_potential_dataframe( structure=structure, resource_path=resource_path ) df_pot_selected = df_pot[df_pot.Name == potential].iloc[0] + task_dict = optimize_positions_and_volume(structure=structure) + result_dict = evaluate_with_lammps( + task_dict=task_dict, + potential_dataframe=df_pot_selected, + ) calculator = PhonopyWorkflow( - structure=structure, + structure=result_dict["structure_with_optimized_positions_and_volume"], interaction_range=10, factor=VaspToTHz, displacement=0.01, diff --git a/tests/test_quasiharmonic_emt.py b/tests/test_quasiharmonic_emt.py index f7f50042..08a80520 100644 --- a/tests/test_quasiharmonic_emt.py +++ b/tests/test_quasiharmonic_emt.py @@ -20,9 +20,9 @@ def test_calc_phonons(self): primitive_matrix=None, number_of_snapshots=None, ) - structure_dict = calculator.generate_structures() + task_dict = calculator.generate_structures() result_dict = evaluate_with_ase( - task_dict=structure_dict, + task_dict=task_dict, ase_calculator=EMT() ) eng_internal_dict, mesh_collect_dict, dos_collect_dict = calculator.analyse_structures(output_dict=result_dict) diff --git a/tests/test_quasiharmonic_lammps.py b/tests/test_quasiharmonic_lammps.py index 4a99948e..fddd83ed 100644 --- a/tests/test_quasiharmonic_lammps.py +++ b/tests/test_quasiharmonic_lammps.py @@ -5,6 +5,7 @@ import unittest from atomistics.workflows.quasiharmonic.workflow import QuasiHarmonicWorkflow +from atomistics.workflows.structure_optimization.workflow import optimize_positions_and_volume try: from atomistics.calculators.lammps import ( @@ -29,8 +30,13 @@ def test_calc_phonons(self): resource_path=resource_path ) df_pot_selected = df_pot[df_pot.Name == potential].iloc[0] + task_dict = optimize_positions_and_volume(structure=structure) + result_dict = evaluate_with_lammps( + task_dict=task_dict, + potential_dataframe=df_pot_selected, + ) calculator = QuasiHarmonicWorkflow( - structure=structure, + structure=result_dict["structure_with_optimized_positions_and_volume"], num_points=11, vol_range=0.05, interaction_range=10,