Skip to content

Commit

Permalink
Merge pull request #78 from pyiron/atomistics_library
Browse files Browse the repository at this point in the history
Nodes for the `atomistics` package
  • Loading branch information
jan-janssen authored Nov 20, 2023
2 parents 7b02bc0 + 82f1b4b commit 1b8cf3c
Show file tree
Hide file tree
Showing 4 changed files with 295 additions and 0 deletions.
Empty file.
119 changes: 119 additions & 0 deletions pyiron_workflow/atomistics_library/calculatornodes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
from ase.units import Ry

from pyiron_workflow.function import single_value_node


@single_value_node("calculator")
def get_emt():
from ase.calculators.emt import EMT

return EMT()


@single_value_node("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("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("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("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("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("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("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,
)


nodes = [
calc_with_calculator,
get_abinit,
get_emt,
get_gpaw,
get_lammps,
get_lammps_potential,
get_quantum_espresso,
get_siesta,
]
86 changes: 86 additions & 0 deletions pyiron_workflow/atomistics_library/macronodes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
from pyiron_workflow.macro import Macro, macro_node
from pyiron_workflow.function import single_value_node
from pyiron_workflow.atomistics_library.calculatornodes import calc_with_calculator
from pyiron_workflow.atomistics_library.tasknodes import (
get_elastic_matrix_task_generator,
get_evcurve_task_generator,
get_phonons_task_generator,
analyse_structures,
generate_structures,
)


@single_value_node("instance")
def get_instance(instance):
return instance


@macro_node()
def internal_macro(wf: Macro) -> None:
wf.get_instance = get_instance()
wf.generate_structures = generate_structures(instance=wf.get_instance)
wf.calc_with_calculator = calc_with_calculator(task_dict=wf.generate_structures)
wf.fit = analyse_structures(
instance=wf.get_instance, output_dict=wf.calc_with_calculator
)
wf.inputs_map = {
"get_instance__instance": "instance",
"calc_with_calculator__calculator": "calculator",
}
wf.outputs_map = {"fit__fit_dict": "fit_dict"}


@macro_node()
def get_energy_volume_curve(wf: Macro) -> None:
wf.get_task_generator = get_evcurve_task_generator()
wf.internal = internal_macro(instance=wf.get_task_generator)
wf.inputs_map = {
"get_task_generator__structure": "structure",
"get_task_generator__num_points": "num_points",
"get_task_generator__fit_type": "fit_type",
"get_task_generator__fit_order": "fit_order",
"get_task_generator__vol_range": "vol_range",
"get_task_generator__axes": "axes",
"get_task_generator__strains": "strains",
"internal__calculator": "calculator",
}
wf.outputs_map = {"internal__fit_dict": "fit_dict"}


@macro_node()
def get_elastic_matrix(wf: Macro) -> None:
wf.get_task_generator = get_elastic_matrix_task_generator()
wf.internal = internal_macro(instance=wf.get_task_generator)
wf.inputs_map = {
"get_task_generator__structure": "structure",
"get_task_generator__num_of_point": "num_of_point",
"get_task_generator__eps_range": "eps_range",
"get_task_generator__sqrt_eta": "sqrt_eta",
"get_task_generator__fit_order": "fit_order",
"internal__calculator": "calculator",
}
wf.outputs_map = {"internal__fit_dict": "fit_dict"}


@macro_node()
def get_phonons(wf: Macro) -> None:
wf.get_task_generator = get_phonons_task_generator()
wf.internal = internal_macro(instance=wf.get_task_generator)
wf.inputs_map = {
"get_task_generator__structure": "structure",
"get_task_generator__interaction_range": "interaction_range",
"get_task_generator__factor": "factor",
"get_task_generator__displacement": "displacement",
"get_task_generator__dos_mesh": "dos_mesh",
"get_task_generator__primitive_matrix": "primitive_matrix",
"get_task_generator__number_of_snapshots": "number_of_snapshots",
"internal__calculator": "calculator",
}
wf.outputs_map = {"internal__fit_dict": "fit_dict"}


nodes = [
get_energy_volume_curve,
get_elastic_matrix,
get_phonons,
]
90 changes: 90 additions & 0 deletions pyiron_workflow/atomistics_library/tasknodes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
from phonopy.units import VaspToTHz
from pyiron_workflow.function import single_value_node


@single_value_node("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("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("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("result_dict")
def analyse_structures(instance, output_dict):
return instance.analyse_structures(output_dict=output_dict)


@single_value_node("task_dict")
def generate_structures(instance):
return instance.generate_structures()


@single_value_node("structure")
def get_bulk(element):
from ase.build import bulk

return bulk(element, a=4.00, cubic=True)


nodes = [
analyse_structures,
generate_structures,
get_bulk,
get_elastic_matrix_task_generator,
get_evcurve_task_generator,
get_phonons_task_generator,
]

0 comments on commit 1b8cf3c

Please sign in to comment.