Package: excitingtools
excitingtoolsexcitingtoolsis a collection of
modules to facilitate the generation ofexcitinginputs and the post-processing ofexcitingoutputs.excitingtoolscurrently provides functionality for:Generation of theexcitinginput XML file
using Python classes:Automatically supported for the whole input file through dynamic class constructionCurrently tested forgroundstate,structure,BSEandbandstructureParsing ofexcitingoutputs into Python dictionariesHigh-level class API for interacting with results:Currently implemented for eigenvalues, band structure and DOS (without SO coupling)making it is possible to define a calculation, run it, and parse the relevant outputs all from within Python.excitingtoolsis used by, or in conjunction with:exciting'sregression-testing frameworkParsing of output dataexciting'sJupyter notebook tutorialsData handlingAtomic Simulation Environment (ASE)Input and output handling in ASE'sexcitingcalculatorJobflowFor the development of complex, automatedexcitingworkflowsInstallationIf one wishes to importexcitingtoolsin their own scripts, it can be installed from this project's root directory
($EXCITING_ROOT/tools/exciting_tools).Although not strictly necessary, it is strongly recommended to use a virtual environment to manage Python packages.
There are several solutions available, includingvenv, (mini)condaandpipx.To set up a venv:# Create a directory `excitingvenv` containing our venvpython3-mvenvexcitingvenv# Activate the environmentsourceexcitingvenv/bin/activateBefore installing excitingtools, you should upgradepipandsetuptools:python3-mpipinstall--upgrade--forcepip
pipinstall--upgradesetuptoolsNo matter which type of environment or none is used, it should always be verified that the Python version is compatible withexcitingtools(>=3.7) and thatpipis up-to-date (>=22.0) and also points to the same Python version.Now you can proceed to installexcitingtoolsfromexciting's's root:python3-mpipinstalltools/exciting_toolsAlternatively, you can download it directly from PyPI:pipinstallexcitingtoolsExternal Package DependenciesIf a new external dependency is introduced to the package, this also requires adding topyproject.tomlsuch that pip is aware
of the new dependency.Basic File StructureIn general, modules should begin with a docstring giving an overview of the module's purpose. External python
libraries should then be imported, followed by a space, then local modules belonging toexcitingtools. Local modules
should be loaded with absolute paths rather than relative paths or prepending the system pathsys.path.insert(0,'/path/to/module_directory'):"""
Functions that operate on lattice vectors 
"""
import numpy as np

