From af12c58626b6bd0463f34fb798210a60df81a306 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Jan=C3=9Fen?= Date: Wed, 15 Nov 2023 23:01:45 +0100 Subject: [PATCH] Nodes for the `atomistics` package Example: ``` from pyiron_workflow import Workflow from pyiron_workflow.atomistics_library.calculatornodes import get_emt, calc_with_calculator from pyiron_workflow.atomistics_library.tasknodes import get_evcurve_task_generator, analyse_structures, generate_structures, get_bulk wf = Workflow("evcurve") wf.get_structure = get_bulk(element="Al") wf.get_task_generator = get_evcurve_task_generator(structure=wf.get_structure) wf.generate_structures = generate_structures(instance=wf.get_task_generator) wf.get_calculator = get_emt() wf.calc_with_calculator = calc_with_calculator(task_dict=wf.generate_structures, calculator=wf.get_calculator) wf.fit = analyse_structures(instance=wf.get_task_generator, output_dict=wf.calc_with_calculator) wf.draw() wf.run() ``` As the workflows in the `atomistics` package all follow the same pattern of (0) initializing the generator, (1) generating atomic structures, (2) evaluate the atomic structures with a simulation code and (3) analyse the results, the integration was rather straight forward. --- .../atomistics_library/__init__.py | 0 .../atomistics_library/calculatornodes.py | 113 ++++++++++++++++++ .../atomistics_library/tasknodes.py | 80 +++++++++++++ 3 files changed, 193 insertions(+) create mode 100644 pyiron_workflow/atomistics_library/__init__.py create mode 100644 pyiron_workflow/atomistics_library/calculatornodes.py create mode 100644 pyiron_workflow/atomistics_library/tasknodes.py diff --git a/pyiron_workflow/atomistics_library/__init__.py b/pyiron_workflow/atomistics_library/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pyiron_workflow/atomistics_library/calculatornodes.py b/pyiron_workflow/atomistics_library/calculatornodes.py new file mode 100644 index 00000000..889d1bf7 --- /dev/null +++ b/pyiron_workflow/atomistics_library/calculatornodes.py @@ -0,0 +1,113 @@ +from ase.units import Ry + +from pyiron_workflow.function import single_value_node + + +@single_value_node(output_labels="calculator") +def get_emt(): + from ase.calculators.emt import EMT + return EMT() + + +@single_value_node(output_labels="calculator") +def get_abinit( + label='abinit_evcurve', + nbands=32, + ecut=10 * Ry, + kpts=(3, 3, 3), + toldfe=1.0e-2, + v8_legacy_format=False, +): + from ase.calculators.abinit import Abinit + return Abinit( + label=label, + nbands=nbands, + ecut=ecut, + kpts=kpts, + toldfe=toldfe, + v8_legacy_format=v8_legacy_format, + ) + + +@single_value_node(output_labels="calculator") +def get_gpaw( + xc="PBE", + encut=300, + kpts=(3, 3, 3) +): + from gpaw import GPAW, PW + return GPAW( + xc=xc, + mode=PW(encut), + kpts=kpts + ) + + +@single_value_node(output_labels="calculator") +def get_quantum_espresso( + pseudopotentials={"Al": "Al.pbe-n-kjpaw_psl.1.0.0.UPF"}, + tstress=True, + tprnfor=True, + kpts=(3, 3, 3), +): + from ase.calculators.espresso import Espresso + return Espresso( + pseudopotentials=pseudopotentials, + tstress=tstress, + tprnfor=tprnfor, + kpts=kpts, + ) + + +@single_value_node(output_labels="calculator") +def get_siesta( + label="siesta", + xc="PBE", + mesh_cutoff=200 * Ry, + energy_shift=0.01 * Ry, + basis_set="DZ", + kpts=(5, 5, 5), + fdf_arguments={"DM.MixingWeight": 0.1, "MaxSCFIterations": 100}, + pseudo_path="", + pseudo_qualifier="", +): + from ase.calculators.siesta import Siesta + return Siesta( + label=label, + xc=xc, + mesh_cutoff=mesh_cutoff, + energy_shift=energy_shift, + basis_set=basis_set, + kpts=kpts, + fdf_arguments=fdf_arguments, + pseudo_path=pseudo_path, + pseudo_qualifier=pseudo_qualifier, + ) + + +@single_value_node(output_labels="energy_dict") +def calc_with_calculator(task_dict, calculator): + from atomistics.calculators.ase import evaluate_with_ase + return evaluate_with_ase( + task_dict=task_dict, + ase_calculator=calculator + ) + + +@single_value_node(output_labels="lammps_potential_dataframe") +def get_lammps_potential(potential_name, structure, resource_path): + from atomistics.calculators.lammps import get_potential_dataframe + df_pot = get_potential_dataframe( + structure=structure, + resource_path=resource_path + ) + return df_pot[df_pot.Name == potential_name].iloc[0] + + +@single_value_node(output_labels="energy_dict") +def get_lammps(task_dict, potential_dataframe): + from atomistics.calculators.lammps import evaluate_with_lammps + return evaluate_with_lammps( + task_dict=task_dict, + potential_dataframe=potential_dataframe, + ) \ No newline at end of file diff --git a/pyiron_workflow/atomistics_library/tasknodes.py b/pyiron_workflow/atomistics_library/tasknodes.py new file mode 100644 index 00000000..3a9c6e7e --- /dev/null +++ b/pyiron_workflow/atomistics_library/tasknodes.py @@ -0,0 +1,80 @@ +from phonopy.units import VaspToTHz +from pyiron_workflow.function import single_value_node + + +@single_value_node(output_labels="task_generator") +def get_elastic_matrix_task_generator( + structure, + num_of_point=5, + eps_range=0.05, + sqrt_eta=True, + fit_order=2 +): + from atomistics.workflows.elastic.workflow import ElasticMatrixWorkflow + return ElasticMatrixWorkflow( + structure=structure, + num_of_point=num_of_point, + eps_range=eps_range, + sqrt_eta=sqrt_eta, + fit_order=fit_order, + ) + + +@single_value_node(output_labels="task_generator") +def get_evcurve_task_generator( + structure, + num_points=11, + fit_type='polynomial', + fit_order=3, + vol_range=0.05, + axes=['x', 'y', 'z'], + strains=None +): + from atomistics.workflows.evcurve.workflow import EnergyVolumeCurveWorkflow + return EnergyVolumeCurveWorkflow( + structure=structure, + num_points=num_points, + fit_type=fit_type, + fit_order=fit_order, + vol_range=vol_range, + axes=axes, + strains=strains, + ) + + +@single_value_node(output_labels="task_generator") +def get_phonons_task_generator( + structure, + interaction_range=10, + factor=VaspToTHz, + displacement=0.01, + dos_mesh=20, + primitive_matrix=None, + number_of_snapshots=None, +): + from atomistics.workflows.phonons.workflow import PhonopyWorkflow + return PhonopyWorkflow( + structure=structure, + interaction_range=interaction_range, + factor=factor, + displacement=displacement, + dos_mesh=dos_mesh, + primitive_matrix=primitive_matrix, + number_of_snapshots=number_of_snapshots, + ) + + +@single_value_node(output_labels="result_dict") +def analyse_structures(instance, output_dict): + return instance.analyse_structures(output_dict=output_dict) + + +@single_value_node(output_labels="task_dict") +def generate_structures(instance): + return instance.generate_structures() + + +@single_value_node(output_labels="structure") +def get_bulk(element): + from ase.build import bulk + return bulk(element, a=4.00, cubic=True)