Single point and general calculation#
Note
This tutorial is can be executed as a jupyter notebook.
Please copy the mock_registry folder found to your directory of execution, otherwise the mock
code cannot locate the pre-computed calculations to be used as dummy output.
VaspBuilderUpdater provides a simplified interface for setting up calculations
using pre-define default inputs for calculation and for workflow execution.
In jupyter notebook, we normally need to load the necessary AiiDA environment:
%load_ext aiida
%aiida
However, for this tutorial, we will use a temporary AiiDA profile. All results will be destroyed once the session close.
We also assume that you have already installed the aiida-vasp plugin and the VASP has been installed locally on your computer.
If you do not have VASP available, run the code cell below to activate mock-vasp which will
use cached results instead of running actual VASP calculations.
Setting up the basic environment#
Before we start any calculation, we need to do a few basic setups:
Setup a
Computernode, which is this computer (localhost)Tell AiiDA where to find the VASP executable, or using the
mock-vaspexecutable.Upload the pseduopotentials (POTCAR) family
The following code creates Computer node:
from aiida_vasp.utils.temp_profile import *
print(load_temp_profile())
# Uncomment the below line to create a localhost Computer if you have not done so
comp = orm.Computer('localhost', 'localhost', transport_type='core.local', scheduler_type='core.direct')
comp.store()
# Some configuration may be needed for first-time user
comp.set_workdir('/tmp/aiida_run/')
comp.configure()
Profile<uuid='ebbc0c352b4d4934a41fcc54b6f9682e' name='myprofile'>
<aiida.orm.authinfos.AuthInfo at 0x7a191c183c90>
Here we use mock-vasp to simulate the VASP executable.
from pathlib import Path
import os
vasp_path = !which mock-vasp
vasp_code = orm.InstalledCode(comp, vasp_path[0], default_calc_job_plugin='vasp.vasp')
print(vasp_path[0])
vasp_code.label ='mock-vasp'
vasp_code.store()
os.environ['MOCK_VASP_REG_BASE'] = str((Path() / 'mock_registry').absolute())
os.environ['MOCK_VASP_UPLOAD_PREFIX'] = 'singlepoint'
print(os.environ['MOCK_VASP_REG_BASE'])
/home/docs/checkouts/readthedocs.org/user_builds/aiida-vasp-plugin/envs/v4.1.0/bin/mock-vasp
/home/docs/checkouts/readthedocs.org/user_builds/aiida-vasp-plugin/checkouts/v4.1.0/docs/source/tutorials/mock_registry
If you have VASP installed, uncomment and run the the code below to create the InstalledCode node`:
#vasp_path = !which vasp_std
#vasp_code = orm.InstalledCode(comp, vasp_path[0], default_calc_job_plugin='vasp.vasp')
#vasp_code.label ='vasp'
#vasp_code.store()
Hint
Setting the MOCK_VASP_VASP_CMD environment variable will allow the mock-vasp to run real VASP
calculation and add the results to the registry whenever needed.
This environmental variable should be set when generating the test/demo data.
We also need to upload the pseudopotential family, here we use the sample Si POTCAR with a family name of PBE.EXAMPLE:
from aiida_vasp.data.potcar import PotcarData, PotcarFileData
from pathlib import Path
print(PotcarData.upload_potcar_family(str(Path('potcars').absolute()), "PBE.EXAMPLE", "PBE.EXAMPLE"))
(1, 1, 1)
Running the calculation#
We can now set up the calculation, but first we need to create a StructureData node for the Si structure. Here we use the ase package to create the structure.
from ase.build import bulk
si = bulk('Si', 'diamond', 5.4)
si_node = orm.StructureData(ase=si)
Tip
It is also possible to create a StructureData node from a pymatgen.core.Structure object.
from pymatgen.core import Structure
si = Structure.from_spacegroup('Fm-3m', Lattice.cubic(5.4), ['Si'], [[0, 0, 0]]).get_primitive_structure()
si_node = orm.StructureData(pymatgen=si)
using the VaspBuilderUpdater class:
from aiida import orm
from aiida_vasp.workchains.v2 import VaspBuilderUpdater
# This instantiate a VaspBuilderUpdater object and apply the preset
# The default name is VaspPreset stored in the code repository.
# You can place your own preset at ~/.aiida-vasp/ and use them for production
# calculations.
upd = VaspBuilderUpdater().apply_preset(si_node, code='mock-vasp@localhost')
# This preset defaults to use the PBE.54 family, we switch to 'PBE.EXAMPLE' family
# just uploaded.
upd.builder.potential_family = 'PBE.EXAMPLE'
The code block above create a VaspBuilderUpdater object and apply the preset for the Si structure.
We can verify that this configures the ProcessBuilder object with the correct inputs:
upd.builder
Process class: VaspWorkChain
Inputs:
code: mock-vasp@localhost
dynamics: {}
kpoints_spacing: 0.05
metadata:
label: ''
options:
max_wallclock_seconds: 3600
resources:
num_machines: 1
tot_num_mpiprocs: 1
withmpi: false
parameters:
incar:
algo: fast
ediff: 2.0e-06
encut: 250
gga: ps
ismear: 0
ispin: 2
lasph: true
lcharg: false
lreal: false
lvhar: true
lwave: false
nedos: 100
nelm: 200
nelmin: 4
nwrite: 1
prec: normal
sigma: 0.05
potential_family: PBE.EXAMPLE
potential_mapping:
Si: Si
settings: {}
structure: Si
If this looks all fine, we can run the calculation using the run_get_node method:
results = upd.run_get_node()
results
09/13/2025 06:23:45 AM <1105> aiida.orm.nodes.process.workflow.workchain.WorkChainNode: [REPORT] [15|VaspWorkChain|run_process]: launching VaspCalculation<18> iteration #1
09/13/2025 06:23:47 AM <1105> aiida.orm.nodes.process.workflow.workchain.WorkChainNode: [REPORT] [15|VaspWorkChain|results]: work chain completed after 1 iterations
09/13/2025 06:23:48 AM <1105> aiida.orm.nodes.process.workflow.workchain.WorkChainNode: [REPORT] [15|VaspWorkChain|on_terminated]: cleaned remote folders of calculations: 18
ResultAndNode(result={'remote_folder': <RemoteData: uuid: 7eefbe30-451a-4efa-b3a9-86137e95fa39 (pk: 19)>, 'retrieved': <FolderData: uuid: 8eae9fc5-ae2e-4076-8087-02ce487f0a07 (pk: 20)>, 'misc': <Dict: uuid: f4b7e8c7-7158-4dba-904a-8b3558345cbc (pk: 21)>}, node=<WorkChainNode: uuid: 9313856c-38c3-4eaa-988e-684fdd9dd3d9 (pk: 15) (aiida.workflows:vasp.v2.vasp)>)
Accessing the results#
The returned results contains the outputs as well as the WorkChainNode object representing the workflow that was executed.
workchain_node = results.node
workchain_node
<WorkChainNode: uuid: 9313856c-38c3-4eaa-988e-684fdd9dd3d9 (pk: 15) (aiida.workflows:vasp.v2.vasp)>
Note that our workchain_node has a pk as well as a uuid. Both of them are identifiers for the node. You can load the node using the load_node method:
from aiida.orm import load_node
node = load_node(workchain_node.pk)
at a later time to access the results.
Hint
Although pk and uuid are both unique identifiers for this node, they serve different purposes. The pk is a unique identifier within the current aiida database, while the uuid is a truelly unique identifier that would always refer to the same node, even if the data is exported and imported to different databases.
Useful information such as the total energy, forces and stresses are stored in the misc output node:
workchain_node.outputs.misc.get_dict()
{'total_energies': {'energy_extrapolated': -10.61487473,
'energy_extrapolated_electronic': -10.61487473},
'stress': [[17.87702469, 0.0, 0.0],
[0.0, 17.87702469, 0.0],
[0.0, 0.0, 17.87702469]],
'forces': [[0.0, -0.0, -0.0], [-0.0, 0.0, 0.0]],
'fermi_level': 6.19018828,
'band_properties': {'cbm': 6.407,
'vbm': 5.979,
'is_direct_gap': False,
'band_gap': 0.428},
'version': '6.2.0',
'magnetization': [-0.0004385],
'site_magnetization': {'sphere': {'x': {'site_moment': {},
'total_magnetization': {}},
'y': {'site_moment': {}, 'total_magnetization': {}},
'z': {'site_moment': {}, 'total_magnetization': {}}},
'full_cell': [-0.0004385]},
'run_stats': {'mem_usage_base': 30000.0,
'mem_usage_nonl-proj': 471.0,
'mem_usage_fftplans': 1184.0,
'mem_usage_grid': 2809.0,
'mem_usage_one-center': 12.0,
'mem_usage_wavefun': 2160.0,
'total_cpu_time_used': 3.335,
'user_time': 3.276,
'system_time': 0.059,
'elapsed_time': 3.614,
'maximum_memory_used': 99984.0,
'average_memory_used': None},
'run_status': {'nelm': 200,
'nsw': 0,
'last_iteration_index': [1, 13],
'finished': True,
'ionic_converged': None,
'electronic_converged': True,
'consistent_nelm_breach': False,
'contains_nelm_breach': False,
'nbands': 9},
'notifications': []}
The advantage of using a database like AiiDA is that we can easily query for the results of our calculations. For example, to get all calculations that used the PBE.EXAMPLE family:
from aiida.orm import QueryBuilder
from aiida.plugins import WorkflowFactory
q = QueryBuilder()
q.append(orm.WorkChainNode, tag='workchain', project=['*'])
q.append(orm.Str, with_outgoing='workchain', edge_filters={'label': 'potential_family'},
filters={'attributes.value': 'PBE.EXAMPLE'})
q.all()
[[<WorkChainNode: uuid: 9313856c-38c3-4eaa-988e-684fdd9dd3d9 (pk: 15) (aiida.workflows:vasp.v2.vasp)>]]
The QueryBuilder object allows us to construct complex queries path that combine different nodes and their properties.
The first append method says that we need a WorkChainNode and we want to return the node itself.
The second append method defines that such WorkChainNode should have an outgoing Dict node with a label of potential_family and a value of PBE.EXAMPLE. This filters the results to only include nodes that used the PBE.EXAMPLE family.
The final q.all() method returns a list of all the combinations of node and edges that match the query.