Introduction to pyiron workflows

In this notebook, you will learn about different pyiron objects which acts as blocks of a building to complete a workflow. Here, we use a simulation workflow using lammps, but the features introduced here, as will be shown, are generic to other simulation/analysis tools.

As a first step we import the libraries numpy for data analysis and matplotlib for visualization.

import numpy as np
%matplotlib inline
import matplotlib.pylab as plt

To import pyiron simply use:

from pyiron_atomistics import Project

The Project object introduced below is central in pyiron. It allows to name the project as well as to derive all other objects such as structures, jobs etc. without having to import them. Thus, by code completion Tab the respective commands can be found easily.

We now create a pyiron Project named ‘first_steps’.

pr = Project(path='first_steps')

The project name also applies for the directory that is created for the project.

Perform a LAMMPS MD simulation

Having created an instance of the pyiron Project we now perform a LAMMPS molecular dynamics simulation.

For this basic simulation example we construct an fcc Al crystal in a cubic supercell (cubic=True). For more details on generating structures, please have a look at our structures example

basis = pr.create.structure.ase.bulk('Al', cubic=True)
basis = pr.create_ase_bulk('Al', cubic=True)
supercell_3x3x3 = basis.repeat([3, 3, 3])
supercell_3x3x3.plot3d()

Here create_ase_bulk uses the ASE bulk module. The structure can be modified - here we extend the original cell to a 3x3x3 supercell (repeat([3, 3, 3]). Finally, we plot the structure using NGlview.

The project object allows to create various simulation job types. Here, we create a LAMMPS job.

job = pr.create.job.Lammps(job_name='Al_T800K')

Further, we specify a Molecular Dynamics simulation at \(T=800\) K using the supercell structure created above.

job.structure = supercell_3x3x3
job.calc_md(temperature=800, pressure=0, n_ionic_steps=10000)

To see all available interatomic potentials which are compatible with the structure (for our example they must contain Al) and the job type (here LAMMPS) we call job.list_potentials().

job.list_potentials()
['1995--Angelo-J-E--Ni-Al-H--LAMMPS--ipr1',
 '1996--Farkas-D--Nb-Ti-Al--LAMMPS--ipr1',
 '1997--Liu-X-Y--Al-Mg--LAMMPS--ipr1',
 '1998--Liu-X-Y--Al-Mg--LAMMPS--ipr1',
 '1999--Liu-X-Y--Al-Cu--LAMMPS--ipr1',
 '1999--Mishin-Y--Al--LAMMPS--ipr1',
 '2000--Landa-A--Al-Pb--LAMMPS--ipr1',
 '2000--Sturgeon-J-B--Al--LAMMPS--ipr1',
 '2002--Mishin-Y--Ni-Al--LAMMPS--ipr1',
 '2003--Lee-B-J--Al--LAMMPS--ipr1',
 '2003--Zope-R-R--Al--LAMMPS--ipr1',
 '2003--Zope-R-R--Ti-Al--LAMMPS--ipr1',
 '2004--Liu-X-Y--Al--LAMMPS--ipr1',
 '2004--Mishin-Y--Ni-Al--LAMMPS--ipr1',
 '2004--Mishin-Y--Ni-Al--LAMMPS--ipr2',
 '2004--Zhou-X-W--Al--LAMMPS--ipr2',
 '2005--Mendelev-M-I--Al-Fe--LAMMPS--ipr1',
 '2007--Silva-A-C--Al-Ni--LAMMPS--ipr1',
 '2008--Mendelev-M-I--Al--LAMMPS--ipr1',
 '2009--Kim-Y-M--Mg-Al--LAMMPS--ipr1',
 '2009--Mendelev-M-I--Al-Mg--LAMMPS--ipr1',
 '2009--Purja-Pun-G-P--Ni-Al--LAMMPS--ipr1',
 '2009--Zhakhovskii-V-V--Al--LAMMPS--ipr2',
 '2010--Lee-E--Fe-Al--LAMMPS--ipr1',
 '2010--Mendelev-M-I--fictional-Al-1--LAMMPS--ipr1',
 '2010--Mendelev-M-I--fictional-Al-2--LAMMPS--ipr1',
 '2010--Mendelev-M-I--fictional-Al-3--LAMMPS--ipr1',
 '2010--Winey-J-M--Al--LAMMPS--ipr1',
 '2011--Apostol-F--Al-Cu--LAMMPS--ipr1',
 '2011--Ko-W-S--Al-H--LAMMPS--ipr1',
 '2012--Dong-W-P--Co-Al--LAMMPS--ipr1',
 '2012--Jelinek-B--Al-Si-Mg-Cu-Fe--LAMMPS--ipr2',
 '2012--Schopf-D--Al-Mn-Pd--LAMMPS--ipr1',
 '2013--Shim-J-H--V-Al--LAMMPS--ipr1',
 '2013--Shim-J-H--V-Al-H--LAMMPS--ipr1',
 '2015--Choudhary-K--Al--LAMMPS--ipr1',
 '2015--Choudhary-K--Al-O--LAMMPS--ipr1',
 '2015--Kim-Y-K--Ni-Al-Co--LAMMPS--ipr1',
 '2015--Kumar-A--Al-Ni--LAMMPS--ipr1',
 '2015--Kumar-A--Al-Ni-O--LAMMPS--ipr1',
 '2015--Mendelev-M-I--Al-Sm--LAMMPS--ipr1',
 '2015--Pascuet-M-I--Al--LAMMPS--ipr1',
 '2015--Pascuet-M-I--Al-U--LAMMPS--ipr2',
 '2015--Purja-Pun-G-P--Al-Co--LAMMPS--ipr2',
 '2015--Purja-Pun-G-P--Ni-Al-Co--LAMMPS--ipr2',
 '2016--Kim-Y-K--Al-Ti--LAMMPS--ipr1',
 '2016--Zhou-X-W--Al-Cu--LAMMPS--ipr2',
 '2017--Botu-V--Al--LAMMPS--ipr1',
 '2017--Kim-J-S--Pt-Al--LAMMPS--ipr1',
 '2017--Kim-Y-K--Ni-Al-Ti--LAMMPS--ipr1',
 '2018--Dickel-D-E--Mg-Al-Zn--LAMMPS--ipr1',
 '2018--Jeong-G-U--Pd-Al--LAMMPS--ipr1',
 '2018--Zhou-X-W--Al-Cu-H--LAMMPS--ipr1',
 '2020--Farkas-D--Fe-Ni-Cr-Co-Al--LAMMPS--ipr1',
 '2020--Starikov-S--Si-Au-Al--LAMMPS--ipr1',
 '2020--Starikov-S--Si-Au-Al--LAMMPS--ipr2',
 'EAM_CubicNaturalSpline_ErcolessiAdams_1994_Al__MO_800509458712_002',
 'EAM_Dynamo_AngeloMoodyBaskes_1995_NiAlH__MO_418978237058_005',
 'EAM_Dynamo_CaiYe_1996_AlCu__MO_942551040047_005',
 'EAM_Dynamo_ErcolessiAdams_1994_Al__MO_123629422045_005',
 'EAM_Dynamo_FarkasJones_1996_NbTiAl__MO_042691367780_000',
 'EAM_Dynamo_JacobsenNorskovPuska_1987_Al__MO_411692133366_000',
 'EAM_Dynamo_LandaWynblattSiegel_2000_AlPb__MO_699137396381_005',
 'EAM_Dynamo_LiuAdams_1998_AlMg__MO_019873715786_000',
 'EAM_Dynamo_LiuErcolessiAdams_2004_Al__MO_051157671505_000',
 'EAM_Dynamo_LiuLiuBorucki_1999_AlCu__MO_020851069572_000',
 'EAM_Dynamo_LiuOhotnickyAdams_1997_AlMg__MO_559870613549_000',
 'EAM_Dynamo_MendelevAstaRahman_2009_AlMg__MO_658278549784_005',
 'EAM_Dynamo_MendelevFangYe_2015_AlSm__MO_338600200739_000',
 'EAM_Dynamo_MendelevKramerBecker_2008_Al__MO_106969701023_005',
 'EAM_Dynamo_MendelevSrolovitzAckland_2005_AlFe__MO_577453891941_005',
 'EAM_Dynamo_MishinFarkasMehl_1999_Al__MO_651801486679_005',
 'EAM_Dynamo_MishinMehlPapaconstantopoulos_2002_NiAl__MO_109933561507_005',
 'EAM_Dynamo_Mishin_2004_NiAl__MO_101214310689_005',
 'EAM_Dynamo_PunMishin_2009_NiAl__MO_751354403791_005',
 'EAM_Dynamo_PunYamakovMishin_2013_AlCo__MO_678952612413_000',
 'EAM_Dynamo_PunYamakovMishin_2013_NiAlCo__MO_826591359508_000',
 'EAM_Dynamo_SchopfBrommerFrigan_2012_AlMnPd__MO_137572817842_000',
 'EAM_Dynamo_SturgeonLaird_2000_Al__MO_120808805541_005',
 'EAM_Dynamo_VailheFarkas_1997_CoAl__MO_284963179498_005',
 'EAM_Dynamo_WineyKubotaGupta_2010_Al__MO_149316865608_005',
 'EAM_Dynamo_Zhakhovsky_2009_Al__MO_519613893196_000',
 'EAM_Dynamo_ZhouJohnsonWadley_2004NISTretabulation_Al__MO_060567868558_000',
 'EAM_Dynamo_ZhouJohnsonWadley_2004_Al__MO_131650261510_005',
 'EAM_Dynamo_ZhouWadleyJohnson_2001_Al__MO_049243498555_000',
 'EAM_Dynamo_ZopeMishin_2003_Al__MO_664470114311_005',
 'EAM_Dynamo_ZopeMishin_2003_TiAl__MO_117656786760_005',
 'EAM_ErcolessiAdams_1994_Al__MO_324507536345_003',
 'EAM_IMD_BrommerGaehler_2006A_AlNiCo__MO_122703700223_003',
 'EAM_IMD_BrommerGaehler_2006B_AlNiCo__MO_128037485276_003',
 'EAM_IMD_SchopfBrommerFrigan_2012_AlMnPd__MO_878712978062_003',
 'EAM_QuinticClampedSpline_ErcolessiAdams_1994_Al__MO_450093727396_002',
 'EAM_QuinticHermiteSpline_ErcolessiAdams_1994_Al__MO_781138671863_002',
 'EMT_Asap_Standard_JacobsenStoltzeNorskov_1996_AlAgAuCuNiPdPt__MO_115316750986_001',
 'EMT_Asap_Standard_JacobsenStoltzeNorskov_1996_Al__MO_623376124862_001',
 'MEAM_LAMMPS_AlmyrasSangiovanniSarakinos_2019_NAlTi__MO_958395190627_000',
 'MEAM_LAMMPS_CostaAgrenClavaguera_2007_AlNi__MO_131642768288_000',
 'MEAM_LAMMPS_DongKimKo_2012_CoAl__MO_099716416216_000',
 'MEAM_LAMMPS_JelinekGrohHorstemeyer_2012_AlSiMgCuFe__MO_262519520678_000',
 'MEAM_LAMMPS_JeongParkDo_2018_PdAl__MO_616482358807_000',
 'MEAM_LAMMPS_KimJungLee_2015_NiAlCo__MO_876687166519_000',
 'MEAM_LAMMPS_KimKimJung_2016_AlTi__MO_618133763375_000',
 'MEAM_LAMMPS_KimKimJung_2017_NiAlTi__MO_478967255435_000',
 'MEAM_LAMMPS_KimKimLee_2009_AlMg__MO_058537087384_000',
 'MEAM_LAMMPS_KimSeolJi_2017_PtAl__MO_793141037706_000',
 'MEAM_LAMMPS_KoShimLee_2011_AlH__MO_127847080751_000',
 'MEAM_LAMMPS_LeeLee_2010_FeAl__MO_332211522050_000',
 'MEAM_LAMMPS_PascuetFernandez_2015_AlU__MO_596300673917_000',
 'MEAM_LAMMPS_PascuetFernandez_2015_Al__MO_315820974149_000',
 'MEAM_LAMMPS_ShimKoKim_2013_AlVH__MO_344724145339_000',
 'Morse_Shifted_GirifalcoWeizer_1959HighCutoff_Al__MO_140175748626_004',
 'Morse_Shifted_GirifalcoWeizer_1959LowCutoff_Al__MO_411898953661_004',
 'Morse_Shifted_GirifalcoWeizer_1959MedCutoff_Al__MO_279544746097_004',
 'Sim_LAMMPS_ADP_ApostolMishin_2011_AlCu__SM_667696763561_000',
 'Sim_LAMMPS_ADP_StarikovGordeevLysogorskiy_2020_SiAuAl__SM_113843830602_000',
 'Sim_LAMMPS_AGNI_BotuBatraChapman_2017_Al__SM_666183636896_000',
 'Sim_LAMMPS_BOP_ZhouWardFoster_2016_AlCu__SM_566399258279_000',
 'Sim_LAMMPS_MEAM_AlmyrasSangiovanniSarakinos_2019_NAlTi__SM_871795249052_000',
 'Sim_LAMMPS_MEAM_JelinekGrohHorstemeyer_2012_AlSiMgCuFe__SM_656517352485_000',
 'Sim_LAMMPS_MEAM_PascuetFernandez_2015_AlU__SM_721930391003_000',
 'Sim_LAMMPS_MEAM_PascuetFernandez_2015_Al__SM_811588957187_000',
 'Sim_LAMMPS_SMTBQ_SallesPolitanoAmzallag_2016_AlO__SM_853967355976_000',
 'Sim_LAMMPS_SMTBQ_SallesPolitanoAmzallag_2016_Al__SM_404097633924_000']

From the above let us select the first potential in the list.

pot = job.list_potentials()[0]
print ('Selected potential: ', pot)
job.potential = pot
Selected potential:  1995--Angelo-J-E--Ni-Al-H--LAMMPS--ipr1

To run the LAMMPS simulation (locally) we now simply use:

#job.server.core=2
#job.server.queue=
job.run()
The job Al_T800K was saved and received the ID: 96
job.executable
'/home/muhammad/miniconda3/envs/workshop_test/share/pyiron/lammps/bin/run_lammps_2020.03.03.sh'
pr.job_table()
id status chemicalformula job subjob projectpath project timestart timestop totalcputime computer hamilton hamversion parentid masterid
0 96 finished Al108 Al_T800K /Al_T800K /home/muhammad/ workspace/tmp/PMD-workflow-workshop2/1_1_intro_pyiron_building_blocks/first_steps/ 2021-04-28 23:47:01.459433 2021-04-28 23:47:03.734065 2.0 pyiron@cmleo14#1 Lammps 0.1 None None

Analyze the calculation

After the simulation has finished the information about the job can be accessed through the Project object.

job = pr['Al_T800K']
job
{'groups': ['input', 'output'], 'nodes': ['HDF_VERSION', 'NAME', 'TYPE', 'VERSION', 'server', 'status']}

Printing the job object (note that in Jupyter we don’t have to call a print statement if the variable/object is in the last line). The output lists the variables (nodes) and the directories (groups). To get a list of all variables stored in the generic output we type:

job['output/generic']
{'groups': [], 'nodes': ['cells', 'energy_pot', 'energy_tot', 'forces', 'indices', 'positions', 'pressures', 'steps', 'temperature', 'unwrapped_positions', 'velocities', 'volume']}

An animated 3d plot of the MD trajectories is created by:

job.animate_structure()

To analyze the temperature evolution we plot it as function of the MD step.

temperatures = job['output/generic/temperature']
steps = job['output/generic/steps']
plt.plot(steps, temperatures)
plt.xlabel('MD step')
plt.ylabel('Temperature [K]');
../_images/1_1_intro_41_0.png

Exercise 1

Similarly, we would like to plot the trajectories of the particles. How to access positions of the particles in the outputs:
pos = ?

pos = job['output/generic/positions']
x, y, z = [pos[:, :, i] for i in range(3)]
sel = np.abs(z) < 0.1
fig, axs = plt.subplots(1,1)
axs.scatter(x[sel], y[sel])
axs.set_xlabel('x [$\AA$]')
axs.set_ylabel('y [$\AA$]')
axs.set_aspect('equal', 'box');
../_images/1_1_intro_45_0.png

pyiron GUI

With pyiron GUI, one can explore the result of the simulations in a simplified manner.

GUI = pr.gui()

Exercise2: Perform a series of jobs

We would like now to create a series of jobs to perform the same simulation but with diferent temperatures.
The temperature must be in the range, 200K<T<1200K, with increment of 200K.

for temperature in np.arange(200, 1200, 200):
    job = pr.create_job(pr.job_type.Lammps, 
                        'Al_T{}K'.format(int(temperature)))
    job.structure = supercell_3x3x3
    job.potential = pot     
    job.calc_md(temperature=temperature, 
                pressure=0, 
                n_ionic_steps=10000)
    job.run()
/home/muhammad/miniconda3/envs/workshop_test/lib/python3.8/site-packages/pyiron_base/generic/hdfio.py:361: UserWarning: swmr=True only affects read ('r') mode. For swmr write mode, set f.swmr_mode = True after opening the file.
  with h5py.File(self.file_name, mode="a", libver="latest", swmr=True) as h:
The job Al_T200K was saved and received the ID: 55
/home/muhammad/miniconda3/envs/workshop_test/lib/python3.8/site-packages/pyiron_base/generic/hdfio.py:361: UserWarning: swmr=True only affects read ('r') mode. For swmr write mode, set f.swmr_mode = True after opening the file.
  with h5py.File(self.file_name, mode="a", libver="latest", swmr=True) as h:
The job Al_T400K was saved and received the ID: 56
/home/muhammad/miniconda3/envs/workshop_test/lib/python3.8/site-packages/pyiron_base/generic/hdfio.py:361: UserWarning: swmr=True only affects read ('r') mode. For swmr write mode, set f.swmr_mode = True after opening the file.
  with h5py.File(self.file_name, mode="a", libver="latest", swmr=True) as h:
The job Al_T600K was saved and received the ID: 57
2021-04-27 03:06:28,359 - pyiron_log - WARNING - The job Al_T800K is being loaded instead of running. To re-run use the argument 'delete_existing_job=True in create_job'
/home/muhammad/miniconda3/envs/workshop_test/lib/python3.8/site-packages/pyiron_base/generic/hdfio.py:361: UserWarning: swmr=True only affects read ('r') mode. For swmr write mode, set f.swmr_mode = True after opening the file.
  with h5py.File(self.file_name, mode="a", libver="latest", swmr=True) as h:
The job Al_T1000K was saved and received the ID: 58

To inspect the list of jobs in our current project we type (note that the existing job from the previous excercise at \(T=800\) K has been recognized and not run again):

pr
{'groups': [], 'nodes': ['Al_T800K', 'Al_T200K', 'Al_T400K', 'Al_T600K', 'Al_T1000K']}

We can now iterate over the jobs and extract volume and mean temperature.

vol_lst, temp_lst = [], []
for job in pr.iter_jobs(convert_to_object=False):
    volumes = job['output/generic/volume']
    temperatures = job['output/generic/temperature']
    temp_lst.append(np.mean(temperatures[:-20]))
    vol_lst.append(np.mean(volumes[:-20]))

Then we can use the extracted information to plot the thermal expansion, calculated within the \(NPT\) ensemble. For plotting the temperature values in ascending order the volume list is mapped to the sorted temperature list.

plt.figure()
vol_lst[:] = [vol_lst[np.argsort(temp_lst)[k]] 
              for k in range(len(vol_lst))]
plt.plot(sorted(temp_lst), vol_lst, 
         linestyle='-',marker='o',)
plt.title('Thermal expansion')
plt.xlabel('Temperature [K]')
plt.ylabel('Volume [$\AA^3$]');
../_images/1_1_intro_56_0.png

Sharing your project

You can share the completed project at its current status with others. The sharing is done via pack()/unpack() functions of the Project object.
Before packing the project, let’s take a look at pyiron job_table

pr.job_table()
id status chemicalformula job subjob projectpath project timestart timestop totalcputime computer hamilton hamversion parentid masterid
0 54 finished Al108 Al_T800K /Al_T800K /home/muhammad/ workspace/tmp/PMD-workflow-workshop/Exercises/first_steps/ 2021-04-27 02:43:04.398418 2021-04-27 02:43:06.428305 2.0 pyiron@cmleo14#1 Lammps 0.1 None None
1 55 finished Al108 Al_T200K /Al_T200K /home/muhammad/ workspace/tmp/PMD-workflow-workshop/Exercises/first_steps/ 2021-04-27 03:06:20.410345 2021-04-27 03:06:22.363131 1.0 pyiron@cmleo14#1 Lammps 0.1 None None
2 56 finished Al108 Al_T400K /Al_T400K /home/muhammad/ workspace/tmp/PMD-workflow-workshop/Exercises/first_steps/ 2021-04-27 03:06:22.894415 2021-04-27 03:06:24.984784 2.0 pyiron@cmleo14#1 Lammps 0.1 None None
3 57 finished Al108 Al_T600K /Al_T600K /home/muhammad/ workspace/tmp/PMD-workflow-workshop/Exercises/first_steps/ 2021-04-27 03:06:25.604594 2021-04-27 03:06:27.670037 2.0 pyiron@cmleo14#1 Lammps 0.1 None None
4 58 finished Al108 Al_T1000K /Al_T1000K /home/muhammad/ workspace/tmp/PMD-workflow-workshop/Exercises/first_steps/ 2021-04-27 03:06:28.976964 2021-04-27 03:06:31.090220 2.0 pyiron@cmleo14#1 Lammps 0.1 None None

To export the project:

pr.pack(csv_file_name='export.csv',destination_path='lammps_project')

Create a series of projects (Optional)

We extend the previous example and compute the thermal expansion for three of the available aluminum potentials. First, let us create a new pyiron project named ‘Al_potentials’. We can use the information of the previously run job ‘Al_T200K’ of the ‘first_steps’ project to find all the compatible potentials.

pr = Project('Al_potentials')
pot_lst = pr['../first_steps/Al_T200K'].load_object().list_potentials()[:3]
pot_lst
['Al_Mg_Mendelev_eam', 'Zope_Ti_Al_2003_eam', 'Al_H_Ni_Angelo_eam']

Note again that list_potentials() automatically only returns the potentials that are compatible with the structure (chemical species) and the job type.

We can now loop over the selected potentials and run the MD simulation for the desired temperature values for any of the potentials.

for pot in pot_lst:
    print ('Interatomic potential used: ',pot)
    pr_pot = pr.create_group(pot)
    for temperature in np.arange(200, 1200, 200):
        job = pr_pot.create_job(pr.job_type.Lammps, 
                                'Al_T{}K'.format(int(temperature)))
        job.structure = supercell_3x3x3
        job.potential = pot                
        job.calc_md(temperature=temperature, 
                    pressure=0, 
                    n_ionic_steps=10000)
        job.run()
Interatomic potential used:  Al_Mg_Mendelev_eam
Interatomic potential used:  Zope_Ti_Al_2003_eam
Interatomic potential used:  Al_H_Ni_Angelo_eam

With the pr.create_group() command a new subproject (directory) is created named here by the name of the potential.

For any particular potential the thermal expansion data can be obtained again by looping over the jobs performed using that potential. To obtain the thermal expansion curves for all the potentials used we can simply iterate over the subprojects (directories) created above by using the pr.iter_groups() command.

for p in pr.iter_groups():
    vol_lst, temp_lst = [], []
    for out in p.iter_jobs(path='output/generic'):
        volumes = out['volume']
        temperatures = out['temperature']
        temp_lst.append(np.mean(temperatures[:-20]))
        vol_lst.append(np.mean(volumes[:-20]))
    # Plot only if there is a job in that group
    if len(p.get_job_ids()) > 0:
        plt.plot(temp_lst, vol_lst, 
                 linestyle='-',marker='o',
                 label=p.name) 
plt.legend(loc='best')    
plt.title('Thermal expansion for different interatomic potentials')
plt.xlabel('Temperature [K]')
plt.ylabel('Volume [$\AA^3$]');
../_images/1_1_intro_71_0.png