from excitingtools.maths.math_utils import triple_productExposed modules, forming user API, should be defined in__init__.pywhere ever possible.Code FormattingWe currently favouryapfformatter, which by default applies PEP8 formatting to
the code.After installing yapf, if you are in the root directory of excitingtools, you can simply type:yapf-iexcitingtools/path/to/file.pyand it will do the formatting for you. Note: This will automatically use our custom.style.yapfstyle-file.DocumentationWriting DocumentationAll functions and classes should be documented. The favoured docstring isreStructuredText:classSimpleEquation:defdemo(self,a:int,b:int,c:int)->list:"""Function definition.:param int a: quadratic coefficient:param int b: linear coefficient:param c: free term:type c: int:return list y: Function values"""where the type can be specified in theparamdescription, or separately using thetypetag. For more details on the
documentation syntax, please refer to thislink. Thegoogle style guideforreStructuredTextdocstrings is also
acceptable to follow.Generating DocumentationDocumentation can straightforwardly be generated using thepdocpackage:pipinstallpdoc
pdoc-odocumentation-drestructuredtext--mathexcitingtools/TODO(Alex) Issue 57. Set up generation of documentation from docstrings, with SphinxBasic UsageInput XML Generationexcitingtoolsmaps the XML tags and attributes
ofinput.xmlonto Python classes, enabling the generation of XML-formatted inputs directly from Python. A simple
ground state calculation could like this:importaseimportnumpyasnpfromexcitingtoolsimportExcitingStructure,ExcitingGroundStateInput,ExcitingInputXML# Lattice and positions in angstrom, as expected by ASElattice=np.array([[3.168394160510246,0.0,0.0],[-1.5841970805453853,2.7439098312114987,0.0],[0.0,0.0,39.58711265]])positions=np.array([[0.00000000,0.00000000,16.68421565],[1.58419708,0.91463661,18.25982194],[1.58419708,0.91463661,15.10652203],[1.58419708,0.91463661,22.90251866],[0.00000000,0.00000000,24.46831689],[0.00000000,0.00000000,21.33906353]])symbols=['W','S','S','Mo','S','S']atoms=ase.atoms.Atoms(symbols=symbols,positions=positions,cell=lattice)structure=ExcitingStructure(atoms,species_path='.')ground_state=ExcitingGroundStateInput(rgkmax=8.0,do="fromscratch",ngridk=[6,6,6],xctype="GGA_PBE_SOL",vkloff=[0,0,0],tforce=True,nosource=False)input_xml=ExcitingInputXML(structure=structure,groundstate=ground_state,title="My exciting Crystal")input_xml.write("input.xml")Here we defined the attributes required to perform a ground state calculation as seperate classes, and composed the
final XML string withExcitingInputXMLclass. If the user does not have access to ASE, they can instead use aList[dict]to define the container with atoms data:atoms=[{'species':'W','position':[0.00000000,0.00000000,16.68421565]},{'species':'S','position':[1.58419708,0.91463661,18.25982194]},{'species':'S','position':[1.58419708,0.91463661,15.10652203]},{'species':'Mo','position':[1.58419708,0.91463661,22.90251866]},{'species':'S','position':[0.00000000,0.00000000,24.46831689]},{'species':'S','position':[0.00000000,0.00000000,21.33906353]}]structure=ExcitingStructure(atoms,lattice,species_path='.')Additional examples can be found in the test cases,exciting_tools/tests/input. We note that not all XML tags
currently map onto Python classes. One can consultexciting_tools/excitingtools/inputto see what is available.
Development follows a continuous integration and deployment workflow, therefore if one wishes for additional features,
please make a request on GitHub issues or open a merge request.Binary ExecutionNext we can define a runner and run our calculation:fromexcitingtools.runner.runnerimportBinaryRunnerrunner=BinaryRunner('exciting_smp',run_cmd='',omp_num_threads=4,time_out=500)run_status=runner.run()Parsing OutputsAfter the successful completion of the calculation, we can parse the relevant output files as dictionaries, usingparser_chooser. These are the main files one would be interested in after performing a ground state calculation, for
example:fromexcitingtoolsimportparser_chooserinfo_out:dict=parser_chooser("INFO.OUT")eigval_info:dict=parser_chooser("eigval.xml")atoms_info:dict=parser_chooser("atoms.xml")A full list of parsers is provided inexcitingtools/exciting_dict_parsers/parser_factory.py. If we wish to perform
analysis of the data,excitingtoolsprovides
classes with high-level API. To perform a band structure plot using theBandDataclass:""" Plot silicon band structure"""importmatplotlib.pyplotaspltfromexcitingtools.exciting_obj_parsers.ks_band_structureimportparse_band_structurefromexcitingtools.dataclasses.band_structureimportBandDataband_data:BandData=parse_band_structure("bandstructure.xml")vertices,labels=band_data.band_path()ha_to_ev=27.2114fig,ax=plt.subplots(figsize=(6,9))ax.set_xticks(vertices)ax.set_xticklabels(labels)plt.ylabel('Energy (eV)')# Font sizesax.yaxis.label.set_size(20)ax.tick_params(axis='both',which='major',labelsize=20)# Vertical lines at high symmetry pointsforxinvertices:plt.axvline(x,linestyle='--',color='black')# Fermi referencee_fermi=0.0# Number of valence bandsn_valence=4# Colour valence and conduction bands differentlyline_colour={key:'blue'forkeyinrange(0,n_valence)}line_colour.update({key:'red'forkeyinrange(n_valence,band_data.n_bands)})foribinrange(0,band_data.n_bands):plt.plot(band_data.flattened_k_points,ha_to_ev*band_data.bands[:,ib],color=line_colour[ib])Tests demonstrating further usage are present inexcitingtools/tests/dataclasses. We note that the high-level objects
and their parsers are separated. In principle, the data classes should only define a sensible schema or API for
accepting relevant data, rather than know anything about the parsing. Object parsers (defined inobj_parsers) by definition
should return to data classes, but the data classes dictate the format of the data, not vice versa.TestingEvery function should have a test where possible, unless the function is correct by inspection. The naming convention
for a module calledmodule.pyis to prepend it withtest_, which allows it to be automatically recognised and run
bypytest:excitingtools/module.py# Collection of functionstests/test_module.py# Collection of tests for functions in module.pyTests are intended to be run usingpytest, for which the documentation can be foundhere.
One is able to runpytestfrom theexciting_toolsroot with no arguments. By default, all test files, classes and functions defined in the specification,exciting_tools/pytest.ini,  will get executed.ParsersThe parsers are used in the test suite. Therefore, they should only return dictionaries with a specific structure.The tolerance comparison will only evaluate the values of lowest-nested keys. As such, one should consider how they structure the parsed data.
For example, it makes more sense to structure data like:{‘wannier1’:{‘localisation_vector’:np.array(shape=(3)),‘Omega’:float}}such that the tolerances will be w.r.t.localisation_vector, andOmega, rather than using the structure:{‘localisation_vector’:{‘wannier1’:np.array(shape=(3))‘wannier2’:np.array(shape=(3))},‘Omega’:{‘wannier1’:float‘wannier2’:float}}which will results in tolerances defined w.r.t.wannier1andwannier2. One can see in the latter case, there is no distinction betweenlocalisation_vectorandOmega. In general, we’re more likely to want to set different tolerances for different properties, rather than for different functions with the same set of properties.
One could also structure the data like:{‘localisation_vector’:np.array(shape=(n_wannier,3)),‘Omega’::np.array(shape=(n_wannier)}where the less serialised data removes the key nesting.Uploading to PyPiexcitingtools is available as a separate package on PyPi. In order to upload a new version:# Ensure twine is installedpip3install--upgradetwinebuild# Build the wheelscd$EXCITINGROOT/tools/exciting_tools
python3-mbuild# Test the distribution and uploading (one requires a test-PyPi account)twinecheckdist/*
twineupload--repository-urlhttps://test.pypi.org/legacy/dist/*# Upload to PyPitwineuploaddist/*Before doing so, please ensure the semantic versioning is appropriately updated inpyproject.toml.ContributorsThe following people (in alphabetic order by their family names) have contributed to excitingtools:Alexander BuccheriHannah KleineMartin KubanBenedikt MaurerFabian PeschelDaniel SpeckhardElisa StephanMara Voiculescu