diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4191de1ff..e0a682b8b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,10 +11,10 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set up Python 3.7 + - name: Set up Python 3.8 uses: actions/setup-python@v2 with: - python-version: 3.7 + python-version: 3.8 - name: Cache python dependencies id: cache-pip @@ -26,13 +26,14 @@ jobs: pip-docs- - name: Install python dependencies - run: + run: | pip install -e .[docs] + reentry scan - name: Build documentation env: READTHEDOCS: 'True' - run: + run: | SPHINXOPTS='-nW' make -C docs html pre-commit: @@ -63,7 +64,7 @@ jobs: pip freeze - name: Run pre-commit - run: + run: | pre-commit run --all-files || (git status --short; git diff ; exit 1) tests: diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 000000000..9e05f3497 --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,20 @@ +# Required +version: 2 + +# Build documentation in the docs/ directory with Sphinx +sphinx: + builder: html + configuration: docs/source/conf.py + +# Optionally build your docs in additional formats such as PDF +formats: + - pdf + +# Optionally set the version of Python and requirements required to build your docs +python: + version: 3.8 + install: + - requirements: docs/requirements_for_rtd.txt + - method: pip + path: . + system_packages: true diff --git a/AUTHORS.txt b/AUTHORS.txt index 9927ffccd..c7fadc810 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -5,7 +5,7 @@ Vasily Tseplyaev (v.tseplyaev@fz-juelich.de) 2018-today Anoop Chandran (a.chandran@fz-juelich.de) 2019-today Henning Janssen (he.janssen@fz-juelich.de) 2020-today -We would also like to thank the following people for their contibution: +We would also like to thank the following people for their contribution: ... diff --git a/CHANGELOG.md b/CHANGELOG.md index 3cf301680..fba7a89fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,22 @@ +## v.1.2.0 +### release compatible with AiiDA-core 1.3.0+ +possibly ready for aiida-core 2.0.0 +- supports Fleur MaXR4 and MaXR5 versions with new inpgen +MaXR4 requires providing versions in the code nodes +- Some features relying on the id in the inpgen files, +may be broken by the new inpgen interface change when using MaXR5.1 +- Added support for GW calculations with Spex, and the Strain workchain +- Major code refactoring, moving all xml tools to masci-tools (therefore requires masci-tools >=0.4.8) +- Also all file parsers are overworked and moved to masci-tools +- Work over of the BanddosWorkChain. +- FleurinpData now consistently supports more included xml files (kpts.xml, sym.xml, ...) +- Added new modification functions to the FleurinpModifier for kpoint manipulation for Max5 + + ## v.1.1.4 ### release compatible with AiiDA-core 1.3.0 -### still support of Fleur MaXR4 version with inpgen -### Does not support yet Fleur MaXR5 and new inpgen +- still support of Fleur MaXR4 version with inpgen +- Does not support yet Fleur MaXR5 and new inpgen - Fixed numpy dependency issue with aiida-common-workflows and quantum mobile ## v1.1.3 diff --git a/MANIFEST.in b/MANIFEST.in index 74dea3b67..d4f662a19 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,8 +1,6 @@ include aiida_fleur/tools/exp_bindingenergies.json include aiida_fleur/fleur_schema/input/*/*.xsd -include ./tests/run* -recursive-include ./tests/files/ * include setup.json include setup_requirements.txt include AUTHORS.txt diff --git a/README.md b/README.md index a445d85c1..e8cf88d76 100644 --- a/README.md +++ b/README.md @@ -21,11 +21,15 @@ Developed at [Forschungszentrum Jülich GmbH](http://www.fz-juelich.de/pgi/pgi-1 | `v1.0.0 < v2.0.0` | | [![PyPI pyversions](https://img.shields.io/pypi/pyversions/aiida-fleur.svg)](https://pypi.org/project/aiida-fleur.svg) | MaXR1 < MaXR5 (v0.29)| | `< v0.6.3` | | [![PyPI pyversions](https://img.shields.io/pypi/pyversions/aiida-fleur/0.6.svg)](https://pypi.python.org/pypi/aiida-fleur/0.6.3/) | MaXR1 < MaXR3 (v0.28)| -### Documentation +### Documentation and User Support Hosted at http://aiida-fleur.readthedocs.io/en/develop/index.html. For other information see the AiiDA-core docs or http://www.flapw.de. +Users can post any questions in the Fleur user [forum](http://fleur.xobor.de/) + +For bugs, feature requests and further issues please use the issue tracker on github of the aiida-fleur repository. + ### License: MIT license. @@ -94,7 +98,6 @@ filename | Description ---------|------------ Structure_util.py | Constains some methods to handle AiiDA structures (some of them might now be methods of the AiiDA structureData, if so use them from there!) merge_parameter.py | Methods to handle parameterData nodes, i.e merge them. Which is very useful for all-electron codes, because instead of pseudo potentialsfamilies you can create now families of parameter nodes for the periodic table. -xml_util.py | All xml functions that are used, by parsers and other tools are in here. Some are 'general' some a very specific to Fleur. read_cif.py | This can be used as stand-alone to create StructureData nodes from .cif files from an directory tree. Utility and tools, which are independend of AiiDA are moved to the [masci-tools](https://github.com/JuDFTteam/masci-tools) (material science tools) repository, @@ -214,11 +217,10 @@ Usage examples are shown in 'examples'. ## Acknowledgements -Besides the Forschungszentrum Juelich, this work is supported by the [MaX -European Centre of Excellence]() funded by the Horizon 2020 EINFRA-5 program, -Grant No. 676598. +Besides the Forschungszentrum Juelich, this work is supported by the European MaX Centre of Excellence 'Materials design at the Exascale' [MaX]() funded by the Horizon 2020 EINFRA-5 program, Grant No. 676598 and under grant agreement No. 824143. This work is further supported by the Joint Lab Virtual Materials Design (JLVMD) of the Forschungszentrum Jülich. For this work essential is AiiDA, which itself is supported by the [MARVEL National Centre for Competency in Research]() funded by the [Swiss National Science Foundation](). -![MaX](docs/source/images/MaX.png) +MaX +JLVMD diff --git a/aiida_fleur/__init__.py b/aiida_fleur/__init__.py index 4c2c3ee99..229012a86 100644 --- a/aiida_fleur/__init__.py +++ b/aiida_fleur/__init__.py @@ -12,4 +12,4 @@ ''' AiiDA-FLEUR ''' -__version__ = '1.1.4' +__version__ = '1.2.0' diff --git a/aiida_fleur/calculation/fleur.py b/aiida_fleur/calculation/fleur.py index 344407326..7116ae110 100644 --- a/aiida_fleur/calculation/fleur.py +++ b/aiida_fleur/calculation/fleur.py @@ -78,6 +78,7 @@ class FleurCalculation(CalcJob): _DOSINP_FILE_NAME = 'dosinp' _BAND_GNU_FILE_NAME = 'band.gnu' _BAND_FILE_NAME = 'bands.*' + _BANDDOS_FILE_NAME = 'banddos.hdf' # helper files _FLEUR_WARN_ONLY_INFO_FILE_NAME = 'FLEUR_WARN_ONLY' @@ -99,6 +100,9 @@ class FleurCalculation(CalcJob): _NMMPMAT_FILE_NAME = 'n_mmp_mat' _NMMPMAT_HDF5_FILE_NAME = 'n_mmp_mat_out' + #files for greensfunctions + _GREENSF_HDF5_FILE_NAME = 'greensf.hdf' + # files for hybrid functionals _COULOMB1_FILE_NAME = 'coulomb1' _MIXBAS_FILE_NAME = 'mixbas' @@ -188,8 +192,6 @@ class FleurCalculation(CalcJob): 'additional_retrieve_list', 'remove_from_retrieve_list', 'additional_remotecopy_list', 'remove_from_remotecopy_list', 'cmdline' ] - # possible modes? - _fleur_modes = ['band', 'dos', 'forces', 'chargeDen', 'latticeCo', 'scf', 'force_theorem', 'gw', 'ldau'] @classmethod def define(cls, spec): @@ -396,9 +398,13 @@ def prepare_for_submission(self, folder): if modes['band']: mode_retrieved_filelist.append(self._BAND_FILE_NAME) mode_retrieved_filelist.append(self._BAND_GNU_FILE_NAME) + if with_hdf5: + mode_retrieved_filelist.append(self._BANDDOS_FILE_NAME) if modes['dos']: mode_retrieved_filelist.append(self._DOS_FILE_NAME) - if modes['forces']: + if with_hdf5: + mode_retrieved_filelist.append(self._BANDDOS_FILE_NAME) + if modes['relax']: # if l_f="T" retrieve relax.xml mode_retrieved_filelist.append(self._RELAX_FILE_NAME) if modes['ldau']: @@ -406,6 +412,9 @@ def prepare_for_submission(self, folder): mode_retrieved_filelist.append(self._NMMPMAT_HDF5_FILE_NAME) else: mode_retrieved_filelist.append(self._NMMPMAT_FILE_NAME) + if modes['greensf']: + if with_hdf5: + mode_retrieved_filelist.append(self._GREENSF_HDF5_FILE_NAME) if modes['force_theorem']: if 'remove_from_retrieve_list' not in settings_dict: settings_dict['remove_from_retrieve_list'] = [] @@ -514,7 +523,13 @@ def prepare_for_submission(self, folder): # Retrieve by default the output file and the xml file retrieve_list = [] retrieve_list.append(self._OUTXML_FILE_NAME) - retrieve_list.append(self._INPXML_FILE_NAME) + if has_fleurinp: + allfiles = fleurinp.files + for file1 in allfiles: + if file1.endswith('.xml'): + retrieve_list.append(file1) + else: + retrieve_list.append(self._INPXML_FILE_NAME) retrieve_list.append(self._SHELLOUTPUT_FILE_NAME) retrieve_list.append(self._ERROR_FILE_NAME) retrieve_list.append(self._USAGE_FILE_NAME) @@ -526,7 +541,8 @@ def prepare_for_submission(self, folder): retrieve_list.append(self._CDN1_FILE_NAME) for mode_file in mode_retrieved_filelist: - retrieve_list.append(mode_file) + if mode_file not in retrieve_list: + retrieve_list.append(mode_file) self.logger.info('retrieve_list: %s', str(retrieve_list)) # user specific retrieve diff --git a/aiida_fleur/calculation/fleurinputgen.py b/aiida_fleur/calculation/fleurinputgen.py index 6d1a123c2..7fdffa5fc 100644 --- a/aiida_fleur/calculation/fleurinputgen.py +++ b/aiida_fleur/calculation/fleurinputgen.py @@ -27,9 +27,11 @@ from aiida_fleur.data.fleurinp import FleurinpData from aiida_fleur.tools.StructureData_util import abs_to_rel_f, abs_to_rel -from aiida_fleur.tools.xml_util import convert_to_fortran_bool, convert_to_fortran_string from aiida_fleur.common.constants import BOHR_A +from masci_tools.util.xml.converters import convert_to_fortran_bool +from masci_tools.io.common_functions import convert_to_fortran_string + class FleurinputgenCalculation(CalcJob): """ @@ -137,8 +139,7 @@ def define(cls, spec): spec.exit_code(307, 'ERROR_MISSING_RETRIEVED_FILES', message='Some required files were not retrieved.') spec.exit_code(308, 'ERROR_FLEURINPDATA_INPUT_NOT_VALID', - message=('During parsing: FleurinpData could not be initialized, see log. ' - 'Maybe no Schemafile was found or the Fleurinput is not valid.')) + message=('During parsing: FleurinpData could not be initialized, see log. ')) spec.exit_code(309, 'ERROR_FLEURINPDATA_NOT_VALID', message='During parsing: FleurinpData failed validation.') def prepare_for_submission(self, folder): @@ -346,6 +347,9 @@ def prepare_for_submission(self, folder): site_symbol = kind.symbols[0] atomic_number = _atomic_numbers[site_symbol] atomic_number_name = atomic_number + if atomic_number == 0: # 'X' element for vacancies + natoms = natoms - 1 + continue # per default we use relative coordinates in Fleur # we have to scale back to atomic units from angstrom @@ -484,8 +488,15 @@ def prepare_for_submission(self, folder): codeinfo = CodeInfo() # , "-electronConfig"] # TODO? let the user decide -electronconfig? - #cmdline_params = ['-explicit', '-inc', '+all', '-f', '{}'.format(self._INPUT_FILE_NAME)] - cmdline_params = ['-explicit'] + + # We support different inpgen and fleur version via reading the version from the code node extras + code_extras = code.extras + code_version = code_extras.get('version', 32) + if int(code_version) < 32: + # run old inpgen + cmdline_params = ['-explicit'] + else: + cmdline_params = ['-explicit', '-inc', '+all', '-f', '{}'.format(self._INPUT_FILE_NAME)] # user specific commandline_options for command in settings_dict.get('cmdline', []): @@ -618,3 +629,27 @@ def _lowercase_dict(dic, dict_name): return new_dict else: raise TypeError('_lowercase_dict accepts only dictionaries as argument') + + +def write_inpgen_file_aiida_struct(structure, path, input_params=None, settings=None): + """Wraps around masci_tools write inpgen_file, unpacks aiida structure""" + from masci_tools.io.io_fleur_inpgen import write_inpgen_file # pylint: disable=import-error,no-name-in-module + + atoms_dict_list = [] + kind_list = [] + + for kind in structure.kinds: + kind_list.append(kind.get_raw()) + + for site in structure.sites: + atoms_dict_list.append(site.get_raw()) + + report = write_inpgen_file(structure.cell, + atoms_dict_list, + kind_list, + path=path, + pbc=structure.pbc, + input_params=input_params, + settings=settings) + + return report diff --git a/aiida_fleur/cmdline/__init__.py b/aiida_fleur/cmdline/__init__.py index 05ed9c8e8..d3d5e22e5 100755 --- a/aiida_fleur/cmdline/__init__.py +++ b/aiida_fleur/cmdline/__init__.py @@ -14,12 +14,15 @@ ''' import click import click_completion +import difflib +from aiida_fleur import __version__ from aiida.cmdline.params import options, types from .launch import cmd_launch from .data import cmd_data from .workflows import cmd_workflow from .visualization import cmd_plot +from .util import options as options_af # Activate the completion of parameter types provided by the click_completion package # for bash: eval "$(_AIIDA_FLEUR_COMPLETE=source aiida-fleur)" @@ -30,14 +33,54 @@ # less material science specific +class MostSimilarCommandGroup(click.Group): + """ + Overloads the get_command to display a list of possible command + candidates if the command could not be found with an exact match. + """ + + def get_command(self, ctx, cmd_name): + """ + Override the default click.Group get_command with one giving the user + a selection of possible commands if the exact command name could not be found. + """ + cmd = click.Group.get_command(self, ctx, cmd_name) + + # return the exact match + if cmd is not None: + return cmd + + matches = difflib.get_close_matches(cmd_name, self.list_commands(ctx), cutoff=0.5) + + if not matches: + # single letters are sometimes not matched, try with a simple startswith + matches = [c for c in sorted(self.list_commands(ctx)) if c.startswith(cmd_name)][:3] + + if matches: + ctx.fail("'{cmd}' is not a aiida-fleur command.\n\n" + 'The most similar commands are: \n' + '{matches}'.format(cmd=cmd_name, matches='\n'.join('\t{}'.format(m) for m in sorted(matches)))) + else: + ctx.fail(f"'{cmd_name}' is not a aiida-fleur command.\n\nNo similar commands found.") + + return None + + +# Uncomment this for now, has problems with sphinx-click +#@click.command('aiida-fleur', cls=MostSimilarCommandGroup, context_settings={'help_option_names': ['-h', '--help']}) @click.group('aiida-fleur', context_settings={'help_option_names': ['-h', '--help']}) @options.PROFILE(type=types.ProfileParamType(load_profile=True)) +# Note, __version__ should always be passed explicitly here, +# because click does not retrieve a dynamic version when installed in editable mode +@click.version_option(__version__, '-v', '--version', message='AiiDA-FLEUR version %(version)s') def cmd_root(profile): # pylint: disable=unused-argument """CLI for the `aiida-fleur` plugin.""" # To avoid circular imports all commands are not yet connected to the root -# but they have to be here because of bash completion +# but they have to be here because of bash completion on the other hand, this +# makes them not work with the difflib... +# see how aiida-core does it. cmd_root.add_command(cmd_launch) cmd_root.add_command(cmd_data) diff --git a/aiida_fleur/cmdline/data/parameters.py b/aiida_fleur/cmdline/data/parameters.py index cffbb0d74..a79878c63 100644 --- a/aiida_fleur/cmdline/data/parameters.py +++ b/aiida_fleur/cmdline/data/parameters.py @@ -13,9 +13,9 @@ """Command line utilities to create and inspect `Dict` nodes with FLAPW parameters.""" import click -from aiida.cmdline.params import options +from aiida.cmdline.params import options as aiida_options from aiida.cmdline.utils import decorators, echo - +from ..util import options as fleur_options from . import cmd_data @@ -31,7 +31,7 @@ def cmd_parameter(): show_default=True, help='Store also the fleurinp and the extractor calcfunction in the db.') @click.option('--show/--no-show', default=True, show_default=True, help='Print the contents from the extracted dict.') -@options.DRY_RUN() +@aiida_options.DRY_RUN() @decorators.with_dbenv() def cmd_param_import(filename, dry_run, fleurinp, show): """ @@ -47,7 +47,7 @@ def cmd_param_import(filename, dry_run, fleurinp, show): if not fleurinp or dry_run: parameters = fleurinpd.get_parameterdata_ncf() else: - parameters = fleurinpd.get_parameterdata(fleurinpd) + parameters = fleurinpd.get_parameterdata() if dry_run: echo.echo_success('parsed FLAPW parameters') @@ -64,3 +64,42 @@ def cmd_param_import(filename, dry_run, fleurinp, show): # Example show me all for Si # query for options nodes # command to split and merge parameters nodes together based on elements. +@cmd_data.group('options') +def cmd_options(): + """Commands to create and inspect `Dict` nodes containing options.""" + + +@cmd_options.command('create') +@fleur_options.MAX_NUM_MACHINES() +@fleur_options.NUM_MPIPROCS_PER_MACHINE() +@fleur_options.MAX_WALLCLOCK_SECONDS() +@fleur_options.QUEUE_NAME() +@aiida_options.DRY_RUN() +@click.option('--show/--no-show', default=True, show_default=True, help='Print the contents from the options dict.') +@decorators.with_dbenv() +def cmd_option_create(max_num_machines, num_mpiprocs_per_machine, queue, max_wallclock_seconds, dry_run, show): + """ + Command to create options dict nodes + """ + from aiida_fleur.common.node_generators import generate_wf_option_node + + optiondict = {} + optiondict = { + 'resources': { + 'num_machines': max_num_machines, + 'num_mpiprocs_per_machine': num_mpiprocs_per_machine + }, + 'max_wallclock_seconds': max_wallclock_seconds, + 'queue_name': queue + } + + options = generate_wf_option_node(**optiondict) + + if dry_run: + echo.echo_success('Created option node.') + else: + options.store() + echo.echo_success(f'Created and stored Options node <{options.pk}> <{options.uuid}>') + + if show: + echo.echo_dictionary(options.get_dict()) diff --git a/aiida_fleur/cmdline/launch/launch.py b/aiida_fleur/cmdline/launch/launch.py index 8e0720729..d21b0b006 100755 --- a/aiida_fleur/cmdline/launch/launch.py +++ b/aiida_fleur/cmdline/launch/launch.py @@ -30,7 +30,9 @@ @options.CALC_PARAMETERS() @options.SETTINGS() @options.DAEMON() -def launch_inpgen(structure, inpgen, calc_parameters, settings, daemon): +@options.OPTION_NODE() +@options.QUEUE_NAME() +def launch_inpgen(structure, inpgen, calc_parameters, settings, daemon, option_node, queue): """ Launch an inpgen calcjob on given input @@ -41,6 +43,20 @@ def launch_inpgen(structure, inpgen, calc_parameters, settings, daemon): Default structure is Si bulk. """ + options_d = { + 'withmpi': False, + 'max_wallclock_seconds': 6000, + 'resources': { + 'num_machines': 1, + 'num_mpiprocs_per_machine': 1 + }, + 'queue_name': queue + } + if option_node: + opt_dict = option_node.get_dict() + for key, val in opt_dict.items(): + options_d[key] = val + process_class = CalculationFactory('fleur.inpgen') inputs = { 'code': inpgen, @@ -48,16 +64,10 @@ def launch_inpgen(structure, inpgen, calc_parameters, settings, daemon): 'parameters': calc_parameters, 'settings': settings, 'metadata': { - 'options': { - 'withmpi': False, - 'max_wallclock_seconds': 6000, - 'resources': { - 'num_machines': 1, - 'num_mpiprocs_per_machine': 1, - } - } + 'options': options_d } } + inputs = clean_nones(inputs) builder = process_class.get_builder() builder.update(inputs) @@ -75,6 +85,7 @@ def launch_inpgen(structure, inpgen, calc_parameters, settings, daemon): @options.NUM_MPIPROCS_PER_MACHINE() @options.OPTION_NODE() @options.WITH_MPI() +@options.QUEUE_NAME() @click.option('--launch_base/--no-launch_base', is_flag=True, default=True, @@ -82,7 +93,7 @@ def launch_inpgen(structure, inpgen, calc_parameters, settings, daemon): help=('Run the base_fleur workchain, which also handles errors instead ' 'of a single fleur calcjob.')) def launch_fleur(fleurinp, fleur, parent_folder, settings, daemon, max_num_machines, max_wallclock_seconds, - num_mpiprocs_per_machine, option_node, with_mpi, launch_base): + num_mpiprocs_per_machine, option_node, with_mpi, launch_base, queue): """ Launch a base_fleur workchain. If launch_base is False launch a single fleur calcjob instead. @@ -92,20 +103,27 @@ def launch_fleur(fleurinp, fleur, parent_folder, settings, daemon, max_num_machi process_class = CalculationFactory('fleur.fleur') workchain_class = WorkflowFactory('fleur.base') + options_d = { + 'withmpi': with_mpi, + 'queue_name': queue, + 'max_wallclock_seconds': max_wallclock_seconds, + 'resources': { + 'num_machines': max_num_machines, + 'num_mpiprocs_per_machine': num_mpiprocs_per_machine, + } + } + if option_node: + opt_dict = option_node.get_dict() + for key, val in opt_dict.items(): + options_d[key] = val + inputs = { 'code': fleur, 'fleurinpdata': fleurinp, 'parent_folder': parent_folder, 'settings': settings, 'metadata': { - 'options': { - 'withmpi': with_mpi, - 'max_wallclock_seconds': max_wallclock_seconds, - 'resources': { - 'num_machines': max_num_machines, - 'num_mpiprocs_per_machine': num_mpiprocs_per_machine, - } - } + 'options': options_d } } diff --git a/aiida_fleur/cmdline/util/options.py b/aiida_fleur/cmdline/util/options.py index a498a38cc..0a8509393 100755 --- a/aiida_fleur/cmdline/util/options.py +++ b/aiida_fleur/cmdline/util/options.py @@ -109,10 +109,17 @@ show_default=True, help='The maximum wallclock time in seconds to set for the calculations.') +QUEUE_NAME = OverridableOption('-q', + '--queue', + type=click.STRING, + default='', + show_default=True, + help='The queue name to submit to.') + NUM_MPIPROCS_PER_MACHINE = OverridableOption('-M', '--num-mpiprocs-per-machine', type=click.INT, - default=12, + default=2, show_default=True, help='Run the simulation with so many num-mpi-procs-per-machine.') diff --git a/aiida_fleur/common/defaults.py b/aiida_fleur/common/defaults.py index fc45bee68..8726672f0 100644 --- a/aiida_fleur/common/defaults.py +++ b/aiida_fleur/common/defaults.py @@ -21,7 +21,7 @@ }, 'max_wallclock_seconds': 6 * 60 * 60, 'queue_name': '', - 'custom_scheduler_commands': '', - 'import_sys_environment': False, - 'environment_variables': {} + #'custom_scheduler_commands': '', + #'import_sys_environment': False, + #'environment_variables': {} } diff --git a/aiida_fleur/common/node_generators.py b/aiida_fleur/common/node_generators.py index 23e8dc655..60d8a5081 100644 --- a/aiida_fleur/common/node_generators.py +++ b/aiida_fleur/common/node_generators.py @@ -55,7 +55,7 @@ def generate_wf_option_dict(computer=None, protocol_file=None, **kwargs): # and this function should read them from aiida_fleur.common.defaults import default_options - default_wf_dict = default_options.deepcopy() + default_wf_dict = default_options.copy() #todo better rekursive merge? default_wf_dict.update(kwargs) diff --git a/aiida_fleur/data/fleurinp.py b/aiida_fleur/data/fleurinp.py index 7e4083bd0..60495815b 100644 --- a/aiida_fleur/data/fleurinp.py +++ b/aiida_fleur/data/fleurinp.py @@ -28,14 +28,52 @@ import re import six from lxml import etree +import warnings -from aiida.orm import Data, Node, load_node +from aiida.orm import Data, Node, load_node, CalcJobNode from aiida.common.exceptions import InputValidationError, ValidationError from aiida.engine.processes.functions import calcfunction as cf -from aiida_fleur.tools.xml_util import replace_tag -from aiida_fleur.fleur_schema.schemafile_index import get_internal_search_paths, get_schema_paths -from aiida_fleur.common.constants import BOHR_A + +def get_fleurinp_from_folder_data(folder_node, store=False, additional_files=None): + """ + Create FleurinpData object from the given RemoteData object + + :param remote_node: RemoteData to use for the generation of the FleurinpData + :param store: bool, if True the FleurinpData object will be stored after generation + + :returns: FleurinpData object with the input xml files from the retrieved folder + of the calculation associated RemoteData + """ + if additional_files is None: + additional_files = [] + + input_xml_files = [file for file in folder_node.list_object_names() if file.endswith('.xml') and 'out' not in file] + + fleurinp = FleurinpData(files=input_xml_files + additional_files, node=folder_node) + if store: + fleurinp.store() + + return fleurinp + + +def get_fleurinp_from_remote_data(remote_node, store=False, additional_files=None): + """ + Create FleurinpData object from the given RemoteData object + + :param remote_node: RemoteData to use for the generation of the FleurinpData + :param store: bool, if True the FleurinpData object will be stored after generation + + :returns: FleurinpData object with the input xml files from the retrieved folder + of the calculation associated RemoteData + """ + + for link in remote_node.get_incoming().all(): + if isinstance(link.node, CalcJobNode): + parent_calc_node = link.node + retrieved = parent_calc_node.get_outgoing().get_node_by_label('retrieved') + + return get_fleurinp_from_folder_data(retrieved, store=store, additional_files=additional_files) class FleurinpData(Data): @@ -71,6 +109,11 @@ class FleurinpData(Data): # needs to be improved, schema file is often after new installation not found... # installation with pip should always lead to a schema file in the python path, or even specific place + __version__ = '0.5.0' + + # ignore machine dependent attributes in hash + _hash_ignored_attributes = [] #'_schema_file_path', '_search_paths'] + def __init__(self, **kwargs): """ Initialize a FleurinpData object set the files given @@ -84,71 +127,25 @@ def __init__(self, **kwargs): node = kwargs.pop('node', None) super().__init__(**kwargs) - search_paths = [] - ifolders = get_internal_search_paths() - ischemas = get_schema_paths() - for path in ischemas: - search_paths.append(path) - for path in ifolders: - search_paths.append(path) - search_paths.append('./') - - # Now add also python path maybe will be decaptivated - # if pythonpath is non existent catch error - try: - pythonpath = os.environ['PYTHONPATH'].split(':') - except KeyError: - pythonpath = [] - - for path in pythonpath[:]: - search_paths.append(path) - - self.set_attribute('_has_schema', False) - self.set_attribute('_schema_file_path', None) - self.set_attribute('_search_paths', search_paths) if files: if node: self.set_files(files, node=node) else: self.set_files(files) - # ignore machine dependent attributes in hash - _hash_ignored_attributes = ['_schema_file_path', '_search_paths'] - @property - def _has_schema(self): + def parser_info(self): """ - Boolean property, which stores if a schema file is already known + Dict property, with the info and warnings from the inpxml_parser """ - return self.get_attribute('_has_schema') + return self.get_extra('_parser_info', {}) - @property - def _schema_file_path(self): - """ - A string, which stores the absolute path to the schemafile found - """ - return self.get_attribute('_schema_file_path') - - @property - def _search_paths(self): - """ - A string, which stores the paths to search for schemafiles - """ - return self.get_attribute('_search_paths') - - @_has_schema.setter - def _has_schema(self, boolean): + @parser_info.setter + def parser_info(self, info_dict): """ Setter for has_schema """ - self.set_attribute('_has_schema', boolean) - - @_schema_file_path.setter - def _schema_file_path(self, schemapath): - """ - Setter for the schema file path - """ - self.set_attribute('_schema_file_path', schemapath) + self.set_extra('_parser_info', info_dict) # files @property @@ -190,7 +187,7 @@ def set_file(self, filename, dst_filename=None, node=None): """ self._add_path(filename, dst_filename=dst_filename, node=node) - def open(self, path='inp.xml', mode='r', key=None): + def open(self, path='inp.xml', mode='r', key=None): #pylint: disable=arguments-differ """ Returns an open file handle to the content of this data node. @@ -202,7 +199,7 @@ def open(self, path='inp.xml', mode='r', key=None): if key is not None: path = key - return self._repository.open(path, mode=mode) + return super().open(path, mode=mode) def get_content(self, filename='inp.xml'): """ @@ -231,60 +228,6 @@ def del_file(self, filename): if filename in self.list_object_names(): # get_folder_list(): self.delete_object(filename) - def find_schema(self, inp_version_number): - """ - Method which searches for a schema files (.xsd) which correspond - to the input xml file. (compares the version numbers) - - :param inp_version_number: a version of ``inp.xml`` file schema to be found - - :return: A two-element tuple: - - 1. A list of paths where schema files are located - 2. A boolen which shows if the required version schema file was found - - """ - # user changed version number, or no file yet known. - # TODO test if this still does the right thing if user adds one - # inp.xml and then an inp.xml with different version. - # or after copying and read - schemafile_paths = [] - - for path in self._search_paths: # paths: - for root, dirs, files in os.walk(path): - for file1 in files: - if file1.endswith('.xsd'): - if ('Fleur' in file1) or ('fleur' in file1): - schemafile_path = os.path.join(root, file1) - schemafile_paths.append(schemafile_path) - i = 0 - imin = 0 - imax = 0 - schemafile = open(schemafile_path, 'r') - for line in schemafile.readlines(): - i = i + 1 - # This kind of hardcoded - if re.search('name="FleurVersionType"', line): - imax = i + 10 # maybe make larger or different - imin = i - schema_version_numbers = [] - if imin < i <= imax: - if re.search('enumeration value', line): - schema_version_number = re.findall(r'\d+.\d+', line)[0] - elif re.search('simpleType>', line): - break - schema_version_numbers.append(schema_version_number) - schemafile.close() - # test if schemafiles works with multiple fleur versions - for version_number in schema_version_numbers: - if version_number == inp_version_number: - # we found the right schemafile for the current inp.xml - self.set_attribute('_schema_file_path', schemafile_path) - self.set_attribute('_has_schema', True) - return schemafile_paths, True - - return schemafile_paths, False - def _add_path(self, file1, dst_filename=None, node=None): """ Add a single file to the FleurinpData folder. @@ -361,7 +304,10 @@ def _add_path(self, file1, dst_filename=None, node=None): pass if is_filelike: - self.put_object_from_filelike(file1, key, mode='wb') + try: + self.put_object_from_filelike(file1, key, mode='wb') + except TypeError: + self.put_object_from_filelike(file1, key) else: self.put_object_from_file(file1, key) @@ -384,28 +330,8 @@ def _add_path(self, file1, dst_filename=None, node=None): 'Please check if this is a valid fleur input file. ' 'It can not be validated and I can not use it. ' ''.format(file1, lines)) - # search for Schema file with same version number - schemafile_paths, found = self.find_schema(inp_version_number) - - if (not self._has_schema) and (self._schema_file_path is None): - err_msg = ('No XML schema file (.xsd) with matching version number {} ' - 'to the inp.xml file was found. I have looked here: {} ' - 'and have found only these schema files for Fleur: {}. ' - 'I need this file to validate your input and to know the structure ' - 'of the current inp.xml file, sorry.'.format(inp_version_number, self._search_paths, - schemafile_paths)) - raise InputValidationError(err_msg) - if self._schema_file_path is None: - schemafile_paths, found = self.find_schema(inp_version_number) - if not found: - err_msg = ('No XML schema file (.xsd) with matching version number {} ' - 'to the inp.xml file was found. I have looked here: {} ' - 'and have found only these schema files for Fleur: {}. ' - 'I need this file to validate your input and to know the structure ' - 'of the current inp.xml file, sorry.'.format(inp_version_number, self._search_paths, - schemafile_paths)) - raise InputValidationError(err_msg) + self.set_attribute('inp_version', inp_version_number) # finally set inp dict of Fleurinpdata self._set_inp_dict() @@ -413,71 +339,107 @@ def _set_inp_dict(self): """ Sets the inputxml_dict from the ``inp.xml`` file attached to FleurinpData - 1. get ``inp.xml`` structure/layout - 2. load ``inp.xml`` file - 3. call inpxml_to_dict + 1. load ``inp.xml`` file + 2. insert all files to include into the etree + 3. call masci-tools input file parser (Validation happens inside here) 4. set inputxml_dict """ - from aiida_fleur.tools.xml_util import get_inpxml_file_structure, inpxml_todict, clear_xml - import tempfile - # get inpxml structure - inpxmlstructure = get_inpxml_file_structure() + from masci_tools.io.parsers.fleur import inpxml_parser - # read xmlinp file into an etree with autocomplition from schema - # we do not validate here because we want this to always succeed + #The schema_dict is not needed outside the inpxml_parser so we ignore it with the underscore + xmltree, _ = self.load_inpxml() - xmlschema_doc = etree.parse(self._schema_file_path) - xmlschema = etree.XMLSchema(xmlschema_doc) - parser = etree.XMLParser(attribute_defaults=True, encoding='utf-8') - # dtd_validation=True - with self.open(path='inp.xml', mode='r') as inpxmlfile: + parser_info = {} + try: + inpxml_dict = inpxml_parser(xmltree, parser_info_out=parser_info) + except (ValueError, FileNotFoundError) as exc: + raise InputValidationError(f'inp.xml parser failed: {exc}') from exc + finally: + #Always try to provide the error/warning information + self.parser_info = parser_info + + # set inpxml_dict attribute + self.set_attribute('inp_dict', inpxml_dict) + + def load_inpxml(self, validate_xml_schema=True, return_included_tags=False, **kwargs): + """ + Returns the lxml etree and the schema dictionary corresponding to the version. If validate_xml_schema=True + the file will also be validated against the schema + + Keyword arguments are passed on to the parser + """ + from masci_tools.io.io_fleurxml import load_inpxml + from masci_tools.util.xml.common_functions import validate_xml + + self._validate() + + with self.open(path='inp.xml', mode='rb') as inpxmlfile: try: - tree_x = etree.parse(inpxmlfile, parser) - except etree.XMLSyntaxError as exc: + xmltree, schema_dict = load_inpxml(inpxmlfile, **kwargs) + except ValueError as exc: # prob inp.xml file broken err_msg = ('The inp.xml file is probably broken, could not parse it to an xml etree.') raise InputValidationError(err_msg) from exc - # relax.xml should be available to be used in xinclude - # hence copy relax.xml from the retrieved node into the temp file - fo = tempfile.NamedTemporaryFile(mode='w', delete=False) - try: - with self.open(path='relax.xml', mode='r') as relax_file: - relax_content = relax_file.read() - except FileNotFoundError: - relax_content = '' - fo.write(relax_content) - tempfile_name = fo.name - fo.close() - - # replace relax.xml by the temp file path - tree_x_string = etree.tostring(tree_x) - tree_x_string = tree_x_string.replace(bytes('relax.xml', 'utf-8'), bytes(tempfile_name, 'utf-8')) - tree_x = etree.fromstring(tree_x_string).getroottree() - - # replace XInclude parts to validate against schema - tree_x = clear_xml(tree_x) - os.remove(tempfile_name) - - # check if it validates against the schema - if not xmlschema.validate(tree_x): - # get a more information on what does not validate - message = '' - parser_on_fly = etree.XMLParser(attribute_defaults=True, schema=xmlschema, encoding='utf-8') - inpxmlfile = etree.tostring(tree_x) + except FileNotFoundError as exc: + # prob inp.xml file broken + err_msg = ('The inp.xml file is probably broken, could not find corresponding input schema.') + raise InputValidationError(err_msg) from exc + + xmltree, included_tags = self._include_files(xmltree) + + if validate_xml_schema: try: - tree_x = etree.fromstring(inpxmlfile, parser_on_fly) - except etree.XMLSyntaxError as msg: - message = msg - raise InputValidationError( - 'Input file does not validate against the schema: {}'.format(message)) from msg + validate_xml(xmltree, + schema_dict.xmlschema, + error_header='Input file is not validated against the schema') + except etree.DocumentInvalid as err: + raise InputValidationError(err) from err + + if return_included_tags: + return xmltree, schema_dict, included_tags + else: + return xmltree, schema_dict - raise InputValidationError('Input file is not validated against the schema.' 'Reason is unknown') + def _include_files(self, xmltree): + """ + Tries to insert all .xml, which are not inp.xml file into the etree since they are + not naturally available for the parser (open vs self.open) - # convert etree into python dictionary - root = tree_x.getroot() - inpxml_dict = inpxml_todict(root, inpxmlstructure) - # set inpxml_dict attribute - self.set_attribute('inp_dict', inpxml_dict) + Creates a NamedTemporaryFile for each one and replaces the name in the etree_string + Then it is reparsed into a ElementTree and teh xi:include tags are executed + """ + from masci_tools.util.xml.common_functions import clear_xml + import tempfile + + xmltree_string = etree.tostring(xmltree) + + temp_files = [] + for file in self.files: + if file.endswith('.xml') and file != 'inp.xml': + + #Get file content from node + include_content = '' + with self.open(path=file, mode='r') as include_file: + include_content = include_file.read() + + #Write content into temporary file + with tempfile.NamedTemporaryFile(mode='w', delete=False) as fo: + fo.write(include_content) + temp_files.append(fo.name) + #If the include tag for the given file is not present nothing is replaced + xmltree_string = xmltree_string.replace(bytes(file, 'utf-8'), bytes(fo.name, 'utf-8')) + + #Regenerate the tree with tempfile names + xmltree_with_includes = etree.fromstring(xmltree_string).getroottree() + + #Performs the inclusions and remove comments + cleared_tree, included_tags = clear_xml(xmltree_with_includes) + + #Remove temporary files + for file in temp_files: + os.remove(file) + + return cleared_tree, included_tags # dict with inp paramters parsed from inp.xml @property @@ -488,14 +450,20 @@ def inp_dict(self): """ return self.get_attribute('inp_dict', {}) - # TODO better validation? other files, if has a schema + # version of the inp.xml file + @property + def inp_version(self): + """ + Returns the version string corresponding to the inp.xml file + """ + return self.get_attribute('inp_version', None) def _validate(self): """ A validation method. Checks if an ``inp.xml`` file is in the FleurinpData. """ #from aiida.common.exceptions import ValidationError - + # check if schema file path exists. super()._validate() if 'inp.xml' in self.files: @@ -506,39 +474,33 @@ def _validate(self): 'FleurinpData needs to have and inp.xml file!') def get_fleur_modes(self): - ''' + """ Analyses ``inp.xml`` file to set up a calculation mode. 'Modes' are paths a FLEUR - calculation can take, resulting in different output files. + calculation can take, resulting in different output. This files can be automatically addded to the retrieve_list of the calculation. Common modes are: scf, jspin2, dos, band, pot8, lda+U, eels, ... - :return: a dictionary containing all possible modes. A mode is activated assigning a - non-empty string to the corresponding key. - ''' - # TODO these should be retrieved by looking at the inpfile structure and - # then setting the paths. - # use methods from fleur parser... - # For now they are hardcoded. - # 'dos': '/fleurInput/output', - # 'band': '/fleurInput/output', - # 'jspins': '/fleurInput/calculationSetup/magnetism', - fleur_modes = {'jspins': '', 'dos': '', 'band': '', 'ldau': '', 'forces': '', 'force_theorem': '', 'gw': ''} - if 'inp.xml' in self.files: - fleur_modes['jspins'] = self.inp_dict['calculationSetup']['magnetism']['jspins'] - fleur_modes['dos'] = self.inp_dict['output']['dos'] # 'fleurInput'] - fleur_modes['band'] = self.inp_dict['output']['band'] - fleur_modes['forces'] = self.inp_dict['calculationSetup']['geometryOptimization']['l_f'] - fleur_modes['force_theorem'] = 'forceTheorem' in self.inp_dict - try: - fleur_modes['gw'] = int(self.inp_dict['calculationSetup']['expertModes']['gw']) != 0 - except KeyError: - fleur_modes['gw'] = False - fleur_modes['ldau'] = False - for species in self.inp_dict['atomSpecies']['species']: - if 'ldaU' in species: - fleur_modes['ldau'] = True - return fleur_modes + :return: a dictionary containing all possible modes. + """ + from masci_tools.util.xml.xml_getters import get_fleur_modes + + xmltree, schema_dict = self.load_inpxml() + + return get_fleur_modes(xmltree, schema_dict) + + def get_nkpts(self): + """ + This routine returns the number of kpoints used in the fleur calculation + defined in this input + + :returns: int with the number of kPoints + """ + from masci_tools.util.xml.xml_getters import get_nkpts + + xmltree, schema_dict = self.load_inpxml() + + return get_nkpts(xmltree, schema_dict) def get_structuredata_ncf(self): """ @@ -549,198 +511,17 @@ def get_structuredata_ncf(self): :returns: StructureData node, or None """ from aiida.orm import StructureData - from aiida_fleur.tools.StructureData_util import rel_to_abs, rel_to_abs_f - - # StructureData = DataFactory(‘structure’) - # Disclaimer: this routine needs some xpath expressions. these are hardcoded here, - # therefore maintainance might be needed, if you want to circumvent this, you have - # to get all the paths from somewhere. - - ####### - # all hardcoded xpaths used and attributes names: - bravaismatrix_bulk_xpath = '/fleurInput/cell/bulkLattice/bravaisMatrix/' - bravaismatrix_film_xpath = '/fleurInput/cell/filmLattice/bravaisMatrix/' - species_xpath = '/fleurInput/atomSpecies/species' - all_atom_groups_xpath = '/fleurInput/atomGroups/atomGroup' - - species_attrib_name = 'name' - species_attrib_element = 'element' - - row1_tag_name = 'row-1' - row2_tag_name = 'row-2' - row3_tag_name = 'row-3' - - atom_group_attrib_species = 'species' - atom_group_tag_abspos = 'absPos' - atom_group_tag_relpos = 'relPos' - atom_group_tag_filmpos = 'filmPos' - ######## - - if 'inp.xml' not in self.files: - print('cannot get a StructureData because fleurinpdata has no inp.xml file yet') - # TODO what to do in this case? - return None - - # read in inpxml - - if self._schema_file_path: # Schema there, parse with schema - xmlschema_doc = etree.parse(self._schema_file_path) - xmlschema = etree.XMLSchema(xmlschema_doc) - parser = etree.XMLParser(attribute_defaults=True, encoding='utf-8') - with self.open(path='inp.xml', mode='r') as inpxmlfile: - tree = etree.parse(inpxmlfile, parser) - tree.xinclude() - # remove comments from inp.xml - comments = tree.xpath('//comment()') - for c in comments: - p = c.getparent() - p.remove(c) - if not xmlschema.validate(tree): - raise ValueError('Input file is not validated against the schema.') - else: # schema not there, parse without - print('parsing inp.xml without XMLSchema') - with self.open(path='inp.xml', mode='r') as inpxmlfile: - tree = etree.parse(inpxmlfile) - tree.xinclude() - # remove comments from inp.xml - comments = tree.xpath('//comment()') - for c in comments: - p = c.getparent() - p.remove(c) - root = tree.getroot() - - # Fleur uses atomic units, convert to Angstrom - # get cell matrix from inp.xml - row1 = root.xpath(bravaismatrix_bulk_xpath + row1_tag_name) # [0].text.split() - cell = None - - if row1: # bulk calculation - row1 = row1[0].text.split() - row2 = root.xpath(bravaismatrix_bulk_xpath + row2_tag_name)[0].text.split() - row3 = root.xpath(bravaismatrix_bulk_xpath + row3_tag_name)[0].text.split() - # TODO? allow math? - for i, cor in enumerate(row1): - row1[i] = float(cor) * BOHR_A - for i, cor in enumerate(row2): - row2[i] = float(cor) * BOHR_A - for i, cor in enumerate(row3): - row3[i] = float(cor) * BOHR_A - - cell = [row1, row2, row3] - # create new structure Node - struc = StructureData(cell=cell) - struc.pbc = [True, True, True] - - elif root.xpath(bravaismatrix_film_xpath + row1_tag_name): - # film calculation - row1 = root.xpath(bravaismatrix_film_xpath + row1_tag_name)[0].text.split() - row2 = root.xpath(bravaismatrix_film_xpath + row2_tag_name)[0].text.split() - row3 = root.xpath(bravaismatrix_film_xpath + row3_tag_name)[0].text.split() - for i, cor in enumerate(row1): - row1[i] = float(cor) * BOHR_A - for i, cor in enumerate(row2): - row2[i] = float(cor) * BOHR_A - for i, cor in enumerate(row3): - row3[i] = float(cor) * BOHR_A - # row3 = [0, 0, 0]#? TODO:what has it to be in this case? - cell = [row1, row2, row3] - struc = StructureData(cell=cell) - struc.pbc = [True, True, False] - - if cell is None: - print('Could not extract Bravias matrix out of inp.xml. Is the ' - 'Bravias matrix explicitly given? i.e Latnam definition ' - 'not supported.') - return None - - # get species for atom kinds - #species = root.xpath(species_xpath) - species_name = root.xpath(species_xpath + '/@' + species_attrib_name) - species_element = root.xpath(species_xpath + '/@' + species_attrib_element) - # alternativ: loop over species and species.get(species_attrib_name) - - # save species info in a dict - species_dict = {} - for i, spec in enumerate(species_name): - species_dict[spec] = {species_attrib_element: species_element[i]} - - # Now we have to get all atomgroups, look what their species is and - # their positions are. - # Then we append them to the new structureData - - all_atom_groups = root.xpath(all_atom_groups_xpath) - - for atom_group in all_atom_groups: - current_species = atom_group.get(atom_group_attrib_species) - - group_atom_positions_abs = atom_group.xpath(atom_group_tag_abspos) - group_atom_positions_rel = atom_group.xpath(atom_group_tag_relpos) - group_atom_positions_film = atom_group.xpath(atom_group_tag_filmpos) - - if group_atom_positions_abs: # we have absolute positions - for atom in group_atom_positions_abs: - postion_a = atom.text.split() - # allow for math *, / - for i, pos in enumerate(postion_a): - if '/' in pos: - temppos = pos.split('/') - postion_a[i] = float(temppos[0]) / float(temppos[1]) - elif '*' in pos: - temppos = pos.split('*') - postion_a[i] = float(temppos[0]) * float(temppos[1]) - else: - postion_a[i] = float(pos) - postion_a[i] = postion_a[i] * BOHR_A - # append atom to StructureData - struc.append_atom(position=postion_a, symbols=species_dict[current_species][species_attrib_element]) - - elif group_atom_positions_rel: # we have relative positions - # TODO: check if film or 1D calc, because this is not allowed! I guess - for atom in group_atom_positions_rel: - postion_r = atom.text.split() - # allow for math * / - for i, pos in enumerate(postion_r): - if '/' in pos: - temppos = pos.split('/') - postion_r[i] = float(temppos[0]) / float(temppos[1]) - elif '*' in pos: - temppos = pos.split('*') - postion_r[i] = float(temppos[0]) * float(temppos[1]) - else: - postion_r[i] = float(pos) - - # now transform to absolute Positions - new_abs_pos = rel_to_abs(postion_r, cell) - - # append atom to StructureData - struc.append_atom(position=new_abs_pos, - symbols=species_dict[current_species][species_attrib_element]) - - elif group_atom_positions_film: # Do we support mixture always, or only in film case? - # either support or throw error - for atom in group_atom_positions_film: - # film pos are rel rel abs, therefore only transform first two coordinates - postion_f = atom.text.split() - # allow for math * / - for i, pos in enumerate(postion_f): - if '/' in pos: - temppos = pos.split('/') - postion_f[i] = float(temppos[0]) / float(temppos[1]) - elif '*' in postion_f[i]: - temppos = pos.split('*') - postion_f[i] = float(temppos[0]) * float(temppos[1]) - else: - postion_f[i] = float(pos) - # now transform to absolute Positions - postion_f[2] = postion_f[2] * BOHR_A - new_abs_pos = rel_to_abs_f(postion_f, cell) - # append atom to StructureData - struc.append_atom(position=new_abs_pos, - symbols=species_dict[current_species][species_attrib_element]) - else: - # TODO throw error - print('I should never get here, 1D not supported yet, ' 'I only know relPos, absPos, filmPos') - # TODO throw error + from masci_tools.util.xml.xml_getters import get_structure_data + + xmltree, schema_dict = self.load_inpxml() + + atoms, cell, pbc = get_structure_data(xmltree, schema_dict) + + struc = StructureData(cell=cell, pbc=pbc) + + for pos, symbol in atoms: + struc.append_atom(position=pos, symbols=symbol) + # TODO DATA-DATA links are not wanted, you might want to use a cf instead #struc.add_link_from(self, label='self.structure', link_type=LinkType.CREATE) # label='self.structure' @@ -759,15 +540,21 @@ def get_structuredata(self): """ return self.get_structuredata_ncf() - def get_kpointsdata_ncf(self): + def get_kpointsdata_ncf(self, name=None, index=None): """ This routine returns an AiiDA :class:`~aiida.orm.KpointsData` type produced from the ``inp.xml`` file. This only works if the kpoints are listed in the in inpxml. This is NOT a calcfunction and does not keep the provenance! + :param name: str, optional, if given only the kpoint set with the given name + is returned + :param index: int, optional, if given only the kpoint set with the given index + is returned + :returns: :class:`~aiida.orm.KpointsData` node """ from aiida.orm import KpointsData + from masci_tools.util.xml.xml_getters import get_kpoints_data # HINT, TODO:? in this routine, the 'cell' you might get in an other way # exp: StructureData.cell, but for this you have to make a structureData Node, @@ -775,136 +562,35 @@ def get_kpointsdata_ncf(self): # then just parsing the cell from the inp.xml # as in the routine get_structureData - # Disclaimer: this routine needs some xpath expressions. - # these are hardcoded here, therefore maintainance might be needed, - # if you want to circumvent this, you have - # to get all the paths from somewhere. - - ####### - # all hardcoded xpaths used and attributes names: - bravaismatrix_bulk_xpath = '/fleurInput/cell/bulkLattice/bravaisMatrix/' - bravaismatrix_film_xpath = '/fleurInput/cell/filmLattice/bravaisMatrix/' - kpointlist_xpath = '/fleurInput/calculationSetup/bzIntegration/kPointList/' - - kpoint_tag = 'kPoint' - kpointlist_attrib_posscale = 'posScale' - kpointlist_attrib_weightscale = 'weightScale' - #kpointlist_attrib_count = 'count' - kpoint_attrib_weight = 'weight' - row1_tag_name = 'row-1' - row2_tag_name = 'row-2' - row3_tag_name = 'row-3' - ######## - - if 'inp.xml' not in self.files: - print('cannot get a KpointsData because fleurinpdata has no inp.xml file yet') - # TODO what to do in this case? - return False - - # else read in inpxml - - if self._schema_file_path: # Schema there, parse with schema - xmlschema_doc = etree.parse(self._schema_file_path) - xmlschema = etree.XMLSchema(xmlschema_doc) - parser = etree.XMLParser(attribute_defaults=True, encoding='utf-8') - with self.open(path='inp.xml', mode='r') as inpxmlfile: - tree = etree.parse(inpxmlfile, parser) - tree.xinclude() - # remove comments from inp.xml - comments = tree.xpath('//comment()') - for c in comments: - p = c.getparent() - p.remove(c) - if not xmlschema.validate(tree): - raise ValueError('Input file is not validated against the schema.') - else: # schema not there, parse without - print('parsing inp.xml without XMLSchema') - with self.open(path='inp.xml', mode='r') as inpxmlfile: - tree = etree.parse(inpxmlfile) - tree.xinclude() - # remove comments from inp.xml - comments = tree.xpath('//comment()') - for c in comments: - p = c.getparent() - p.remove(c) - root = tree.getroot() - - # get cell matrix from inp.xml - cell = None - row1 = root.xpath(bravaismatrix_bulk_xpath + row1_tag_name) # [0].text.split() - - if row1: # bulk calculation - row1 = row1[0].text.split() - row2 = root.xpath(bravaismatrix_bulk_xpath + row2_tag_name)[0].text.split() - row3 = root.xpath(bravaismatrix_bulk_xpath + row3_tag_name)[0].text.split() - # TODO? allow math? - for i, cor in enumerate(row1): - row1[i] = float(cor) * BOHR_A - for i, cor in enumerate(row2): - row2[i] = float(cor) * BOHR_A - for i, cor in enumerate(row3): - row3[i] = float(cor) * BOHR_A - - cell = [row1, row2, row3] - # set boundary conditions - pbc1 = [True, True, True] - - elif root.xpath(bravaismatrix_film_xpath + row1_tag_name): - # film calculation - row1 = root.xpath(bravaismatrix_film_xpath + row1_tag_name)[0].text.split() - row2 = root.xpath(bravaismatrix_film_xpath + row2_tag_name)[0].text.split() - row3 = root.xpath(bravaismatrix_film_xpath + row3_tag_name)[0].text.split() - for i, cor in enumerate(row1): - row1[i] = float(cor) * BOHR_A - for i, cor in enumerate(row2): - row2[i] = float(cor) * BOHR_A - for i, cor in enumerate(row3): - row3[i] = float(cor) * BOHR_A - # row3 = [0, 0, 0]#? TODO:what has it to be in this case? - cell = [row1, row2, row3] - pbc1 = [True, True, False] - - if cell is None: - print('Could not extract Bravias matrix out of inp.xml. Is the ' - 'Bravias matrix explicitly given? i.e Latnam definition ' - 'not supported.') - return None - # get kpoints only works if kpointlist in inp.xml - kpoints = root.xpath(kpointlist_xpath + kpoint_tag) - - if kpoints: - posscale = root.xpath(kpointlist_xpath + '@' + kpointlist_attrib_posscale) - weightscale = root.xpath(kpointlist_xpath + '@' + kpointlist_attrib_weightscale) - #count = root.xpath(kpointlist_xpath + '@' + kpointlist_attrib_count) - - kpoints_pos = [] - kpoints_weight = [] - - for kpoint in kpoints: - kpoint_pos = kpoint.text.split() - for i, kval in enumerate(kpoint_pos): - kpoint_pos[i] = float(kval) / float(posscale[0]) - kpoint_weight = float(kpoint.get(kpoint_attrib_weight)) / float(weightscale[0]) - kpoints_pos.append(kpoint_pos) - kpoints_weight.append(kpoint_weight) - totalw = 0 - for weight in kpoints_weight: - totalw = totalw + weight - kps = KpointsData() - kps.set_cell(cell) - kps.pbc = pbc1 - - kps.set_kpoints(kpoints_pos, cartesian=False, weights=kpoints_weight) - #kps.add_link_from(self, label='fleurinp.kpts', link_type=LinkType.CREATE) - kps.label = 'fleurinp.kpts' - # return {label: kps} - return kps - else: # TODO parser other kpoints formats, if they fit in an AiiDA node - print('No kpoint list in inp.xml') - return None + xmltree, schema_dict = self.load_inpxml() + + if name is None and index is None: + kpoints, weights, cell, pbc = get_kpoints_data(xmltree, schema_dict) + else: + kpoints, weights, cell, pbc = get_kpoints_data(xmltree, schema_dict, name=name, index=index) + + if isinstance(kpoints, dict): + kpoints_data = {} + for (label, kpoints_set), weights_set in zip(kpoints.items(), weights.values()): + kps = KpointsData() + kps.set_cell(cell) + kps.pbc = pbc + kps.set_kpoints(kpoints_set, cartesian=False, weights=weights_set) + #kpoints_data.add_link_from(self, label='fleurinp.kpts', link_type=LinkType.CREATE) + kps.label = 'fleurinp.kpts' + kpoints_data[label] = kps + else: + kpoints_data = KpointsData() + kpoints_data.set_cell(cell) + kpoints_data.pbc = pbc + kpoints_data.set_kpoints(kpoints, cartesian=False, weights=weights) + #kpoints_data.add_link_from(self, label='fleurinp.kpts', link_type=LinkType.CREATE) + kpoints_data.label = 'fleurinp.kpts' + + return kpoints_data @cf - def get_kpointsdata(self): + def get_kpointsdata(self, name=None, index=None): """ This routine returns an AiiDA :class:`~aiida.orm.KpointsData` type produced from the ``inp.xml`` file. This only works if the kpoints are listed in the in inpxml. @@ -912,10 +598,9 @@ def get_kpointsdata(self): :returns: :class:`~aiida.orm.KpointsData` node """ + return self.get_kpointsdata_ncf(name=name, index=index) - return self.get_kpointsdata_ncf() - - def get_parameterdata_ncf(self): + def get_parameterdata_ncf(self, inpgen_ready=True, write_ids=True): """ This routine returns an AiiDA :class:`~aiida.orm.Dict` type produced from the ``inp.xml`` file. This node can be used for inpgen as `calc_parameters`. @@ -923,21 +608,17 @@ def get_parameterdata_ncf(self): :returns: :class:`~aiida.orm.Dict` node """ - from aiida_fleur.tools.xml_util import get_inpgen_paranode_from_xml - if 'inp.xml' not in self.files: - print('cannot get a StructureData because fleurinpdata has no inp.xml file yet') - # TODO what to do in this case? - return False + from aiida.orm import Dict + from masci_tools.util.xml.xml_getters import get_parameter_data + + xmltree, schema_dict = self.load_inpxml() - # read in inpxml - with self.open(path='inp.xml', mode='r') as inpxmlfile: - new_parameters = get_inpgen_paranode_from_xml(etree.parse(inpxmlfile)) - return new_parameters + parameter_data = get_parameter_data(xmltree, schema_dict, inpgen_ready=inpgen_ready, write_ids=write_ids) + + return Dict(dict=parameter_data) - # Is there a way to give self to calcfunctions? - @staticmethod @cf - def get_parameterdata(fleurinp): + def get_parameterdata(self): """ This routine returns an AiiDA :class:`~aiida.orm.Dict` type produced from the ``inp.xml`` file. The returned node can be used for inpgen as `calc_parameters`. @@ -946,7 +627,7 @@ def get_parameterdata(fleurinp): :returns: :class:`~aiida.orm.Dict` node """ - return fleurinp.get_parameterdata_ncf() + return self.get_parameterdata_ncf() def get_tag(self, xpath): """ @@ -955,31 +636,14 @@ def get_tag(self, xpath): :param xpath: an xpath expression :returns: A node list retrived using given xpath """ + from masci_tools.util.xml.common_functions import eval_xpath - if 'inp.xml' in self.files: - # read in inpxml - if self._schema_file_path: # Schema there, parse with schema - #xmlschema_doc = etree.parse(self._schema_file_path) - #xmlschema = etree.XMLSchema(xmlschema_doc) - #parser = etree.XMLParser(schema=xmlschema, attribute_defaults=True) - with self.open(path='inp.xml', mode='r') as inpxmlfile: - tree = etree.parse(inpxmlfile) # , parser) - else: # schema not there, parse without - print('parsing inp.xml without XMLSchema') - with self.open(path='inp.xml', mode='r') as inpxmlfile: - tree = etree.parse(inpxmlfile) - root = tree.getroot() - else: - raise InputValidationError('No inp.xml file yet specified, to get a tag from') + warnings.warn( + 'The get_tag method is deprecated. Instead you can use the load_inpxml method to access ' + 'the xmltree and schema of the stored inp.xml. Then the required information can be accessed ' + 'via the XML functions in masci-tools or directly', DeprecationWarning) - try: - return_value = root.xpath(xpath) - except etree.XPathEvalError as exc: - raise InputValidationError('There was a XpathEvalError on the xpath: {} \n Either it does ' - 'not exist, or something is wrong with the expression.' - ''.format(xpath)) from exc - - if len(return_value) == 1: - return return_value - else: - return return_value + xmltree, _ = self.load_inpxml() + root = xmltree.getroot() + + return eval_xpath(root, xpath) diff --git a/aiida_fleur/data/fleurinpmodifier.py b/aiida_fleur/data/fleurinpmodifier.py index d95f4c3af..c2d1300f5 100644 --- a/aiida_fleur/data/fleurinpmodifier.py +++ b/aiida_fleur/data/fleurinpmodifier.py @@ -14,25 +14,24 @@ In this module is the FleurinpModifier class, which is used to manipulate FleurinpData objects in a way which keeps the provernance. """ - -from __future__ import absolute_import -from __future__ import print_function import os -import io - from lxml import etree +import warnings -from aiida.plugins import DataFactory -from aiida import orm from aiida.engine.processes.functions import calcfunction as cf from aiida_fleur.data.fleurinp import FleurinpData +from aiida_fleur.tools.xml_aiida_modifiers import set_kpointsdata_f + +from masci_tools.io.fleurxmlmodifier import ModifierTask, FleurXMLModifier -class FleurinpModifier(object): +class FleurinpModifier(FleurXMLModifier): """ A class which represents changes to the :class:`~aiida_fleur.data.fleurinp.FleurinpData` object. """ + _extra_functions = {'schema_dict': {'set_kpointsdata': set_kpointsdata_f}} + def __init__(self, original): """ Initiation of FleurinpModifier. @@ -40,475 +39,541 @@ def __init__(self, original): assert isinstance(original, FleurinpData), 'Wrong AiiDA data type' self._original = original - self._tasks = [] self._other_nodes = {} - @staticmethod - def apply_modifications(fleurinp_tree_copy, nmmp_lines_copy, modification_tasks, schema_tree=None): - """ - Applies given modifications to the fleurinp lxml tree. - It also checks if a new lxml tree is validated against schema. - Does not rise an error if inp.xml is not validated, simple prints a message about it. + super().__init__() - :param fleurinp_tree_copy: a fleurinp lxml tree to be modified - :param n_mmp_lines_copy: a n_mmp_mat file to be modified - :param modification_tasks: a list of modification tuples + @classmethod + def apply_fleurinp_modifications(cls, new_fleurinp, modification_tasks): + """ + Apply the modifications working directly on the cloned + FleurinpData instance. The functions will warn the user if one of the methods + executed here are after XML modifications in the task list, since this method will implictly + reorder the order of the execution - :returns: a modified fleurinp lxml tree and a modified n_mmp_mat file - """ - from aiida_fleur.tools.xml_util import xml_set_attribv_occ, xml_set_first_attribv - from aiida_fleur.tools.xml_util import xml_set_all_attribv, xml_set_text - from aiida_fleur.tools.xml_util import xml_set_text_occ, xml_set_all_text - from aiida_fleur.tools.xml_util import create_tag, replace_tag, delete_tag - from aiida_fleur.tools.xml_util import delete_att, set_species - from aiida_fleur.tools.xml_util import change_atomgr_att, add_num_to_att - from aiida_fleur.tools.xml_util import change_atomgr_att_label, set_species_label - from aiida_fleur.tools.xml_util import set_inpchanges, set_nkpts, set_kpath, shift_value - from aiida_fleur.tools.xml_util import shift_value_species_label - from aiida_fleur.tools.xml_util import clear_xml - from aiida_fleur.tools.set_nmmpmat import set_nmmpmat, validate_nmmpmat - - def xml_set_attribv_occ1(fleurinp_tree_copy, xpathn, attributename, attribv, occ=None, create=False): - if occ is None: - occ = [0] - xml_set_attribv_occ(fleurinp_tree_copy, xpathn, attributename, attribv, occ=occ, create=create) - return fleurinp_tree_copy - - def xml_set_first_attribv1(fleurinp_tree_copy, xpathn, attributename, attribv, create=False): - xml_set_first_attribv(fleurinp_tree_copy, xpathn, attributename, attribv, create=create) - return fleurinp_tree_copy - - def xml_set_all_attribv1(fleurinp_tree_copy, xpathn, attributename, attribv, create=False): - xml_set_all_attribv(fleurinp_tree_copy, xpathn, attributename, attribv, create=create) - return fleurinp_tree_copy - - def xml_set_text1(fleurinp_tree_copy, xpathn, text, create=False): - xml_set_text(fleurinp_tree_copy, xpathn, text, create=create) - return fleurinp_tree_copy - - def xml_set_text_occ1(fleurinp_tree_copy, xpathn, text, create=False, occ=0): - xml_set_text_occ(fleurinp_tree_copy, xpathn, text, create=create, occ=occ) - return fleurinp_tree_copy - - def xml_set_all_text1(fleurinp_tree_copy, xpathn, text, create=False): - xml_set_all_text(fleurinp_tree_copy, xpathn, text, create=create) - return fleurinp_tree_copy - - def create_tag1(fleurinp_tree_copy, xpath, newelement, create=False): - fleurinp_tree_copy = create_tag(fleurinp_tree_copy, xpath, newelement, create=create) - return fleurinp_tree_copy - - def delete_att1(fleurinp_tree_copy, xpath, attrib): - fleurinp_tree_copy = delete_att(fleurinp_tree_copy, xpath, attrib) - return fleurinp_tree_copy - - def delete_tag1(fleurinp_tree_copy, xpath): - fleurinp_tree_copy = delete_tag(fleurinp_tree_copy, xpath) - return fleurinp_tree_copy - - def replace_tag1(fleurinp_tree_copy, xpath, newelement): - fleurinp_tree_copy = replace_tag(fleurinp_tree_copy, xpath, newelement) - return fleurinp_tree_copy - - def set_species1(fleurinp_tree_copy, species_name, attributedict, create=False): - fleurinp_tree_copy = set_species(fleurinp_tree_copy, species_name, attributedict, create=create) - return fleurinp_tree_copy - - def set_species2(fleurinp_tree_copy, at_label, attributedict, create=False): - fleurinp_tree_copy = set_species_label(fleurinp_tree_copy, at_label, attributedict, create=create) - return fleurinp_tree_copy - - def change_atomgr_att1(fleurinp_tree_copy, attributedict, position=None, species=None, create=False): - fleurinp_tree_copy = change_atomgr_att(fleurinp_tree_copy, - attributedict, - position=position, - species=species) - return fleurinp_tree_copy - - def change_atomgr_att2(fleurinp_tree_copy, attributedict, atom_label, create=False): - fleurinp_tree_copy = change_atomgr_att_label(fleurinp_tree_copy, attributedict, at_label=atom_label) - return fleurinp_tree_copy - - def add_num_to_att1(fleurinp_tree_copy, xpathn, attributename, set_val, mode='abs', occ=None): - if occ is None: - occ = [0] - fleurinp_tree_copy = add_num_to_att(fleurinp_tree_copy, xpathn, attributename, set_val, mode=mode, occ=occ) - return fleurinp_tree_copy - - def set_inpchanges1(fleurinp_tree_copy, change_dict): - fleurinp_tree_copy = set_inpchanges(fleurinp_tree_copy, change_dict) - return fleurinp_tree_copy - - def shift_value1(fleurinp_tree_copy, change_dict, mode): - fleurinp_tree_copy = shift_value(fleurinp_tree_copy, change_dict, mode) - return fleurinp_tree_copy - - def shift_value_species_label1(fleurinp_tree_copy, label, att_name, value, mode): - fleurinp_tree_copy = shift_value_species_label(fleurinp_tree_copy, label, att_name, value, mode) - return fleurinp_tree_copy - - def set_nkpts1(fleurinp_tree_copy, count, gamma): - fleurinp_tree_copy = set_nkpts(fleurinp_tree_copy, count, gamma) - return fleurinp_tree_copy - - def set_kpath1(fleurinp_tree_copy, kpath, count, gamma): - fleurinp_tree_copy = set_kpath(fleurinp_tree_copy, kpath, count, gamma) - return fleurinp_tree_copy - - def set_kpointsdata1(fleurinp_tree_copy, kpointsdata_uuid): - fleurinp_tree_copy = set_kpointsdata_f(fleurinp_tree_copy, kpointsdata_uuid) - return fleurinp_tree_copy - - def set_nmmpmat1(fleurinp_tree_copy, nmmp_lines_copy, species_name, orbital,\ - spin, occStates, denmat, phi, theta): - nmmp_lines_copy = set_nmmpmat(fleurinp_tree_copy, nmmp_lines_copy, species_name, orbital,\ - spin, occStates, denmat, phi, theta) - return nmmp_lines_copy - - actions = { - 'xml_set_attribv_occ': xml_set_attribv_occ1, - 'xml_set_first_attribv': xml_set_first_attribv1, - 'xml_set_all_attribv': xml_set_all_attribv1, - 'xml_set_text': xml_set_text1, - 'xml_set_text_occ': xml_set_text_occ1, - 'xml_set_all_text': xml_set_all_text1, - 'create_tag': create_tag1, - 'replace_tag': replace_tag1, - 'delete_tag': delete_tag1, - 'delete_att': delete_att1, - 'set_species': set_species1, - 'set_species_label': set_species2, - 'set_atomgr_att': change_atomgr_att1, - 'set_atomgr_att_label': change_atomgr_att2, - 'set_inpchanges': set_inpchanges1, - 'shift_value': shift_value1, - 'shift_value_species_label': shift_value_species_label1, - 'set_nkpts': set_nkpts1, - 'set_kpath': set_kpath1, - 'set_kpointsdata': set_kpointsdata1, - 'add_num_to_att': add_num_to_att1, - 'set_nmmpmat': set_nmmpmat1 - } + .. warning:: + These should be performed BEFORE the XML Modification methods + in any of the functions doing modifcations (:py:meth:`FleurinpModifier.show()`, + :py:meth:`FleurinpModifier.validate()`, :py:meth:`FleurinpModifier.freeze()`). - workingtree = fleurinp_tree_copy - workingnmmp = nmmp_lines_copy - if schema_tree: - #xmlschema_doc = etree.parse(new_fleurinp._schema_file_path) - xmlschema = etree.XMLSchema(schema_tree) + :param new_fleurinp: The Fleurinpdata instance cloned from the original + :param modification_tasks: a list of modification tuples + """ + #These functions from the FleurinpData are supported + fleurinp_mod_functions = {'set_file': new_fleurinp.set_file, 'del_file': new_fleurinp.del_file} + warn = False for task in modification_tasks: - try: - action = actions[task[0]] - except KeyError as exc: - raise ValueError('Unknown task {}'.format(task[0])) from exc - - if task[0] == 'set_nmmpmat': - workingnmmp = action(workingtree, workingnmmp, *task[1:]) + if task.name in fleurinp_mod_functions: + modification_tasks.remove(task) + action = fleurinp_mod_functions[task.name] + action(*task.args, **task.kwargs) + if warn: + warnings.warn('The modification methods operating directly adding/removing files ' + 'are performed before any XML modification methods') else: - workingtree = action(workingtree, *task[1:]) - - if schema_tree: - try: - xmlschema.assertValid(clear_xml(workingtree)) - except etree.DocumentInvalid as exc: - msg = 'Changes were not valid: {}'.format(modification_tasks) - print(msg) - raise etree.DocumentInvalid(msg) from exc - try: - validate_nmmpmat(workingtree, workingnmmp) - except ValueError as exc: - msg = 'Changes were not valid (n_mmp_mat file is not compatible): {}'.format(modification_tasks) - print(msg) - raise ValueError(msg) from exc - - return workingtree, workingnmmp + warn = True def get_avail_actions(self): """ Returns the allowed functions from FleurinpModifier """ - outside_actions = { + outside_actions_fleurinp = { + 'set_kpointsdata': self.set_kpointsdata, + 'set_atomgr_att': self.set_atomgr_att, + 'set_atomgr_att_label': self.set_atomgr_att_label, 'xml_set_attribv_occ': self.xml_set_attribv_occ, 'xml_set_first_attribv': self.xml_set_first_attribv, 'xml_set_all_attribv': self.xml_set_all_attribv, - 'xml_set_text': self.xml_set_text, 'xml_set_text_occ': self.xml_set_text_occ, + 'xml_set_text': self.xml_set_text, 'xml_set_all_text': self.xml_set_all_text, - 'create_tag': self.create_tag, - 'replace_tag': self.replace_tag, - 'delete_tag': self.delete_tag, - 'delete_att': self.delete_att, - 'set_species': self.set_species, - 'set_species_label': self.set_species_label, - 'set_atomgr_att': self.set_atomgr_att, - 'set_atomgr_att_label': self.set_atomgr_att_label, - 'set_inpchanges': self.set_inpchanges, - 'shift_value': self.shift_value, - 'shift_value_species_label': self.shift_value_species_label, - 'set_nkpts': self.set_nkpts, - 'set_kpath': self.set_kpath, - 'set_kpointsdata': self.set_kpointsdata, 'add_num_to_att': self.add_num_to_att, - 'set_nmmpmat': self.set_nmmpmat + 'set_file': self.set_file, + 'del_file': self.del_file } - return outside_actions - def xml_set_attribv_occ(self, xpathn, attributename, attribv, occ=None, create=False): - """ - Appends a :func:`~aiida_fleur.tools.xml_util.xml_set_attribv_occ()` to - the list of tasks that will be done on the FleurinpData. + outside_actions_fleurxml = super().get_avail_actions().copy() - :param xpathn: a path to the attribute - :param attributename: an attribute name - :param attribv: an attribute value which will be set - :param occ: a list of integers specifying number of occurrence to be set - :param create: if True and there is no given xpath in the FleurinpData, creates it - """ - if occ is None: - occ = [0] - self._tasks.append(('xml_set_attribv_occ', xpathn, attributename, attribv, occ, create)) + return {**outside_actions_fleurxml, **outside_actions_fleurinp} - def xml_set_first_attribv(self, xpathn, attributename, attribv, create=False): + def set_kpointsdata(self, kpointsdata_uuid, name=None, switch=False): """ - Appends a :func:`~aiida_fleur.tools.xml_util.xml_set_first_attribv()` to + Appends a :py:func:`~aiida_fleur.tools.xml_aiida_modifiers.set_kpointsdata_f()` to the list of tasks that will be done on the FleurinpData. - :param xpathn: a path to the attribute - :param attributename: an attribute name - :param attribv: an attribute value which will be set - :param create: if True and there is no given xpath in the FleurinpData, creates it + :param kpointsdata_uuid: an :class:`aiida.orm.KpointsData` or node uuid, since the node is self cannot be be serialized in tasks. """ - self._tasks.append(('xml_set_first_attribv', xpathn, attributename, attribv, create)) + from aiida.orm import KpointsData, load_node - def xml_set_all_attribv(self, xpathn, attributename, attribv, create=False): - """ - Appends a :func:`~aiida_fleur.tools.xml_util.xml_set_all_attribv()` to - the list of tasks that will be done on the FleurinpData. + if isinstance(kpointsdata_uuid, KpointsData): + kpointsdata_uuid = kpointsdata_uuid.uuid + # Be more careful? Needs to be stored, otherwise we cannot load it - :param xpathn: a path to the attribute - :param attributename: an attribute name - :param attribv: an attribute value which will be set - :param create: if True and there is no given xpath in the FleurinpData, creates it - """ - self._tasks.append(('xml_set_all_attribv', xpathn, attributename, attribv, create)) + num_nodes = sum('kpoints' in label for label in self._other_nodes) + 1 + node_label = f'kpoints_{num_nodes}' - def xml_set_text(self, xpathn, text, create=False): - """ - Appends a :func:`~aiida_fleur.tools.xml_util.xml_set_text()` to - the list of tasks that will be done on the FleurinpData. + self._other_nodes[node_label] = load_node(kpointsdata_uuid) + self._tasks.append( + ModifierTask('set_kpointsdata', args=(kpointsdata_uuid,), kwargs={ + 'name': name, + 'switch': switch + })) - :param xpathn: a path to the attribute - :param text: text to be set - :param create: if True and there is no given xpath in the FleurinpData, creates it - """ - self._tasks.append(('xml_set_text', xpathn, text, create)) + #Modification functions that were renamed in masci-tools - def xml_set_text_occ(self, xpathn, text, create=False, occ=0): + def shift_value_species_label(self, *args, **kwargs): """ - Appends a :func:`~aiida_fleur.tools.xml_util.xml_set_text_occ()` to - the list of tasks that will be done on the FleurinpData. + Appends a :py:func:`~masci_tools.util.xml.xml_setters_names.shift_value_species_label()` to + the list of tasks that will be done on the xmltree. - :param xpathn: a path to the attribute - :param text: text to be set - :param create: if True and there is no given xpath in the FleurinpData, creates it - :param occ: an integer specifying number of occurrence to be set - """ - self._tasks.append(('xml_set_text_occ', xpathn, text, create, occ)) + :param atom_label: string, a label of the atom which specie will be changed. 'all' if set up all species + :param attributename: name of the attribute to change + :param value_given: value to add or to multiply by + :param mode: 'rel' for multiplication or 'abs' for addition - def xml_set_all_text(self, xpathn, text, create=False): - """ - Appends a :func:`~aiida_fleur.tools.xml_util.xml_set_all_text()` to - the list of tasks that will be done on the FleurinpData. + Kwargs if the attributename does not correspond to a unique path: + :param contains: str, this string has to be in the final path + :param not_contains: str, this string has to NOT be in the final path - :param xpathn: a path to the attribute - :param text: text to be set - :param create: if True and there is no given xpath in the FleurinpData, creates it """ - self._tasks.append(('xml_set_all_text', xpathn, text, create)) + if 'label' in kwargs: + warnings.warn('The argument label is deprecated.' "Use 'atom_label' instead", DeprecationWarning) + kwargs['atom_label'] = kwargs.pop('label') - def create_tag(self, xpath, newelement, create=False): - """ - Appends a :func:`~aiida_fleur.tools.xml_util.create_tag()` to - the list of tasks that will be done on the FleurinpData. + if 'att_name' in kwargs: + warnings.warn('The argument att_name is deprecated.' "Use 'attributename' instead", DeprecationWarning) + kwargs['attributename'] = kwargs.pop('att_name') - :param xpathn: a path where to place a new tag - :param newelement: a tag name to be created - :param create: if True and there is no given xpath in the FleurinpData, creates it - """ - self._tasks.append(('create_tag', xpath, newelement, create)) + if 'value' in kwargs: + warnings.warn('The argument value is deprecated.' "Use 'value_given' instead", DeprecationWarning) + kwargs['value_given'] = kwargs.pop('value') + + super().shift_value_species_label(*args, **kwargs) - def delete_att(self, xpath, attrib): + def set_species_label(self, *args, **kwargs): """ - Appends a :func:`~aiida_fleur.tools.xml_util.delete_att()` to - the list of tasks that will be done on the FleurinpData. + Appends a :py:func:`~masci_tools.util.xml.xml_setters_names.set_species_label()` to + the list of tasks that will be done on the xmltree. + + :param atom_label: string, a label of the atom which specie will be changed. 'all' to change all the species + :param attributedict: a python dict specifying what you want to change. - :param xpathn: a path to the attribute to be deleted - :param attrib: the name of an attribute """ - self._tasks.append(('delete_att', xpath, attrib)) + if 'at_label' in kwargs: + warnings.warn('The argument at_label is deprecated.' "Use 'atom_label' instead", DeprecationWarning) + kwargs['atom_label'] = kwargs.pop('at_label') + + super().set_species_label(*args, **kwargs) - def delete_tag(self, xpath): + def set_atomgr_att(self, *args, **kwargs): """ - Appends a :func:`~aiida_fleur.tools.xml_util.delete_tag()` to - the list of tasks that will be done on the FleurinpData. + Deprecated method for setting attributes on atomgroups + """ + warnings.warn('This modification method is deprecated.' + "Use the 'set_atomgroup' method instead", DeprecationWarning) + + self.set_atomgroup(*args, **kwargs) - :param xpathn: a path to the tag to be deleted + def set_atomgr_att_label(self, *args, **kwargs): """ - self._tasks.append(('delete_tag', xpath)) + Deprecated method for setting attributes on atomgroups identified by an atom label + """ + warnings.warn('This modification method is deprecated.' + "Use the 'set_atomgroup_label' method instead", DeprecationWarning) + + if 'atom_label' not in kwargs: + if len(args) == 2: + kwargs['atom_label'], args = args[1], args[:1] + elif len(args) > 2: + kwargs['atom_label'], args = args[1], args[:1] + args[2:] - def replace_tag(self, xpath, newelement): + self.set_atomgroup_label(*args, **kwargs) + + def xml_set_attribv_occ(self, *args, **kwargs): + """ + Deprecated method for setting attributes for occurrences on a specific xpath """ - Appends a :func:`~aiida_fleur.tools.xml_util.replace_tag()` to - the list of tasks that will be done on the FleurinpData. - :param xpathn: a path to the tag to be replaced - :param newelement: a new tag + warnings.warn( + 'This modification method is deprecated.' + "Use the 'xml_set_attrib_value_no_create' or" + "'set_attrib_value' method instead", DeprecationWarning) + + if 'xpathn' in kwargs: + kwargs['xpath'] = kwargs.pop('xpathn') + + occ = kwargs.pop('occ', None) + kwargs.pop('create', None) + + self.xml_set_attrib_value_no_create(*args, **kwargs, occurrences=occ) + + def xml_set_first_attribv(self, *args, **kwargs): """ - self._tasks.append(('replace_tag', xpath, newelement)) + Deprecated method for setting the first attribute on a specific xpath + """ + + warnings.warn( + 'This modification method is deprecated.' + "Use the 'xml_set_first_attrib_value_no_create' with 'occurrences=0' or" + "'set_first_attrib_value' method instead", DeprecationWarning) - def set_species(self, species_name, attributedict, create=False): + if 'xpathn' in kwargs: + kwargs['xpath'] = kwargs.pop('xpathn') + kwargs.pop('create', None) + + self.xml_set_attrib_value_no_create(*args, occurrences=0, **kwargs) + + def xml_set_all_attribv(self, *args, **kwargs): """ - Appends a :func:`~aiida_fleur.tools.xml_util.set_species()` to - the list of tasks that will be done on the FleurinpData. + Deprecated method for setting all attributes on a specific xpath + """ + + warnings.warn( + 'This modification method is deprecated.' + "Use the 'xml_set_attrib_value_no_create' or" + "'set_attrib_value' method instead", DeprecationWarning) + + if 'xpathn' in kwargs: + kwargs['xpath'] = kwargs.pop('xpathn') + kwargs.pop('create', None) - :param species_name: a path to the tag to be replaced - :param attributedict: attribute dictionary to be set into the specie - :param create: if True and there is no given specie in the FleurinpData, creates it + self.xml_set_attrib_value_no_create(*args, **kwargs) + + def xml_set_text_occ(self, *args, **kwargs): + """ + Deprecated method for setting texts for occurrences on a specific xpath """ - self._tasks.append(('set_species', species_name, attributedict, create)) - def set_species_label(self, at_label, attributedict, create=False): + warnings.warn( + 'This modification method is deprecated.' + "Use the 'xml_set_text_no_create' or" + "'set_text' method instead", DeprecationWarning) + + if 'xpathn' in kwargs: + kwargs['xpath'] = kwargs.pop('xpathn') + occ = kwargs.pop('occ', None) + kwargs.pop('create', None) + kwargs.pop('place_index', None) + kwargs.pop('tag_order', None) + + self.xml_set_text_no_create(*args, **kwargs, occurrences=occ) + + def xml_set_text(self, *args, **kwargs): """ - Appends a :func:`~aiida_fleur.tools.xml_util.set_species_label()` to - the list of tasks that will be done on the FleurinpData. + Deprecated method for setting attributes for occurrences on a specific xpath + """ + + warnings.warn( + 'This modification method is deprecated.' + "Use the 'xml_set_text_no_create' with 'occurrences=0' or" + "'set_first_text' method instead", DeprecationWarning) + + if 'xpathn' in kwargs: + kwargs['xpath'] = kwargs.pop('xpathn') + kwargs.pop('create', None) + kwargs.pop('place_index', None) + kwargs.pop('tag_order', None) + + self.xml_set_text_no_create(*args, occurrences=0, **kwargs) - :param at_label: Atom label which specie will be set - :param attributedict: attribute dictionary to be set into the specie - :param create: if True and there is no given specie in the FleurinpData, creates it + def xml_set_all_text(self, *args, **kwargs): """ - self._tasks.append(('set_species_label', at_label, attributedict, create)) + Deprecated method for setting attributes for occurrences on a specific xpath + """ + + warnings.warn( + 'This modification method is deprecated.' + "Use the 'xml_set_text_no_create' or" + "'set_text' method instead", DeprecationWarning) + + if 'xpathn' in kwargs: + kwargs['xpath'] = kwargs.pop('xpathn') + kwargs.pop('create', None) + kwargs.pop('place_index', None) + kwargs.pop('tag_order', None) - def set_atomgr_att(self, attributedict, position=None, species=None, create=False): + self.xml_set_text_no_create(*args, **kwargs) + + def xml_create_tag(self, *args, **kwargs): """ - Appends a :func:`~aiida_fleur.tools.xml_util.change_atomgr_att()` to - the list of tasks that will be done on the FleurinpData. + Appends a :py:func:`~masci_tools.util.xml.xml_setters_basic.xml_create_tag()` to + the list of tasks that will be done on the xmltree. - :param species_name: a path to the tag to be replaced - :param attributedict: attribute dictionary to be set into the atom group - :param create: if True and there is no given atom group in the FleurinpData, creates it + :param xpath: a path where to place a new tag + :param element: a tag name or etree Element to be created + :param place_index: defines the place where to put a created tag + :param tag_order: defines a tag order + :param occurrences: int or list of int. Which occurence of the parent nodes to create a tag. + By default all nodes are used. """ - self._tasks.append(('set_atomgr_att', attributedict, position, species, create)) + self._validate_signature('xml_create_tag', *args, **kwargs) - def set_atomgr_att_label(self, attributedict, atom_label, create=False): + if 'element' in kwargs: + element = kwargs + else: + element = args[1] + + if etree.iselement(element): + warnings.warn('Creating a tag from a given etree Element is only supported via the show()' + 'and validate() methods on the Fleurinpmodifier and cannot be used with freeze()') + + super().xml_create_tag(*args, **kwargs) + + def create_tag(self, *args, **kwargs): """ - Appends a :func:`~aiida_fleur.tools.xml_util.change_atomgr_att_label()` to - the list of tasks that will be done on the FleurinpData. + Deprecation layer for create_tag if there are slashes in the first positional argument or xpath is is in kwargs. + We know that it is the old usage. + + Appends a :py:func:`~masci_tools.util.xml.xml_setters_names.create_tag()` to + the list of tasks that will be done on the xmltree. - :param attributedict: a new tag - :param atom_label: Atom label which atom group will be set - :param create: if True and there is no given atom group in the FleurinpData, creates it + :param tag: str of the tag to create + :param complex_xpath: an optional xpath to use instead of the simple xpath for the evaluation + :param create_parents: bool optional (default False), if True and the given xpath has no results the + the parent tags are created recursively + :param occurrences: int or list of int. Which occurence of the parent nodes to create a tag. + By default all nodes are used. + + Kwargs: + :param contains: str, this string has to be in the final path + :param not_contains: str, this string has to NOT be in the final path """ - self._tasks.append(('set_atomgr_att_label', attributedict, atom_label, create)) - def set_inpchanges(self, change_dict): + if 'xpath' in kwargs or '/' in args[0]: + warnings.warn( + "The 'create_tag' method no longer requires an explicit xpath. " + 'This Usage is deprecated. ' + "Use the 'xml_create_tag' method instead or only pass in the name of the tag, you want to use", + DeprecationWarning) + + if 'xpath' in kwargs: + xpath = kwargs.pop('xpath') + else: + xpath, args = args[0], args[1:] + + if 'newelement' in kwargs: + element = kwargs.pop('newelement') + else: + element, args = args[0], args[1:] + + self.xml_create_tag(xpath, element, *args, **kwargs) + else: + tag = kwargs.get('tag') + if tag is None: + tag = args[0] + + if etree.iselement(tag): + warnings.warn('Creating a tag from a given etree Element is only supported via the show()' + 'and validate() methods on the Fleurinpmodifier and cannot be used with freeze()') + + super().create_tag(*args, **kwargs) + + def delete_tag(self, *args, **kwargs): """ - Appends a :py:func:`~aiida_fleur.tools.xml_util.set_inpchanges()` to - the list of tasks that will be done on the FleurinpData. + Deprecation layer for delete_tag if there are slashes in the first positional argument or xpath is is in kwargs. + We know that it is the old usage. - :param change_dict: a dictionary with changes + Appends a :py:func:`~masci_tools.util.xml.xml_setters_names.delete_tag()` to + the list of tasks that will be done on the xmltree. - An example of change_dict:: + :param tag: str of the tag to delete + :param complex_xpath: an optional xpath to use instead of the simple xpath for the evaluation + :param occurrences: int or list of int. Which occurence of the parent nodes to delete a tag. + By default all nodes are used. - change_dict = {'itmax' : 1, - 'l_noco': True, - 'ctail': False, - 'l_ss': True} + Kwargs: + :param contains: str, this string has to be in the final path + :param not_contains: str, this string has to NOT be in the final path """ - self._tasks.append(('set_inpchanges', change_dict)) - def shift_value(self, change_dict, mode='abs'): + if 'xpath' in kwargs or '/' in args[0]: + warnings.warn( + "The 'delete_tag' method no longer requires an explicit xpath. " + 'This Usage is deprecated. ' + "Use the 'xml_delete_tag' method instead or only pass in the name of the tag, you want to use", + DeprecationWarning) + + if 'xpath' in kwargs: + xpath = kwargs.pop('xpath') + else: + xpath, args = args[0], args[1:] + + self.xml_delete_tag(xpath, *args, **kwargs) + else: + super().delete_tag(*args, **kwargs) + + def delete_att(self, *args, **kwargs): """ - Appends a :py:func:`~aiida_fleur.tools.xml_util.shift_value()` to - the list of tasks that will be done on the FleurinpData. + Deprecation layer for delete_att if there are slashes in the first positional argument or xpath is is in kwargs. + We know that it is the old usage. - :param change_dict: a dictionary with changes - :param mode: 'abs' if change given is absolute, 'rel' if relative + Appends a :py:func:`~masci_tools.util.xml.xml_setters_names.delete_att()` to + the list of tasks that will be done on the xmltree. - An example of change_dict:: + :param tag: str of the attribute to delete + :param complex_xpath: an optional xpath to use instead of the simple xpath for the evaluation + :param occurrences: int or list of int. Which occurence of the parent nodes to delete a attribute. + By default all nodes are used. - change_dict = {'itmax' : 1, dVac = -2} + Kwargs: + :param tag_name: str, name of the tag where the attribute should be parsed + :param contains: str, this string has to be in the final path + :param not_contains: str, this string has to NOT be in the final path + :param exclude: list of str, here specific types of attributes can be excluded + valid values are: settable, settable_contains, other """ - self._tasks.append(('shift_value', change_dict, mode)) - def shift_value_species_label(self, label, att_name, value, mode='abs'): + if 'xpath' in kwargs or '/' in args[0]: + warnings.warn( + "The 'delete_att' method no longer requires an explicit xpath. " + 'This Usage is deprecated. ' + "Use the 'xml_delete_att' method instead or only pass in the name of the attribute, you want to use", + DeprecationWarning) + + if 'xpath' in kwargs: + xpath = kwargs.pop('xpath') + else: + xpath, args = args[0], args[1:] + + self.xml_delete_att(xpath, *args, **kwargs) + else: + super().delete_att(*args, **kwargs) + + def xml_replace_tag(self, *args, **kwargs): """ - Appends a :py:func:`~aiida_fleur.tools.xml_util.shift_value_species_label()` to - the list of tasks that will be done on the FleurinpData. + Appends a :py:func:`~masci_tools.util.xml.xml_setters_basic.xml_replace_tag()` to + the list of tasks that will be done on the xmltree. - :param label: a label of an atom - :param att_name: attrubute name of a specie - :param value: value to set - :param mode: 'abs' if change given is absolute, 'rel' if relative + :param xpath: a path to the tag to be replaced + :param newelement: a new tag + :param occurrences: int or list of int. Which occurence of the parent nodes to create a tag. + By default all nodes are used. """ - self._tasks.append(('shift_value_species_label', label, att_name, value, mode)) + self._validate_signature('xml_replace_tag', *args, **kwargs) + + warnings.warn('Creating a tag from a given etree Element is only supported via the show()' + 'and validate() methods on the Fleurinpmodifier and cannot be used with freeze()') + + super().xml_replace_tag(*args, **kwargs) - def set_nkpts(self, count, gamma='F'): + def replace_tag(self, *args, **kwargs): """ - Appends a :py:func:`~aiida_fleur.tools.xml_util.set_nkpts()` to - the list of tasks that will be done on the FleurinpData. + Deprecation layer for replace_tag if there are slashes in the first positional argument or xpath is is in kwargs. + We know that it is the old usage. + + Appends a :py:func:`~masci_tools.util.xml.xml_setters_names.replace_tag()` to + the list of tasks that will be done on the xmltree. + + :param tag: str of the tag to replace + :param newelement: a new tag + :param complex_xpath: an optional xpath to use instead of the simple xpath for the evaluation + :param occurrences: int or list of int. Which occurence of the parent nodes to replace a tag. + By default all nodes are used. + + Kwargs: + :param contains: str, this string has to be in the final path + :param not_contains: str, this string has to NOT be in the final path """ - self._tasks.append(('set_nkpts', count, gamma)) - def set_kpath(self, kpath, count, gamma='F'): + warnings.warn('Replacing a tag with a given etree Element is only supported via the show()' + 'and validate() methods on the Fleurinpmodifier and cannot be used with freeze()') + + if 'xpath' in kwargs or '/' in args[0]: + warnings.warn( + "The 'delete_att' method no longer requires an explicit xpath. " + 'This Usage is deprecated. ' + "Use the 'xml_delete_att' method instead or only pass in the name of the attribute, you want to use", + DeprecationWarning) + + if 'xpath' in kwargs: + xpath = kwargs.pop('xpath') + else: + xpath, args = args[0], args[1:] + + self.xml_replace_tag(xpath, *args, **kwargs) + else: + super().replace_tag(*args, **kwargs) + + def add_num_to_att(self, *args, **kwargs): """ - Appends a :py:func:`~aiida_fleur.tools.xml_util.set_kpath()` to - the list of tasks that will be done on the FleurinpData. + Deprecated method for adding a number to a attribute at a specific xpath """ - self._tasks.append(('set_kpath', kpath, count, gamma)) - def set_kpointsdata(self, kpointsdata_uuid): + warnings.warn( + 'This modification method is deprecated.' + "Use the 'add_number_to_attrib' or 'add_number_to_first_attrib' method instead", DeprecationWarning) + + #Since the new method takes only an attribute we extract the xpath and pass it in as complex_xpath + + if len(args) == 0: + xpath = kwargs.pop('xpathn') + else: + xpath, args = args[0], args[1:] + + if 'occ' not in kwargs: + self.add_number_to_first_attrib(*args, **kwargs, complex_xpath=xpath) + else: + occ = kwargs.pop('occ') + self.add_number_to_attrib(*args, **kwargs, complex_xpath=xpath, occurrences=occ) + + def set_nmmpmat(self, *args, **kwargs): """ - Appends a :py:func:`~aiida_fleur.data.fleurinpmodifier.set_kpointsdata_f()` to - the list of tasks that will be done on the FleurinpData. + Appends a :py:func:`~masci_tools.util.xml.xml_setters_nmmpmat.set_nmmpmat()` to + the list of tasks that will be done on the xmltree. - :param kpointsdata_uuid: an :class:`aiida.orm.KpointsData` or node uuid, since the node is self cannot be be serialized in tasks. + :param species_name: string, name of the species you want to change + :param orbital: integer, orbital quantum number of the LDA+U procedure to be modified + :param spin: integer, specifies which spin block should be modified + :param state_occupations: list, sets the diagonal elements of the density matrix and everything + else to zero + :param denmat: matrix, specify the density matrix explicitely + :param phi: float, optional angle (radian), by which to rotate the density matrix before writing it + :param theta: float, optional angle (radian), by which to rotate the density matrix before writing it """ - from aiida.orm import KpointsData, load_node - if isinstance(kpointsdata_uuid, KpointsData): - kpointsdata_uuid = kpointsdata_uuid.uuid - # Be more careful? Needs to be stored, otherwise we cannot load it - self._other_nodes['kpoints'] = load_node(kpointsdata_uuid) - self._tasks.append(('set_kpointsdata', kpointsdata_uuid)) + if 'occStates' in kwargs: + warnings.warn('The argument occStates is deprecated.' "Use 'state_occupations' instead", DeprecationWarning) + kwargs['state_occupations'] = kwargs.pop('occStates') - def add_num_to_att(self, xpathn, attributename, set_val, mode='abs', occ=None): + super().set_nmmpmat(*args, **kwargs) + + def set_file(self, filename, dst_filename=None, node=None): """ - Appends a :py:func:`~aiida_fleur.tools.xml_util.add_num_to_att()` to - the list of tasks that will be done on the FleurinpData. + Appends a :py:func:`~aiida_fleur.data.fleurinp.FleurinpData.set_file()` to + the list of tasks that will be done on the FleurinpData instance. - :param xpathn: an xml path to the attribute to change - :param attributename: a name of the attribute to change - :param set_val: a value to be added/multiplied to the previous value - :param mode: 'abs' if to add set_val, 'rel' if multiply - :param occ: a list of integers specifying number of occurrence to be set + :param filename: absolute path to the file or a filename of node is specified + :param dst_filename: str of the filename, which should be used instead of the real filename + to store it + :param node: a :class:`~aiida.orm.FolderData` node containing the file """ - if occ is None: - occ = [0] - self._tasks.append(('add_num_to_att', xpathn, attributename, set_val, mode, occ)) + from aiida.orm import FolderData, load_node - def set_nmmpmat(self, species_name, orbital, spin, occStates=None, denmat=None, phi=None, theta=None): + node_uuid = None + if node is not None: + if isinstance(node, FolderData): + node_uuid = node.uuid + + num_nodes = sum('folder' in label for label in self._other_nodes) + 1 + node_label = f'folder_{num_nodes}' + # Be more careful? Needs to be stored, otherwise we cannot load it + self._other_nodes[node_label] = load_node(node_uuid) + + self._tasks.append( + ModifierTask('set_file', args=(filename,), kwargs={ + 'dst_filename': dst_filename, + 'node': node_uuid + })) + + def del_file(self, filename): """ - Appends a :py:func:`~aiida_fleur.tools.set_nmmpmat.set_nmmpmat()` to - the list of tasks that will be done on the FleurinpData. + Appends a :py:func:`~aiida_fleur.data.fleurinp.FleurinpData.del_file()` to + the list of tasks that will be done on the FleurinpData instance. - :param species_name: species on which the density matrix should be set - :param orbital: orbital on which the density matrix should be set - :param occStates: list which specifies the diagonal elements of the density matrix - :param denmat: matrix, which specifies the density matrix - :param phi: optional angle to rotate density matrix - :param theta: optional angle to rotate density matrix + :param filename: name of the file to be removed from FleurinpData instance """ - self._tasks.append(('set_nmmpmat', species_name, orbital, spin, occStates, denmat, phi, theta)) + self._tasks.append(ModifierTask('del_file', args=(filename,), kwargs={})) def validate(self): """ @@ -518,25 +583,20 @@ def validate(self): :return: a lxml tree representing inp.xml with applied changes """ - with self._original.open(key='inp.xml') as inpxmlfile: - tree = etree.parse(inpxmlfile) + + new_fleurinp = self._original.clone() + self.apply_fleurinp_modifications(new_fleurinp, self._tasks) + + xmltree, schema_dict = new_fleurinp.load_inpxml(remove_blank_text=True) try: - with self._original.open(path='n_mmp_mat', mode='r') as n_mmp_file: + with new_fleurinp.open(path='n_mmp_mat', mode='r') as n_mmp_file: nmmplines = n_mmp_file.read().split('\n') except FileNotFoundError: nmmplines = None - try: # could be not found or on another computer... - xmlschema_tree = etree.parse(self._original._schema_file_path) - with_schema = True - except BaseException: - with_schema = False - print('No schema file found') - return - if with_schema: - tree, nmmp = self.apply_modifications(tree, nmmplines, self._tasks, schema_tree=xmlschema_tree) - return tree + xmltree, nmmp = super().apply_modifications(xmltree, nmmplines, self._tasks) + return xmltree def show(self, display=True, validate=False): """ @@ -551,25 +611,24 @@ def show(self, display=True, validate=False): """ if validate: - tree = self.validate() + xmltree = self.validate() else: - with self._original.open(path='inp.xml') as inpxmlfile: - tree = etree.parse(inpxmlfile) - tree, temp_nmmp = self.apply_modifications(tree, None, self._tasks) + new_fleurinp = self._original.clone() + self.apply_fleurinp_modifications(new_fleurinp, self._tasks) + + xmltree, schema_dict = new_fleurinp.load_inpxml(remove_blank_text=True) + try: + with new_fleurinp.open(path='n_mmp_mat', mode='r') as n_mmp_file: + nmmplines = n_mmp_file.read().split('\n') + except FileNotFoundError: + nmmplines = None + + xmltree, nmmp = super().apply_modifications(xmltree, nmmplines, self._tasks, validate_changes=False) if display: - xmltreestring = etree.tostring(tree, xml_declaration=True, pretty_print=True) + xmltreestring = etree.tostring(xmltree, encoding='unicode', pretty_print=True) print(xmltreestring) - return tree - - def changes(self): - """ - Prints out all changes given in a - :class:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier` instance. - """ - from pprint import pprint - pprint(self._tasks) - return self._tasks + return xmltree def freeze(self): """ @@ -578,12 +637,11 @@ def freeze(self): :return: stored :class:`~aiida_fleur.data.fleurinp.FleurinpData` with applied changes """ - modifications = orm.Dict(dict={'tasks': self._tasks}) - #print(self._tasks) + from aiida.orm import Dict + modifications = Dict(dict={'tasks': self._tasks}) modifications.description = 'Fleurinpmodifier Tasks and inputs of these.' modifications.label = 'Fleurinpdata modifications' # This runs in a inline calculation to keep provenance - print(self._original) inputs = dict(original=self._original, modifications=modifications, metadata={ @@ -594,20 +652,9 @@ def freeze(self): out = modify_fleurinpdata(**inputs) return out - def undo(self, revert_all=False): - """ - Cancels the last change or all of them - - :param revert_all: set True if need to cancel all the changes, False if the last one. - """ - if revert_all: - self._tasks = [] - else: - if self._tasks: - task = self._tasks.pop() - #TODO delete nodes from other nodes - #del self._tasks[-1] - return self._tasks + #Deactivate modify_xmlfile method from FleurXMLModifier (Only modify fleurinp) + def modify_xmlfile(self, *args, **kwargs): #pylint: disable=missing-function-docstring + raise Exception(f'modify_xmlfile is disabled on {self.__class__.__name__}') @cf @@ -621,7 +668,6 @@ def modify_fleurinpdata(original, modifications, **kwargs): :param kwargs: dict of other aiida nodes to be linked to the modifications :returns new_fleurinp: a modified FleurinpData that is stored in a database """ - # copy # get schema # read in inp.xml @@ -629,23 +675,20 @@ def modify_fleurinpdata(original, modifications, **kwargs): # validate # save inp.xml # store new fleurinp (copy) - from aiida_fleur.tools.xml_util import clear_xml + import tempfile + from masci_tools.util.xml.common_functions import reverse_xinclude new_fleurinp = original.clone() + modification_tasks = modifications.get_dict()['tasks'] - xmlschema_doc = etree.parse(new_fleurinp._schema_file_path) - xmlschema = etree.XMLSchema(xmlschema_doc) - parser = etree.XMLParser(attribute_defaults=True, remove_blank_text=True) - with new_fleurinp.open(path='inp.xml', mode='r') as inpxmlfile: - tree = etree.parse(inpxmlfile, parser) + #We need to rebuild the namedtuples since the serialization for the calcufunction inputs + #converts the namedtuples into lists + modification_tasks = [ModifierTask(*task) for task in modification_tasks] - try: - xmlschema.assertValid(clear_xml(tree)) - except etree.DocumentInvalid as exc: - msg = 'Input file is not validated against the schema' - print(msg) - raise etree.DocumentInvalid(msg) from exc + FleurinpModifier.apply_fleurinp_modifications(new_fleurinp, modification_tasks) + + xmltree, schema_dict, included_tags = new_fleurinp.load_inpxml(remove_blank_text=True, return_included_tags=True) try: with new_fleurinp.open(path='n_mmp_mat', mode='r') as n_mmp_file: @@ -653,75 +696,34 @@ def modify_fleurinpdata(original, modifications, **kwargs): except FileNotFoundError: nmmplines = None - new_fleurtree, new_nmmplines = FleurinpModifier.apply_modifications(fleurinp_tree_copy=tree,\ - nmmp_lines_copy=nmmplines,\ - modification_tasks=modification_tasks) + new_fleurtree, new_nmmplines = FleurinpModifier.apply_modifications(xmltree=xmltree,\ + nmmp_lines=nmmplines,\ + modification_tasks=modification_tasks, + validate_changes=False) # To include object store storage this prob has to be done differently - inpxmlfile_new = inpxmlfile.name.replace('inp.xml', 'temp_inp.xml') - inpxmlfile.close() - - new_fleurtree.write(inpxmlfile_new, pretty_print=True) + inpxmltree, includedtrees = reverse_xinclude(new_fleurtree, schema_dict, included_tags) new_fleurinp.del_file('inp.xml') - new_fleurinp._add_path(str(inpxmlfile_new), 'inp.xml') - os.remove(inpxmlfile_new) - - if new_nmmplines: - new_nmmp = bytes('\n'.join(new_nmmplines), 'utf-8') - new_fleurinp._add_path(io.BytesIO(new_nmmp), 'n_mmp_mat') + with tempfile.TemporaryDirectory() as td: + inpxml_path = os.path.join(td, 'inp.xml') + inpxmltree.write(inpxml_path, encoding='utf-8', pretty_print=True) + new_fleurinp.set_file(inpxml_path, 'inp.xml') + + for file_name, tree in includedtrees.items(): + file_path = os.path.join(td, file_name) + tree.write(file_path, encoding='utf-8', pretty_print=True) + new_fleurinp.set_file(file_path, file_name) + + if new_nmmplines is not None: + n_mmp_path = os.path.join(td, 'n_mmp_mat') + with open(n_mmp_path, 'w') as n_mmp_file: + n_mmp_file.write('\n'.join(new_nmmplines)) + new_fleurinp.set_file(n_mmp_path, 'n_mmp_mat') # default label and description new_fleurinp.label = 'mod_fleurinp' new_fleurinp.description = 'Fleurinpdata with modifications (see inputs of modify_fleurinpdata)' return new_fleurinp - - -def set_kpointsdata_f(fleurinp_tree_copy, kpointsdata_uuid): - """This calc function writes all kpoints from a :class:`~aiida.orm.KpointsData` node - in the ``inp.xml`` file as a kpointslist. It replaces kpoints written in the - ``inp.xml`` file. Currently it is the users responsibility to provide a full - :class:`~aiida.orm.KpointsData` node with weights. - - :param fleurinp_tree_copy: fleurinp_tree_copy - :param kpointsdata_uuid: node identifier or :class:`~aiida.orm.KpointsData` node to be written into ``inp.xml`` - :return: modified xml tree - """ - # TODO: check on weights, - # also fleur allows for several kpoint sets, lists, paths and meshes, - # support this. - from aiida.orm import KpointsData, load_node - from aiida.common.exceptions import InputValidationError - from aiida_fleur.tools.xml_util import replace_tag - - # all hardcoded xpaths used and attributes names: - kpointlist_xpath = '/fleurInput/calculationSetup/bzIntegration/kPointList' - - # replace the kpoints tag.(delete old write new) - # - # 17.000000 0.000000 0.000000 - # add new inp.xml to fleurinpdata - if not isinstance(kpointsdata_uuid, KpointsData): - KpointsDataNode = load_node(kpointsdata_uuid) - else: - KpointsDataNode = kpointsdata_uuid - - if not isinstance(KpointsDataNode, KpointsData): - raise InputValidationError('The node given is not a valid KpointsData node.') - - kpoint_list = KpointsDataNode.get_kpoints(also_weights=True, cartesian=False) - nkpts = len(kpoint_list[0]) - totalw = 0 - for weight in kpoint_list[1]: - totalw = totalw + weight - #weightscale = totalw - # fleur will re weight? renormalize? - new_kpo = etree.Element('kPointList', posScale='1.000', weightScale='1.0', count='{}'.format(nkpts)) - for i, kpos in enumerate(kpoint_list[0]): - new_k = etree.Element('kPoint', weight='{}'.format(kpoint_list[1][i])) - new_k.text = '{} {} {}'.format(kpos[0], kpos[1], kpos[2]) - new_kpo.append(new_k) - new_tree = replace_tag(fleurinp_tree_copy, kpointlist_xpath, new_kpo) - return new_tree diff --git a/aiida_fleur/fleur_schema/README b/aiida_fleur/fleur_schema/README deleted file mode 100644 index dd7245aae..000000000 --- a/aiida_fleur/fleur_schema/README +++ /dev/null @@ -1,3 +0,0 @@ -Since the schemas were often not found, we added a small python routine to tell the plugin where they are... - -So please if you add a NEW schemafile, add its relative path to this routine in schema_file_index.py diff --git a/aiida_fleur/fleur_schema/__init__.py b/aiida_fleur/fleur_schema/__init__.py deleted file mode 100644 index 6e91f601d..000000000 --- a/aiida_fleur/fleur_schema/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################### -# Copyright (c), Forschungszentrum Jülich GmbH, IAS-1/PGI-1, Germany. # -# All rights reserved. # -# This file is part of the AiiDA-FLEUR package. # -# # -# The code is hosted on GitHub at https://github.com/JuDFTteam/aiida-fleur # -# For further information on the license, see the LICENSE.txt file # -# For further information please visit http://www.flapw.de or # -# http://aiida-fleur.readthedocs.io/en/develop/ # -############################################################################### -''' -AiiDA-FLEUR -''' diff --git a/aiida_fleur/fleur_schema/input/0.27/FleurInputSchema.xsd b/aiida_fleur/fleur_schema/input/0.27/FleurInputSchema.xsd deleted file mode 100644 index 13b9fe142..000000000 --- a/aiida_fleur/fleur_schema/input/0.27/FleurInputSchema.xsd +++ /dev/null @@ -1,737 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/aiida_fleur/fleur_schema/input/0.28/FleurInputSchema.xsd b/aiida_fleur/fleur_schema/input/0.28/FleurInputSchema.xsd deleted file mode 100644 index 4b4fef723..000000000 --- a/aiida_fleur/fleur_schema/input/0.28/FleurInputSchema.xsd +++ /dev/null @@ -1,920 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/aiida_fleur/fleur_schema/input/0.29/FleurInputSchema.xsd b/aiida_fleur/fleur_schema/input/0.29/FleurInputSchema.xsd deleted file mode 100644 index 245bc526d..000000000 --- a/aiida_fleur/fleur_schema/input/0.29/FleurInputSchema.xsd +++ /dev/null @@ -1,1020 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/aiida_fleur/fleur_schema/input/0.30/FleurInputSchema.xsd b/aiida_fleur/fleur_schema/input/0.30/FleurInputSchema.xsd deleted file mode 100644 index aa4240eb5..000000000 --- a/aiida_fleur/fleur_schema/input/0.30/FleurInputSchema.xsd +++ /dev/null @@ -1,1035 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/aiida_fleur/fleur_schema/input/0.31/FleurInputSchema.xsd b/aiida_fleur/fleur_schema/input/0.31/FleurInputSchema.xsd deleted file mode 100644 index 5053d677e..000000000 --- a/aiida_fleur/fleur_schema/input/0.31/FleurInputSchema.xsd +++ /dev/null @@ -1,1037 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/aiida_fleur/fleur_schema/input/0.32/FleurInputSchema.xsd b/aiida_fleur/fleur_schema/input/0.32/FleurInputSchema.xsd deleted file mode 100644 index 0493f484c..000000000 --- a/aiida_fleur/fleur_schema/input/0.32/FleurInputSchema.xsd +++ /dev/null @@ -1,1226 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/aiida_fleur/fleur_schema/input/0.33/FleurInputSchema.xsd b/aiida_fleur/fleur_schema/input/0.33/FleurInputSchema.xsd deleted file mode 100644 index 6a85750e1..000000000 --- a/aiida_fleur/fleur_schema/input/0.33/FleurInputSchema.xsd +++ /dev/null @@ -1,1226 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/aiida_fleur/fleur_schema/schemafile_index.py b/aiida_fleur/fleur_schema/schemafile_index.py deleted file mode 100644 index a2e0e0be8..000000000 --- a/aiida_fleur/fleur_schema/schemafile_index.py +++ /dev/null @@ -1,51 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################### -# Copyright (c), Forschungszentrum Jülich GmbH, IAS-1/PGI-1, Germany. # -# All rights reserved. # -# This file is part of the AiiDA-FLEUR package. # -# # -# The code is hosted on GitHub at https://github.com/JuDFTteam/aiida-fleur # -# For further information on the license, see the LICENSE.txt file # -# For further information please visit http://www.flapw.de or # -# http://aiida-fleur.readthedocs.io/en/develop/ # -############################################################################### -""" -This file is just were to hardcode some schema file paths -""" - -from __future__ import absolute_import -import os - -# any additional schema file add here, plugin will find them -SCHEMA_FILE_PATHS = [ - './input/0.27/FleurInputSchema.xsd', './input/0.28/FleurInputSchema.xsd', './input/0.29/FleurInputSchema.xsd', - './input/0.30/FleurInputSchema.xsd', './input/0.31/FleurInputSchema.xsd', './input/0.32/FleurInputSchema.xsd', - './input/0.33/FleurInputSchema.xsd', '.' -] - -PACKAGE_DIRECTORY = os.path.dirname(os.path.abspath(__file__)) - - -def get_schema_paths(): - """ - returns all know hardcodes schemas as a list of abs paths - """ - schema_paths = [] - for schema in SCHEMA_FILE_PATHS: - path = os.path.abspath(os.path.join(PACKAGE_DIRECTORY, schema)) - if os.path.isfile(path): - schema_paths.append(path) - return schema_paths - - -def get_internal_search_paths(): - """ - returns all abs paths to dirs where schema files might be - """ - #schema_paths = [] - #for schema in schema_file_paths: - # path = os.path.abspath(os.path.join(package_directory, schema)) - # if os.path.isdir(path): - # schema_paths.append(path) - schema_paths = [PACKAGE_DIRECTORY] - return schema_paths diff --git a/aiida_fleur/parsers/fleur.py b/aiida_fleur/parsers/fleur.py index e810febb3..bfbbfe91a 100644 --- a/aiida_fleur/parsers/fleur.py +++ b/aiida_fleur/parsers/fleur.py @@ -22,14 +22,14 @@ import os import re import json -import numpy as np -from datetime import date from lxml import etree from aiida.parsers import Parser from aiida.orm import Dict, BandsData from aiida.common.exceptions import NotExistent -from aiida_fleur.common.constants import HTR_TO_EV + +from masci_tools.io.parsers.fleur import outxml_parser +from masci_tools.io.parsers.fleur.fleur_schema import InputSchemaDict class FleurParser(Parser): @@ -167,7 +167,7 @@ def parse(self, **kwargs): try: time_avail_sec = self.node.attributes['last_job_info']['requested_wallclock_time_seconds'] time_calculated = self.node.attributes['last_job_info']['wallclock_time_seconds'] - if time_avail_sec < 1.01 * time_calculated: + if 0.97 * time_avail_sec < time_calculated: return self.exit_codes.ERROR_TIME_LIMIT except KeyError: pass @@ -175,6 +175,8 @@ def parse(self, **kwargs): if (kb_used * mpiprocs / mem_kb_avail > 0.93 or 'cgroup out-of-memory handler' in error_file_lines or 'Out Of Memory' in error_file_lines): return self.exit_codes.ERROR_NOT_ENOUGH_MEMORY + elif 'TIME LIMIT' in error_file_lines or 'time limit' in error_file_lines: + return self.exit_codes.ERROR_TIME_LIMIT elif 'Atom spills out into vacuum during relaxation' in error_file_lines: return self.exit_codes.ERROR_VACUUM_SPILL_RELAX elif 'Error checking M.T. radii' in error_file_lines: @@ -182,7 +184,8 @@ def parse(self, **kwargs): elif 'Overlapping MT-spheres during relaxation: ' in error_file_lines: overlap_line = re.findall(r'\S+ +\S+ olap: +\S+', error_file_lines)[0].split() with output_folder.open('relax.xml', 'r') as rlx: - relax_dict = parse_relax_file(rlx) + schema_dict = InputSchemaDict.fromVersion('0.34') + relax_dict = parse_relax_file(rlx, schema_dict) it_number = len(relax_dict['energies']) + 1 # relax.xml was not updated error_params = { 'error_name': 'MT_OVERLAP_RELAX', @@ -198,10 +201,11 @@ def parse(self, **kwargs): return self.exit_codes.ERROR_MT_RADII_RELAX elif 'Invalid elements in mmpmat' in error_file_lines: invalid_mmpmat = True - elif 'parent_folder' in calc.inputs: - if 'fleurinpdata' in calc.inputs: - if 'relax.xml' in calc.inputs.fleurinpdata.files: - return self.exit_codes.ERROR_DROP_CDN + elif 'parent_folder' in calc.inputs: # problem in reusing cdn for relaxations, drop cdn + if 'fleurinpdata' in calc.inputs and 'relax.xml' in calc.inputs.fleurinpdata.files: + return self.exit_codes.ERROR_DROP_CDN + else: + return self.exit_codes.ERROR_FLEUR_CALC_FAILED else: return self.exit_codes.ERROR_FLEUR_CALC_FAILED @@ -219,25 +223,31 @@ def parse(self, **kwargs): if has_xml_outfile: # open output file - with output_folder.open(FleurCalculation._OUTXML_FILE_NAME, 'r') as outxmlfile_opened: - simpledata, complexdata, parser_info, success = parse_xmlout_file(outxmlfile_opened) + + with output_folder.open(FleurCalculation._OUTXML_FILE_NAME, 'rb') as outxmlfile_opened: + success = True + parser_info = {} + try: + out_dict = outxml_parser(outxmlfile_opened, parser_info_out=parser_info, ignore_validation=True) + except (ValueError, FileNotFoundError, KeyError) as exc: + self.logger.error(f'XML output parsing failed: {str(exc)}') + success = False # Call routines for output node creation if not success: self.logger.error('Parsing of XML output file was not successfull.') + parameter_data = dict(list(parser_info.items())) + outxml_params = Dict(dict=parameter_data) + link_name = self.get_linkname_outparams() + self.out(link_name, outxml_params) return self.exit_codes.ERROR_XMLOUT_PARSING_FAILED - elif simpledata: - outputdata = dict(list(simpledata.items()) + list(parser_info.items())) + elif out_dict: + outputdata = dict(list(out_dict.items()) + list(parser_info.items())) outxml_params = Dict(dict=outputdata) link_name = self.get_linkname_outparams() self.out(link_name, outxml_params) - elif complexdata: - parameter_data = dict(list(complexdata.items()) + list(parser_info.items())) - outxml_params_complex = Dict(dict=parameter_data) - link_name = self.get_linkname_outparams_complex() - self.out(link_name, outxml_params_complex) else: - self.logger.error('Something went wrong, neither simpledata nor complexdata found') + self.logger.error('Something went wrong, no out_dict found') parameter_data = dict(list(parser_info.items())) outxml_params = Dict(dict=parameter_data) link_name = self.get_linkname_outparams() @@ -283,12 +293,14 @@ def parse(self, **kwargs): else: old_relax_text = '' + inp_version = outxml_params.get_dict().get('input_file_version', '0.34') + schema_dict = InputSchemaDict.fromVersion(inp_version) # dummy comparison between old and new relax - with output_folder.open(relax_name, 'r') as rlx: + with output_folder.open(relax_name, 'rb') as rlx: new_relax_text = rlx.read() if new_relax_text != old_relax_text: try: - relax_dict = parse_relax_file(rlx) + relax_dict = parse_relax_file(rlx, schema_dict) except etree.XMLSyntaxError: return self.exit_codes.ERROR_RELAX_PARSING_FAILED self.out('relax_parameters', relax_dict) @@ -297,820 +309,6 @@ def parse(self, **kwargs): return self.exit_codes.ERROR_INVALID_ELEMENTS_MMPMAT -def parse_xmlout_file(outxmlfile): - """ - Parses the out.xml file of a FLEUR calculation - Receives as input the absolute path to the xml output file - - :param outxmlfile: path to out.xml file - - :returns xml_data_dict: a simple dictionary (QE output like) - with parsed data - - """ - #from lxml import etree - - #global parser_info_out - # FIXME: This is global, look for a different way to do this, python logging? - - parser_info_out = {'parser_warnings': [], 'unparsed': []} - parser_version = '0.3.2' - parser_info_out['parser_info'] = 'AiiDA Fleur Parser v{}'.format(parser_version) - #parsed_data = {} - - successful = True - outfile_broken = False - parse_xml = True - parser = etree.XMLParser(recover=False) - - try: - tree = etree.parse(outxmlfile, parser) - except etree.XMLSyntaxError: - outfile_broken = True - parser_info_out['parser_warnings'].append('The out.xml file is broken I try to repair it.') - - if outfile_broken: - # repair xmlfile and try to parse what is possible. - parser = etree.XMLParser(recover=True) - try: - tree = etree.parse(outxmlfile, parser) - except etree.XMLSyntaxError: - parser_info_out['parser_warnings'].append('Skipping the parsing of the xml file. ' - 'Repairing was not possible.') - parse_xml = False - successful = False - - def parse_simplexmlout_file(root, outfile_broken): - """ - Parses the xml.out file of a Fleur calculation - Receives in input the root of an xmltree of the xml output file - - :param root: root node of etree of out.xml file - :param outfile_broken: a boolen that indicates if an out.xml has a broken last iteration - output - - :returns xml_data_dict: a simple dictionary (QE output like) - with parsed data - """ - - ### all xpath used. (maintain this) ### - iteration_xpath = '/fleurOutput/scfLoop/iteration' - magnetism_xpath = '/fleurOutput/inputData/calculationSetup/magnetism' - - relPos_xpath = '/fleurOutput/inputData/atomGroups/atomGroup/relPos' - absPos_xpath = '/fleurOutput/inputData/atomGroups/atomGroup/absPos' - filmPos_xpath = '/fleurOutput/inputData/atomGroups/atomGroup/filmPos' - - atomstypes_xpath = '/fleurOutput/inputData/atomGroups/atomGroup' - symmetries_xpath = '/fleurOutput/inputData/cell/symmetryOperations/symOp' - kpoints_xpath = '/fleurOutput/inputData/calculationSetup/bzIntegration/kPointList/kPoint' - species_xpath = '/fleurOutput/inputData/atomSpecies' - - # input parameters - creator_name_xpath = 'programVersion/@version' - output_version_xpath = '/fleurOutput/@fleurOutputVersion' - creator_target_architecture_xpath = 'programVersion/targetComputerArchitectures/text()' - creator_target_structure_xpath = 'programVersion/targetStructureClass/text()' - precision_xpath = 'programVersion/precision/@type' - - title_xpath = '/fleurOutput/inputData/comment/text()' - kmax_xpath = 'calculationSetup/cutoffs' - gmax_xpath = 'calculationSetup/cutoffs' - mixing_xpath = 'calculationSetup/scfLoop' - number_of_bands_xpath = 'calculationSetup/cutoffs' - spin_orbit_calculation = 'calculationSetup/soc' - smearing_energy_xpath = 'calculationSetup/bzIntegration/@fermiSmearingEnergy' - jspin_name = 'jspins' - l_f_xpath = '/fleurOutput/inputData/calculationSetup/geometryOptimization/@l_f' - ldau_xpath = '/fleurOutput/inputData/atomSpecies/species/ldaU' - - # timing - start_time_xpath = '/fleurOutput/startDateAndTime/@time' - end_time_xpath = '/fleurOutput/endDateAndTime/@time' - start_date_xpath = '/fleurOutput/startDateAndTime/@date' - end_date_xpath = '/fleurOutput/endDateAndTime/@date' - - ########### - - # get all iterations in out.xml file - iteration_nodes = eval_xpath2(root, iteration_xpath) - n_iters = len(iteration_nodes) - data_exists = True - - # parse only last stable interation - # (if modes (dos and co) maybe parse anyway if broken?) - if outfile_broken and (n_iters >= 2): - iteration_to_parse = iteration_nodes[-2] - parser_info_out['last_iteration_parsed'] = n_iters - 2 - elif outfile_broken and (n_iters == 1): - iteration_to_parse = iteration_nodes[0] - parser_info_out['last_iteration_parsed'] = n_iters - elif not outfile_broken and (n_iters >= 1): - iteration_to_parse = iteration_nodes[-1] - else: # there was no iteration found. - # only the starting charge density could be generated - parser_info_out['parser_warnings'].append('There was no iteration found in the outfile, either just a ' - 'starting density was generated or something went wrong.') - data_exists = False - iteration_to_parse = None - - # for getting the fleur modes use fleurinp methods - spin = get_xml_attribute(eval_xpath(root, magnetism_xpath), jspin_name) - - if spin: - fleurmode = {'jspin': int(spin)} - else: - fleurmode = {'jspin': 1} - - relax = eval_xpath(root, l_f_xpath) - fleurmode['relax'] = relax == 'T' - fleurmode['ldau'] = len(eval_xpath2(root, ldau_xpath)) != 0 - - if data_exists: - simple_data = parse_simple_outnode(iteration_to_parse, fleurmode) - else: - simple_data = {} - - # TODO: in the future add here the warnings returned from parse_simple_outnode - # Currently Fleur warnings an errors are not written to the out.xml - # should they be lists or dicts? - warnings = {'info': {}, 'debug': {}, 'warning': {}, 'error': {}} - - simple_data['number_of_atoms'] = (len(eval_xpath2(root, relPos_xpath)) + len(eval_xpath2(root, absPos_xpath)) + - len(eval_xpath2(root, filmPos_xpath))) - simple_data['number_of_atom_types'] = len(eval_xpath2(root, atomstypes_xpath)) - simple_data['number_of_iterations'] = n_iters - simple_data['number_of_symmetries'] = len(eval_xpath2(root, symmetries_xpath)) - simple_data['number_of_species'] = len(eval_xpath2(root, species_xpath)) - simple_data['number_of_kpoints'] = len(eval_xpath2(root, kpoints_xpath)) - simple_data['number_of_spin_components'] = fleurmode['jspin'] - - if fleurmode['ldau']: - ldaU_definitions = eval_xpath2(root, ldau_xpath) - for ldaU in ldaU_definitions: - parent = ldaU.getparent() - element_name = get_xml_attribute(parent, 'element') - species_name = get_xml_attribute(parent, 'name') - ldauKey = f'{element_name}/{species_name}' - - if ldauKey not in simple_data['ldau_info']: - simple_data['ldau_info'][ldauKey] = {} - - ldau_l = get_xml_attribute(ldaU, 'l') - ldau_l, suc = convert_to_int(ldau_l) - ldau_l = 'spdf'[ldau_l] - simple_data['ldau_info'][ldauKey][ldau_l] = {} - - ldau_u = get_xml_attribute(ldaU, 'U') - simple_data['ldau_info'][ldauKey][ldau_l]['u'], suc = convert_to_float(ldau_u) - - ldau_j = get_xml_attribute(ldaU, 'J') - simple_data['ldau_info'][ldauKey][ldau_l]['j'], suc = convert_to_float(ldau_j) - - simple_data['ldau_info'][ldauKey][ldau_l]['unit'] = 'eV' - - ldau_amf = get_xml_attribute(ldaU, 'l_amf') == 'T' - if ldau_amf: - ldau_dc = 'AMF' - else: - ldau_dc = 'FLL' - simple_data['ldau_info'][ldauKey][ldau_l]['double_counting'] = ldau_dc - - title = eval_xpath(root, title_xpath) - if title: - title = str(title).strip() - simple_data['title'] = title - simple_data['creator_name'] = eval_xpath(root, creator_name_xpath) - simple_data['creator_target_architecture'] = eval_xpath(root, creator_target_architecture_xpath) - simple_data['creator_target_structure'] = eval_xpath(root, creator_target_structure_xpath) - simple_data['output_file_version'] = eval_xpath(root, output_version_xpath) - - # time - # Maybe change the behavior if things could not be parsed... - # Especially if file was broken, ie endtime it not there. - starttime = eval_xpath(root, start_time_xpath) - if starttime: - starttimes = starttime.split(':') - else: - starttimes = [0, 0, 0] - msg = 'Startime was unparsed, inp.xml prob not complete, do not believe the walltime!' - if data_exists: - parser_info_out['parser_warnings'].append(msg) - - endtime = eval_xpath(root, end_time_xpath) - if endtime: - endtimes = endtime.split(':') - else: - endtimes = [0, 0, 0] - msg = 'Endtime was unparsed, inp.xml prob not complete, do not believe the walltime!' - if data_exists: - parser_info_out['parser_warnings'].append(msg) - start_date = eval_xpath(root, start_date_xpath) - end_date = eval_xpath(root, end_date_xpath) - - offset = 0 - if start_date != end_date: - # date="2018/01/15", Can this fail? what happens if not there - if start_date and end_date: - date_sl = [int(ent) for ent in start_date.split('/')] - date_el = [int(ent) for ent in end_date.split('/')] - date_s = date(*date_sl) - date_e = date(*date_el) - diff = date_e - date_s - offset = diff.days * 86400 - # ncores = 12 #TODO parse parallelization_Parameters - time = offset + (int(endtimes[0]) - int(starttimes[0])) * 60 * 60 + ( - int(endtimes[1]) - int(starttimes[1])) * 60 + int(endtimes[2]) - int(starttimes[2]) - simple_data['walltime'] = time - simple_data['walltime_units'] = 'seconds' - #simple_data['core_hours'] = time*ncores*1.0/3600 - #simple_data['parallelization_Parameters'] = {'mpiPEs' : ncores} - simple_data['start_date'] = {'date': start_date, 'time': starttime} - simple_data['end_date'] = {'date': end_date, 'time': endtime} - - warnings['info'] = {} # TODO - warnings['debug'] = {} # TODO - warnings['warning'] = {} # TODO - warnings['error'] = {} # TODO - simple_data['warnings'] = warnings - - return simple_data - - # TODO find a way to import these from xml_util, but make the parser logger work... - def eval_xpath(node, xpath): - """ - Tries to evaluate an xpath expression. If it fails it logs it. - - :param node: root node of an etree - :param xpath: xpath expression (relative, or absolute) - :returns: text, attribute or a node list - """ - try: - return_value = node.xpath(xpath) - except etree.XPathEvalError: - parser_info_out['parser_warnings'].append('There was a XpathEvalError on the xpath: {} \n Either it does ' - 'not exist, or something is wrong with the expression.' - ''.format(xpath)) - return [] # or rather None? - if len(return_value) == 1: - return return_value[0] - else: - return return_value - - def eval_xpath2(node, xpath): - """ - Tries to evaluate an xpath expression. If it fails it logs it. - It always return a list even if a single element was evaluated. - - :param node: root node of an etree - :param xpath: xpath expression (relative, or absolute) - :returns: a node list - """ - try: - return_value = node.xpath(xpath) - except etree.XPathEvalError: - parser_info_out['parser_warnings'].append('There was a XpathEvalError on the xpath: {} \n Either it does ' - 'not exist, or something is wrong with the expression.' - ''.format(xpath)) - return [] - return return_value - - def get_xml_attribute(node, attributename): - """ - Get an attribute value from a node. - - :param node: a node from etree - :param attributename: a string with the attribute name. - :returns: attributevalue or None - """ - if etree.iselement(node): - attrib_value = node.get(attributename) - if attrib_value: - return attrib_value - else: - parser_info_out['parser_warnings'].append('Tried to get attribute: "{}" from element {}.\n ' - 'I received "{}", maybe the attribute does not exist' - ''.format(attributename, node, attrib_value)) - return None - else: # something doesn't work here, some nodes get through here - parser_info_out['parser_warnings'].append('Can not get attributename: "{}" from node "{}", ' - 'because node is not an element of etree.' - ''.format(attributename, node)) - - return None - - def convert_to_float(value_string): - """ - Tries to make a float out of a string. If it can't it logs a warning - and returns True or False if convertion worked or not. - - :param value_string: a string - :returns value: the new float or value_string: the string given - :returns: True if convertation was successfull, False otherwise - """ - try: - value = float(value_string) - except TypeError: - parser_info_out['parser_warnings'].append('Could not convert: "{}" to float, TypeError' - ''.format(value_string)) - return value_string, False - except ValueError: - parser_info_out['parser_warnings'].append('Could not convert: "{}" to float, ValueError' - ''.format(value_string)) - return value_string, False - return value, True - - def convert_to_int(value_string): - """ - Tries to make a int out of a string. If it can't it logs a warning - and returns True or False if convertion worked or not. - - :param value_string: a string - :returns value: the new int or value_string: the string given - :returns: True or False - """ - try: - value = int(value_string) - except TypeError: - parser_info_out['parser_warnings'].append('Could not convert: "{}" to int, TypeError' - ''.format(value_string)) - return value_string, False - except ValueError: - parser_info_out['parser_warnings'].append('Could not convert: "{}" to int, ValueError' - ''.format(value_string)) - return value_string, False - return value, True - - def convert_htr_to_ev(value): - """ - Multiplies the value given with the Hartree factor (converts htr to eV) - """ - #htr = 27.21138602 - suc = False - value_to_save, suc = convert_to_float(value) - if suc: - return value_to_save * HTR_TO_EV - else: - return value - - def parse_simple_outnode(iteration_node, fleurmode): - """ - Parses the data from the iteration given (usually last iteration) - and some other data for the 'simple' output node. - - :param iteration_node: etree node of an interation - :param Fleurmode: python dictionary, with all the modes, - which influence the parsing - - :returns simple_data: a python dictionary with all results - """ - ################################################### - ########## all xpaths (maintain this) ############ - # (specifies where to find things in the out.xml) # - - # density - densityconvergence_xpath = 'densityConvergence' - chargedensity_xpath = 'densityConvergence/chargeDensity' - overallchargedensity_xpath = 'densityConvergence/overallChargeDensity' - spindensity_xpath = 'densityConvergence/spinDensity' - - bandgap_xpath = 'bandgap' - fermi_energy_xpath = 'FermiEnergy' - - # magnetic moments - magnetic_moments_in_mtpheres_xpath = 'magneticMomentsInMTSpheres' - magneticmoment_xpath = 'magneticMomentsInMTSpheres/magneticMoment' - - magneticmoments_xpath = 'magneticMomentsInMTSpheres/magneticMoment/@moment' - magneticmoments_spinupcharge_xpath = 'magneticMomentsInMTSpheres/magneticMoment/@spinUpCharge' - magneticmoments_spindowncharge_xpath = 'magneticMomentsInMTSpheres/magneticMoment/@spinDownCharge' - - orbmagnetic_moments_in_mtpheres_xpath = 'orbitalMagneticMomentsInMTSpheres' - orbmagneticmoment_xpath = 'orbitalMagneticMomentsInMTSpheres/orbMagMoment' - - orbmagneticmoments_xpath = 'orbitalMagneticMomentsInMTSpheres/orbMagMoment/@moment' - orbmagneticmoments_spinupcharge_xpath = 'orbitalMagneticMomentsInMTSpheres/orbMagMoment/@spinUpCharge' - orbmagneticmoments_spindowncharge_xpath = 'orbitalMagneticMomentsInMTSpheres/orbMagMoment/@spinDownCharge' - - mae_force_theta_xpath = 'Forcetheorem_MAE/Angle/@theta' - mae_force_phi_xpath = 'Forcetheorem_MAE/Angle/@phi' - mae_force_evsum_xpath = 'Forcetheorem_MAE/Angle/@ev-sum' - mae_force_energ_units_xpath = 'Forcetheorem_Loop_MAE/sumValenceSingleParticleEnergies/@units' - - spst_force_xpath = 'Forcetheorem_SSDISP/@qvectors' - spst_force_q_xpath = 'Forcetheorem_SSDISP/Entry/@q' - spst_force_evsum_xpath = 'Forcetheorem_SSDISP/Entry/@ev-sum' - spst_force_energ_units_xpath = 'Forcetheorem_Loop_SSDISP/sumValenceSingleParticleEnergies/@units' - - dmi_force_xpath = 'Forcetheorem_DMI' - dmi_force_q_xpath = 'Forcetheorem_DMI/Entry/@q' - dmi_force_theta_xpath = 'Forcetheorem_DMI/Entry/@theta' - dmi_force_phi_xpath = 'Forcetheorem_DMI/Entry/@phi' - dmi_force_evsum_xpath = 'Forcetheorem_DMI/Entry/@ev-sum' - dmi_force_angles_xpath = 'Forcetheorem_DMI/@Angles' - dmi_force_qs_xpath = 'Forcetheorem_DMI/@qPoints' - dmi_force_energ_units_xpath = 'Forcetheorem_Loop_DMI/sumValenceSingleParticleEnergies/@units' - - spinupcharge_name = 'spinUpCharge' - spindowncharge_name = 'spinDownCharge' - moment_name = 'moment' - - # All electron charges - all_spin_charges_total_xpath = 'allElectronCharges/spinDependentCharge/@total' - all_spin_charges_interstitial_xpath = 'allElectronCharges/spinDependentCharge/@interstitial' - all_spin_charges_mt_spheres_xpath = 'allElectronCharges/spinDependentCharge/@mtSpheres' - all_total_charge_xpath = 'allElectronCharges/totalCharge/@value' - - # energy - totalenergy_xpath = 'totalEnergy' - sumofeigenvalues_xpath = 'totalEnergy/sumOfEigenvalues' - core_electrons_xpath = 'totalEnergy/sumOfEigenvalues/coreElectrons' - valence_electrons_xpath = 'totalEnergy/sumOfEigenvalues/valenceElectrons' - chargeden_xc_den_integral_xpath = 'totalEnergy/chargeDenXCDenIntegral' - - #free_energy_xpath = 'totalEnergy/freeEnergy' - - # forces - forces_units_xpath = 'totalForcesOnRepresentativeAtoms' - forces_total_xpath = 'totalForcesOnRepresentativeAtoms/forceTotal' - - #ldau - eldau_xpath = 'totalEnergy/dftUCorrection/@value' - ldaudistances_xpath = 'ldaUdensityMatrixConvergence/distance/' - - # - iteration_xpath = '.' - - units_name = 'units' - value_name = 'value' - distance_name = 'distance' - - overall_number_name = 'overallNumber' - atomtype_name = 'atomType' - - # forces - f_x_name = 'F_x' - f_y_name = 'F_y' - f_z_name = 'F_z' - new_x_name = 'x' - new_y_name = 'y' - new_z_name = 'z' - - species_xpath = '/fleurOutput/inputData/atomSpecies' - relPos_xpath = '/fleurOutput/inputData/atomGroups/atomGroup/relPos' - absPos_xpath = '/fleurOutput/inputData/atomGroups/atomGroup/absPos' - filmPos_xpath = '/fleurOutput/inputData/atomGroups/atomGroup/filmPos' - atomstypes_xpath = '/fleurOutput/inputData/atomGroups/atomGroup' - - film_lat_xpath = '/fleurOutput/inputData/cell/filmLattice/bravaisMatrix/' - bulk_lat_xpath = '/fleurOutput/inputData/cell/bulkLattice/bravaisMatrix/' - kmax_xpath = '/fleurOutput/inputData/calculationSetup/cutoffs/@Kmax' - - ################################################### - - jspin = fleurmode['jspin'] - relax = fleurmode['relax'] - ldaU = fleurmode['ldau'] - simple_data = {} - - def write_simple_outnode(value, value_type, value_name, dict_out): - """ - Writes a value (int or float) in the simple data dict. - if path does not exit it initializes it! - - If the value has not the type given, - it will be logged in parser info instead of writing. - - :param value: value ti write - :param value_type: a type of a value: 'float', 'int', 'str', 'list', 'list_floats', - 'list_ints', list_list_floats - :param value_name: a name of a value to be used in the dictionary - """ - - iteration_current_number_name = 'numberForCurrentRun' - suc = False - - if value_type == 'float': - value_to_save, suc = convert_to_float(value) - elif value_type == 'int': - value_to_save, suc = convert_to_int(value) - elif value_type == 'str': - suc = True - value_to_save = value - #value_to_save, suc = convert_to_str(value) - elif value_type == 'list': - suc = True - value_to_save = value - elif value_type == 'list_floats': - value_to_save = [] - for val in value: - value_to_savet, suct = convert_to_float(val) - value_to_save.append(value_to_savet) - suc = True # TODO individual or common error message? - elif value_type == 'list_ints': - value_to_save = [] - for val in value: - value_to_savet, suct = convert_to_int(val) - value_to_save.append(value_to_savet) - suc = True - elif value_type == 'list_list_floats': - value_to_save = [] - for val in value: - value_to_savet = [] - for val1 in val: - value_to_savet1, suct = convert_to_float(val1) - value_to_savet.append(value_to_savet1) - value_to_save.append(value_to_savet) - suc = True # TODO individual or common error message? - else: - #self.logger.error('I dont know the type you gave me {}'.format(type)) - pass - # TODO log error, self is not known here... - if suc: - dict_out[value_name] = value_to_save - else: - parser_info_out['unparsed'].append({ - value_name: - value, - 'iteration': - get_xml_attribute(iteration_node, iteration_current_number_name) - }) - - if eval_xpath(iteration_node, mae_force_theta_xpath) != []: - # extract MAE force theorem parameters - mae_force_theta = eval_xpath2(iteration_node, mae_force_theta_xpath) - write_simple_outnode(mae_force_theta, 'list_floats', 'mae_force_theta', simple_data) - - mae_force_evsum = eval_xpath2(iteration_node, mae_force_evsum_xpath) - write_simple_outnode(mae_force_evsum, 'list_floats', 'mae_force_evSum', simple_data) - - mae_force_phi = eval_xpath2(iteration_node, mae_force_phi_xpath) - write_simple_outnode(mae_force_phi, 'list_floats', 'mae_force_phi', simple_data) - - units_e = eval_xpath2(iteration_node, mae_force_energ_units_xpath) - write_simple_outnode(units_e[0], 'str', 'energy_units', simple_data) - elif eval_xpath(iteration_node, spst_force_xpath) != []: - # extract Spin spiral dispersion force theorem parameters - spst_force_q = eval_xpath2(iteration_node, spst_force_q_xpath) - write_simple_outnode(spst_force_q, 'list_floats', 'spst_force_q', simple_data) - - spst_force_evsum = eval_xpath2(iteration_node, spst_force_evsum_xpath) - write_simple_outnode(spst_force_evsum, 'list_floats', 'spst_force_evSum', simple_data) - - units_e = eval_xpath2(iteration_node, spst_force_energ_units_xpath) - write_simple_outnode(units_e[0], 'str', 'energy_units', simple_data) - elif eval_xpath(iteration_node, dmi_force_xpath) != []: - # extract DMI force theorem parameters - dmi_force_q = eval_xpath2(iteration_node, dmi_force_q_xpath) - write_simple_outnode(dmi_force_q, 'list_ints', 'dmi_force_q', simple_data) - - dmi_force_evsum = eval_xpath2(iteration_node, dmi_force_evsum_xpath) - write_simple_outnode(dmi_force_evsum, 'list_floats', 'dmi_force_evSum', simple_data) - - dmi_force_theta = eval_xpath2(iteration_node, dmi_force_theta_xpath) - write_simple_outnode(dmi_force_theta, 'list_floats', 'dmi_force_theta', simple_data) - - dmi_force_phi = eval_xpath2(iteration_node, dmi_force_phi_xpath) - write_simple_outnode(dmi_force_phi, 'list_floats', 'dmi_force_phi', simple_data) - - dmi_force_angles = eval_xpath(iteration_node, dmi_force_angles_xpath) - write_simple_outnode(dmi_force_angles, 'int', 'dmi_force_angles', simple_data) - - dmi_force_qs = eval_xpath(iteration_node, dmi_force_qs_xpath) - write_simple_outnode(dmi_force_qs, 'int', 'dmi_force_qs', simple_data) - - units_e = eval_xpath2(iteration_node, dmi_force_energ_units_xpath) - write_simple_outnode(units_e[0], 'str', 'energy_units', simple_data) - else: - # total energy - - kmax_used = eval_xpath2(root, kmax_xpath)[0] - write_simple_outnode(kmax_used, 'float', 'kmax', simple_data) - - units_e = get_xml_attribute(eval_xpath(iteration_node, totalenergy_xpath), units_name) - write_simple_outnode(units_e, 'str', 'energy_hartree_units', simple_data) - - tE_htr = get_xml_attribute(eval_xpath(iteration_node, totalenergy_xpath), value_name) - write_simple_outnode(tE_htr, 'float', 'energy_hartree', simple_data) - - write_simple_outnode(convert_htr_to_ev(tE_htr), 'float', 'energy', simple_data) - write_simple_outnode('eV', 'str', 'energy_units', simple_data) - - sumofeigenvalues = get_xml_attribute(eval_xpath(iteration_node, sumofeigenvalues_xpath), value_name) - write_simple_outnode(sumofeigenvalues, 'float', 'sum_of_eigenvalues', simple_data) - - coreElectrons = get_xml_attribute(eval_xpath(iteration_node, core_electrons_xpath), value_name) - write_simple_outnode(coreElectrons, 'float', 'energy_core_electrons', simple_data) - - valenceElectrons = get_xml_attribute(eval_xpath(iteration_node, valence_electrons_xpath), value_name) - write_simple_outnode(valenceElectrons, 'float', 'energy_valence_electrons', simple_data) - - ch_d_xc_d_inte = get_xml_attribute(eval_xpath(iteration_node, chargeden_xc_den_integral_xpath), value_name) - write_simple_outnode(ch_d_xc_d_inte, 'float', 'charge_den_xc_den_integral', simple_data) - - # bandgap - units_bandgap = get_xml_attribute(eval_xpath(iteration_node, bandgap_xpath), units_name) - write_simple_outnode(units_bandgap, 'str', 'bandgap_units', simple_data) - - bandgap = get_xml_attribute(eval_xpath(iteration_node, bandgap_xpath), value_name) - write_simple_outnode(bandgap, 'float', 'bandgap', simple_data) - - # fermi - fermi_energy = get_xml_attribute(eval_xpath(iteration_node, fermi_energy_xpath), value_name) - write_simple_outnode(fermi_energy, 'float', 'fermi_energy', simple_data) - units_fermi_energy = get_xml_attribute(eval_xpath(iteration_node, fermi_energy_xpath), units_name) - write_simple_outnode(units_fermi_energy, 'str', 'fermi_energy_units', simple_data) - - # density convergence - units = get_xml_attribute(eval_xpath(iteration_node, densityconvergence_xpath), units_name) - write_simple_outnode(units, 'str', 'density_convergence_units', simple_data) - - if jspin == 1: - if not relax: # there are no charge densities written if relax - charge_density = get_xml_attribute(eval_xpath(iteration_node, chargedensity_xpath), distance_name) - write_simple_outnode(charge_density, 'float', 'charge_density', simple_data) - - elif jspin == 2: - charge_densitys = eval_xpath2(iteration_node, chargedensity_xpath) - - if not relax: # there are no charge densities written if relax - if charge_densitys: # otherwise we get a keyerror if calculation failed. - charge_density1 = get_xml_attribute(charge_densitys[0], distance_name) - charge_density2 = get_xml_attribute(charge_densitys[1], distance_name) - else: # Is non a problem? - charge_density1 = None - charge_density2 = None - write_simple_outnode(charge_density1, 'float', 'charge_density1', simple_data) - write_simple_outnode(charge_density2, 'float', 'charge_density2', simple_data) - - spin_density = get_xml_attribute(eval_xpath(iteration_node, spindensity_xpath), distance_name) - write_simple_outnode(spin_density, 'float', 'spin_density', simple_data) - - overall_charge_density = get_xml_attribute(eval_xpath(iteration_node, overallchargedensity_xpath), - distance_name) - write_simple_outnode(overall_charge_density, 'float', 'overall_charge_density', simple_data) - - # magnetic moments - m_units = get_xml_attribute(eval_xpath(iteration_node, magnetic_moments_in_mtpheres_xpath), units_name) - write_simple_outnode(m_units, 'str', 'magnetic_moment_units', simple_data) - write_simple_outnode(m_units, 'str', 'orbital_magnetic_moment_units', simple_data) - - moments = eval_xpath(iteration_node, magneticmoments_xpath) - write_simple_outnode(moments, 'list_floats', 'magnetic_moments', simple_data) - - spinup = eval_xpath(iteration_node, magneticmoments_spinupcharge_xpath) - write_simple_outnode(spinup, 'list_floats', 'magnetic_spin_up_charges', simple_data) - - spindown = eval_xpath(iteration_node, magneticmoments_spindowncharge_xpath) - write_simple_outnode(spindown, 'list_floats', 'magnetic_spin_down_charges', simple_data) - - spindown = eval_xpath(iteration_node, magneticmoments_spindowncharge_xpath) - write_simple_outnode(spindown, 'list_floats', 'magnetic_spin_down_charges', simple_data) - - # Total charges, total magentic moment - - total_c = eval_xpath2(iteration_node, all_spin_charges_total_xpath) - write_simple_outnode(total_c, 'list_floats', 'spin_dependent_charge_total', simple_data) - - total_magentic_moment_cell = None - if len(total_c) == 2: - val, suc = convert_to_float(total_c[0]) - val2, suc2 = convert_to_float(total_c[1]) - total_magentic_moment_cell = np.abs(val - val2) - write_simple_outnode(total_magentic_moment_cell, 'float', 'total_magnetic_moment_cell', simple_data) - - total_c_i = eval_xpath2(iteration_node, all_spin_charges_interstitial_xpath) - write_simple_outnode(total_c_i, 'list_floats', 'spin_dependent_charge_interstitial', simple_data) - - total_c_mt = eval_xpath2(iteration_node, all_spin_charges_mt_spheres_xpath) - write_simple_outnode(total_c_mt, 'list_floats', 'spin_dependent_charge_mt', simple_data) - - total_c = eval_xpath(iteration_node, all_total_charge_xpath) - write_simple_outnode(total_c, 'float', 'total_charge', simple_data) - - # orbital magnetic moments - orbmoments = eval_xpath(iteration_node, orbmagneticmoments_xpath) - write_simple_outnode(orbmoments, 'list_floats', 'orbital_magnetic_moments', simple_data) - - orbspinup = eval_xpath(iteration_node, orbmagneticmoments_spinupcharge_xpath) - write_simple_outnode(orbspinup, 'list_floats', 'orbital_magnetic_spin_up_charges', simple_data) - - orbspindown = eval_xpath(iteration_node, orbmagneticmoments_spindowncharge_xpath) - write_simple_outnode(orbspindown, 'list_floats', 'orbital_magnetic_spin_down_charges', simple_data) - - if ldaU: - simple_data['ldau_info'] = {} - eldau = eval_xpath(iteration_node, eldau_xpath) - write_simple_outnode(eldau, 'float', 'ldau_energy_correction', simple_data['ldau_info']) - write_simple_outnode(units_e, 'str', 'unit', simple_data['ldau_info']) - - ldau_distances = eval_xpath2(iteration_node, ldaudistances_xpath) - write_simple_outnode(ldau_distances, 'list_floats', 'density_matrix_distance', simple_data['ldau_info']) - - if relax: - # check if it is a film or a bulk structure - film = eval_xpath2(root, os.path.join(film_lat_xpath, 'row-1')) - if film: - lat_path = film_lat_xpath - pos_attr = 'filmPos' - else: - lat_path = bulk_lat_xpath - pos_attr = 'relPos' - - v_1 = eval_xpath(root, os.path.join(lat_path, 'row-1')) - v_1 = [float(x) for x in v_1.text.split()] - - v_2 = eval_xpath(root, os.path.join(lat_path, 'row-2')) - v_2 = [float(x) for x in v_2.text.split()] - - v_3 = eval_xpath(root, os.path.join(lat_path, 'row-3')) - v_3 = [float(x) for x in v_3.text.split()] - - relax_brav_vectors = [v_1, v_2, v_3] - - atom_positions = [] - relax_atom_info = [] - - all_atoms = eval_xpath2(root, atomstypes_xpath) - for a_type in all_atoms: - species = get_xml_attribute(a_type, 'species') - full_xpath = species_xpath + '/species[@name = "{}"]/@element'.format(species) - element = eval_xpath(root, full_xpath) - type_positions = eval_xpath2(a_type, pos_attr) - for pos in type_positions: - pos = [convert_frac(x) for x in pos.text.split()] - atom_positions.append(pos) - relax_atom_info.append([species, element]) - - write_simple_outnode(relax_atom_info, 'list', 'relax_atomtype_info', simple_data) - write_simple_outnode(relax_brav_vectors, 'list', 'relax_brav_vectors', simple_data) - write_simple_outnode(atom_positions, 'list', 'relax_atom_positions', simple_data) - write_simple_outnode(str(bool(film)), 'str', 'film', simple_data) - - # total iterations - number_of_iterations_total = get_xml_attribute(eval_xpath(iteration_node, iteration_xpath), - overall_number_name) - write_simple_outnode(number_of_iterations_total, 'int', 'number_of_iterations_total', simple_data) - - # forces atomtype dependend - forces = eval_xpath2(iteration_node, forces_total_xpath) - # length should be ntypes - largest_force = -0.0 - for force in forces: - atomtype, _ = convert_to_int(get_xml_attribute(force, atomtype_name)) - - forces_unit = get_xml_attribute(eval_xpath(iteration_node, forces_units_xpath), units_name) - write_simple_outnode(forces_unit, 'str', 'force_units', simple_data) - - force_x = get_xml_attribute(force, f_x_name) - write_simple_outnode(force_x, 'float', 'force_x_type{}'.format(atomtype), simple_data) - - force_y = get_xml_attribute(force, f_y_name) - write_simple_outnode(force_y, 'float', 'force_y_type{}'.format(atomtype), simple_data) - - force_z = get_xml_attribute(force, f_z_name) - write_simple_outnode(force_z, 'float', 'force_z_type{}'.format(atomtype), simple_data) - - force_xf, suc1 = convert_to_float(force_x) - force_yf, suc2 = convert_to_float(force_y) - force_zf, suc3 = convert_to_float(force_z) - - if suc1 and suc2 and suc3: - if abs(force_xf) > largest_force: - largest_force = abs(force_xf) - if abs(force_yf) > largest_force: - largest_force = abs(force_yf) - if abs(force_zf) > largest_force: - largest_force = abs(force_zf) - - pos_x = get_xml_attribute(force, new_x_name) - write_simple_outnode(pos_x, 'float', 'abspos_x_type{}'.format(atomtype), simple_data) - pos_y = get_xml_attribute(force, new_y_name) - write_simple_outnode(pos_y, 'float', 'abspos_y_type{}'.format(atomtype), simple_data) - pos_z = get_xml_attribute(force, new_z_name) - write_simple_outnode(pos_z, 'float', 'abspos_z_type{}'.format(atomtype), simple_data) - - write_simple_outnode(largest_force, 'float', 'force_largest', simple_data) - - return simple_data - - if parse_xml: - root = tree.getroot() - if root is None: - parser_info_out['parser_warnings'].append( - 'Somehow the root from the xmltree is None, which it should not be, I skip the parsing.') - successful = False - return {}, {}, parser_info_out, successful - else: - simple_out = parse_simplexmlout_file(root, outfile_broken) - #simple_out['outputfile_path'] = outxmlfile - # TODO: parse complex out - complex_out = {} # parse_xmlout_file(root) - return simple_out, complex_out, parser_info_out, successful - else: - return {}, {}, parser_info_out, successful - - def parse_dos_file(dos_lines): # , number_of_atom_types): """ Parses the returned DOS.X files. @@ -1156,53 +354,16 @@ def parse_bands_file(bands_lines): return fleur_bands -def parse_relax_file(rlx): +def parse_relax_file(relax_file, schema_dict): """ This function parsers relax.xml output file and returns a Dict containing all the data given there. """ - from aiida_fleur.tools.xml_util import eval_xpath2 - - rlx.seek(0) - tree = etree.parse(rlx) + from masci_tools.util.xml.xml_getters import get_relaxation_information - xpath_disp = '/relaxation/displacements/displace' - xpath_energy = '/relaxation/relaxation-history/step/@energy' - xpath_steps = '/relaxation/relaxation-history/step' + relax_file.seek(0) + tree = etree.parse(relax_file) - root = tree.getroot() - - displacements = eval_xpath2(root, xpath_disp) - float_displ = [] - for i in displacements: - temp = [convert_frac(x) for x in i.text.split()] - float_displ.append(temp) - - energies = eval_xpath2(root, xpath_energy) - energies = [float(x) for x in energies] - - float_posforces = [] - iter_all = eval_xpath2(root, xpath_steps) - for posf in iter_all: - posforces = eval_xpath2(posf, 'posforce') - temp2 = [] - for i in posforces: - temp = [convert_frac(x) for x in i.text.split()] - temp2.append(temp) - float_posforces.append(temp2) - - out_dict = {} - out_dict['displacements'] = float_displ - out_dict['energies'] = energies - out_dict['posforces'] = float_posforces + out_dict = get_relaxation_information(tree, schema_dict) return Dict(dict=out_dict) - - -def convert_frac(ratio): - """ Converts ratio strings into float, e.g. 1.0/2.0 -> 0.5 """ - try: - return float(ratio) - except ValueError: - num, denom = ratio.split('/') - return float(num) / float(denom) diff --git a/aiida_fleur/parsers/fleur_inputgen.py b/aiida_fleur/parsers/fleur_inputgen.py index 8cf2adb01..f2d869087 100644 --- a/aiida_fleur/parsers/fleur_inputgen.py +++ b/aiida_fleur/parsers/fleur_inputgen.py @@ -22,6 +22,8 @@ from aiida_fleur.data.fleurinp import FleurinpData from aiida_fleur.calculation.fleurinputgen import FleurinputgenCalculation +import pprint + class Fleur_inputgenParser(Parser): """ @@ -69,10 +71,16 @@ def parse(self, **kwargs): except IOError: self.logger.error('Failed to open error file: {}.'.format(errorfile)) return self.exit_codes.ERROR_OPENING_OUTPUTS - # if not empty, has_error equals True, parse error. + # if not empty, has_error equals True, prior fleur 32 if error_file_lines: - self.logger.error("The following was written to the error file {} : \n '{}'" - ''.format(errorfile, error_file_lines)) + if isinstance(error_file_lines, type(b'')): + error_file_lines = error_file_lines.replace(b'\x00', b' ') + else: + error_file_lines = error_file_lines.replace('\x00', ' ') + if 'Run finished successfully' not in error_file_lines: + self.logger.warning('The following was written into std error and piped to {}' + ' : \n {}'.format(errorfile, error_file_lines)) + self.logger.error('Inpgen calculation did not finish' ' successfully.') inpxml_file = FleurinputgenCalculation._INPXML_FILE_NAME if inpxml_file not in list_of_files: @@ -81,14 +89,19 @@ def parse(self, **kwargs): for file1 in self._default_files: if file1 not in list_of_files: - self.logger.error("'{}' file not found in retrived folder, it was probably " + self.logger.error("'{}' file not found in retrieved folder, it was probably " 'not created by inpgen'.format(file1)) return self.exit_codes.ERROR_MISSING_RETRIEVED_FILES try: - fleurinp = FleurinpData(files=[inpxml_file], node=output_folder) + fleurinp = FleurinpData(files=[]) + fleurinp.set_file(inpxml_file, node=output_folder) except InputValidationError as ex: self.logger.error('FleurinpData initialization failed: {}'.format(str(ex))) + if fleurinp.parser_info == {}: + self.logger.error('Parser output: No Output produced') + else: + self.logger.error(f'Parser output: {pprint.pformat(fleurinp.parser_info)}') return self.exit_codes.ERROR_FLEURINPDATA_INPUT_NOT_VALID except ValidationError as ex: self.logger.error('FleurinpData validation failed: {}'.format(str(ex))) diff --git a/aiida_fleur/tools/StructureData_util.py b/aiida_fleur/tools/StructureData_util.py index 1ce02a3ce..96dacfcce 100644 --- a/aiida_fleur/tools/StructureData_util.py +++ b/aiida_fleur/tools/StructureData_util.py @@ -1186,7 +1186,7 @@ def create_manual_slab_ase(lattice='fcc', *_, layer_occupancies = get_layers(structure) - if replacements is not None: + if replacements is not None and len(replacements) > 0: keys = list(replacements.keys()) if max((abs(int(x)) for x in keys)) >= len(layer_occupancies): raise ValueError('"replacements" has to contain numbers less than number of layers:' @@ -1295,7 +1295,12 @@ def magnetic_slab_from_relaxed(relaxed_structure, magn_structure = StructureData(cell=sorted_struc.cell) magn_structure.pbc = (True, True, False) for kind in relaxed_structure.kinds: - magn_structure.append_kind(kind) + kind_append = kind + kind_append.name = kind.name.split('-')[0] + try: + magn_structure.append_kind(kind) + except ValueError: + pass done_layers = 0 while True: @@ -1308,7 +1313,10 @@ def magnetic_slab_from_relaxed(relaxed_structure, elif done_layers < total_number_layers: k = done_layers % num_layers_org layer, pos_z, _ = get_layers(orig_structure) - add_distance = abs(pos_z[k] - pos_z[k - 1]) + if k == 0: + add_distance = abs(pos_z[0] + orig_structure.cell[2][2] - pos_z[-1]) + else: + add_distance = abs(pos_z[k] - pos_z[k - 1]) prev_layer_z = magn_structure.sites[-1].position[2] for atom in layer[k]: atom[0][2] = prev_layer_z + add_distance @@ -1325,6 +1333,7 @@ def magnetic_slab_from_relaxed(relaxed_structure, def get_layers(structure, decimals=10): """ Extracts atom positions and their types belonging to the same layer + Removes any information related to kind specie. :param structure: ase lattice or StructureData which represents a slab :param number: the layer number. Note, that layers will be sorted according to z-position @@ -1342,7 +1351,9 @@ def get_layers(structure, decimals=10): structure = copy.deepcopy(structure) if isinstance(structure, StructureData): - reformat = [(list(x.position), x.kind_name) for x in sorted(structure.sites, key=lambda x: x.position[2])] + reformat = [ + (list(x.position), x.kind_name.split('-')[0]) for x in sorted(structure.sites, key=lambda x: x.position[2]) + ] elif isinstance(structure, Lattice): reformat = list(zip(structure.positions, structure.get_chemical_symbols())) else: @@ -1463,7 +1474,7 @@ def suggest_distance_to_previous(num_layer): else: rebuilt_structure.append_atom(symbols=atom[1], position=(atom[0][0], atom[0][1], -atom[0][2]), - name=atom[1] + '49') + name=atom[1] + '49999') prev_distance = 0 for i, layer in enumerate(layers[1:]): @@ -1482,7 +1493,7 @@ def suggest_distance_to_previous(num_layer): # a = Site(kind_name=atom[1], position=atom[0]) # rebuilt_structure.append_site(a) if i < hold_layers - 1: - rebuilt_structure.append_atom(position=atom[0], symbols=atom[1], name=atom[1] + '49') + rebuilt_structure.append_atom(position=atom[0], symbols=atom[1], name=atom[1] + '49999') else: rebuilt_structure.append_atom(position=atom[0], symbols=atom[1], name=atom[1]) diff --git a/aiida_fleur/tools/common_fleur_wf.py b/aiida_fleur/tools/common_fleur_wf.py index 30eb2d1c6..f204cd17e 100644 --- a/aiida_fleur/tools/common_fleur_wf.py +++ b/aiida_fleur/tools/common_fleur_wf.py @@ -22,53 +22,44 @@ from aiida.orm import Node, load_node, Bool from aiida.plugins import DataFactory, CalculationFactory - -def is_code(code): - """ - Test if the given input is a Code node, by object, id, uuid, or pk - if yes returns a Code node in all cases - if no returns None - """ - from aiida.orm import Code - from aiida.common.exceptions import NotExistent, MultipleObjectsError, InputValidationError - - if isinstance(code, Code): - return code - - try: - pk = int(code) - except ValueError: - codestring = str(code) - try: - code = Code.get_from_string(codestring) - except NotExistent: - try: - code = load_node(codestring) - except NotExistent: - code = None - except (InputValidationError, MultipleObjectsError): - code = None - else: - try: - code = load_node(pk) - except NotExistent: - code = None - - if isinstance(code, Code): - return code - else: - return None - - -def get_inputs_fleur(code, - remote, - fleurinp, - options, - label='', - description='', - settings=None, - serial=False, - only_even_MPI=False): +# def is_code(code): +# """ +# Test if the given input is a Code node, by object, id, uuid, or pk +# if yes returns a Code node in all cases +# if no returns None +# """ +# from aiida.orm import Code +# from aiida.common.exceptions import NotExistent, MultipleObjectsError, InputValidationError + +# if isinstance(code, Code): +# return code + +# try: +# pk = int(code) +# except ValueError: +# codestring = str(code) +# try: +# code = Code.get_from_string(codestring) +# except NotExistent: +# try: +# code = load_node(codestring) +# except NotExistent: +# code = None +# except (InputValidationError, MultipleObjectsError): +# code = None +# else: +# try: +# code = load_node(pk) +# except NotExistent: +# code = None + +# if isinstance(code, Code): +# return code +# else: +# return None + + +def get_inputs_fleur(code, remote, fleurinp, options, label='', description='', settings=None, add_comp_para=None): ''' Assembles the input dictionary for Fleur Calculation. Does not check if a user gave correct input types, it is the work of FleurCalculation to check it. @@ -92,10 +83,14 @@ def get_inputs_fleur(code, ''' Dict = DataFactory('dict') inputs = {} - if isinstance(only_even_MPI, Bool): - inputs['only_even_MPI'] = only_even_MPI - else: - inputs['only_even_MPI'] = Bool(only_even_MPI) + + if add_comp_para is None: + add_comp_para = { + 'serial': False, + 'only_even_MPI': False, + 'max_queue_nodes': 20, + 'max_queue_wallclock_sec': 86400 + } if remote: inputs['parent_folder'] = remote @@ -113,7 +108,7 @@ def get_inputs_fleur(code, else: inputs['label'] = '' # TODO check if code is parallel version? - if serial: + if add_comp_para['serial']: if not options: options = {} options['withmpi'] = False # for now @@ -124,6 +119,8 @@ def get_inputs_fleur(code, else: options['withmpi'] = True + inputs['add_comp_para'] = Dict(dict=add_comp_para) + custom_commands = options.get('custom_scheduler_commands', '') options['custom_scheduler_commands'] = custom_commands @@ -223,7 +220,7 @@ def test_and_get_codenode(codenode, expected_code_type, use_exceptions=False): qb = QueryBuilder() qb.append(Code, filters={'attributes.input_plugin': {'==': expected_code_type}}, project='*') - valid_code_labels = ['{}@{}'.format(c.label, c.computer.name) for [c] in qb.all()] + valid_code_labels = ['{}@{}'.format(c.label, c.computer.label) for [c] in qb.all()] if valid_code_labels: msg = ('Given Code node is not of expected code type.\n' @@ -316,7 +313,7 @@ def determine_favorable_reaction(reaction_list, workchain_dict): if not formenergy: # test if 0 case ok if isinstance(n, WorkChain): # TODO: untested for aiida > 1.0 plabel = n.get_attr('_process_label') - if plabel == 'fleur_initial_cls_wc': + if plabel == 'FleurInitialCLSWorkChain': try: ouputnode = n.out.output_initial_cls_wc_para.get_dict() except AttributeError: @@ -423,10 +420,10 @@ def performance_extract_calcs(calcs): data_dict['n_iterations'].append(niter) data_dict['n_iterations_total'].append(res.number_of_iterations_total) - if u'charge_density' in res_keys: - data_dict['density_distance'].append(res.charge_density) + if u'overall_density_convergence' not in res_keys: + data_dict['density_distance'].append(res.density_convergence) else: # magnetic, old - data_dict['density_distance'].append(res.overall_charge_density) + data_dict['density_distance'].append(res.overall_density_convergence) walltime = res.walltime if walltime <= 0: @@ -440,7 +437,7 @@ def performance_extract_calcs(calcs): data_dict['walltime_sec'].append(walltime) data_dict['walltime_sec_cor'].append(walltime_new) data_dict['walltime_sec_per_it'].append(walltime_periteration) - cname = calc.computer.name + cname = calc.computer.label data_dict['computer'].append(cname) natom = res.number_of_atoms data_dict['n_atoms'].append(natom) @@ -546,16 +543,7 @@ def optimize_calc_options(nodes, cpus_per_node = mpi_per_node * omp_per_mpi if fleurinpData: - modes = fleurinpData.get_fleur_modes() - kpts = fleurinpData.attributes['inp_dict']['calculationSetup']['bzIntegration'] - if modes['band'] or modes['gw']: - kpts = kpts['altKPointSet']['count'] - else: - if 'kPointList' in kpts: - kpts = kpts['kPointList']['count'] - else: - kpts = kpts['kPointCount']['count'] - kpts = int(kpts) + kpts = fleurinpData.get_nkpts() elif not kpts: raise ValueError('You must specify either kpts of fleurinpData') divisors_kpts = divisors(kpts) diff --git a/aiida_fleur/tools/data_handling.py b/aiida_fleur/tools/data_handling.py index 16ba72a02..888ac6bdf 100644 --- a/aiida_fleur/tools/data_handling.py +++ b/aiida_fleur/tools/data_handling.py @@ -128,10 +128,10 @@ def extract_structure_info(keys, structures=None): eos = input_of_workcal('fleur_eos_wc', struc) structure_dict['eos'] = eos if 'init_cls' in keys: - init_cls = input_of_workcal('fleur_initial_cls_wc', struc) + init_cls = input_of_workcal('FleurInitialCLSWorkChain', struc) structure_dict['init_cls'] = init_cls if 'corehole' in keys: - corehole = input_of_workcal('fleur_corehole_wc', struc) + corehole = input_of_workcal('', struc) structure_dict['corehole'] = corehole if 'calcfunctions' in keys: calcfunctions_uuid, calcfunctions_name = input_of_calcfunctions(struc) diff --git a/aiida_fleur/tools/dict_util.py b/aiida_fleur/tools/dict_util.py index e8e7dcf37..e6796fe25 100644 --- a/aiida_fleur/tools/dict_util.py +++ b/aiida_fleur/tools/dict_util.py @@ -13,9 +13,7 @@ This contains code snippets and utility useful for dealing with parameter data nodes commonly used by the fleur plugin and workflows """ -from __future__ import print_function -from __future__ import absolute_import -import six + import typing as typ import collections @@ -29,7 +27,7 @@ def extract_elementpara(parameter_dict, element): the atom parameters for the given element """ element_para_dict = {} - for key, val in six.iteritems(parameter_dict): + for key, val in parameter_dict.items(): if 'atom' in key: if val.get('element', '') == element: element_para_dict[key] = val @@ -65,7 +63,7 @@ def dict_merger(dict1, dict2): new_dict[key] = dict2[key] # merge common - for key, val in six.iteritems(dict1): + for key, val in dict1.items(): if isinstance(val, dict): new_dict[key] = dict_merger(val, dict2.get(key, {})) elif isinstance(val, list): diff --git a/aiida_fleur/tools/element_econfig_list.py b/aiida_fleur/tools/element_econfig_list.py index df05373d7..0c120160c 100644 --- a/aiida_fleur/tools/element_econfig_list.py +++ b/aiida_fleur/tools/element_econfig_list.py @@ -27,6 +27,11 @@ # because the starting point matters, especially in the magnetic case. econfiguration = { + 0: { # This is for empty spheres etc. + 'mass': 1.00000, + 'name': 'Unknown', + 'symbol': 'X' + }, 1: { 'mass': 1.00794, 'name': 'Hydrogen', @@ -1639,9 +1644,8 @@ def get_state_occ(econfigstr, corehole='', valence='', ch_occ=1.0): import os aiida_path = os.path.dirname(aiida_fleur.__file__) EXP_BINDENERGIES_PATH = os.path.join(aiida_path, 'tools/exp_bindingenergies.json') -fn = open(EXP_BINDENERGIES_PATH, 'r') -exp_bindingenergies = json.load(fn) -fn.close() +with open(EXP_BINDENERGIES_PATH, 'r') as fn: + exp_bindingenergies = json.load(fn) """ exp_bindingenergies = { 1 : {'binding_energy': {'1s1/2': []}, 'name': 'Hydrogen', 'symbol': 'H'}, diff --git a/aiida_fleur/tools/extract_corelevels.py b/aiida_fleur/tools/extract_corelevels.py index a527caddc..059e3d27c 100644 --- a/aiida_fleur/tools/extract_corelevels.py +++ b/aiida_fleur/tools/extract_corelevels.py @@ -16,13 +16,9 @@ # TODO clean up # TODO together with xml_util, parser info handling, has to be also a return value of everything # or rather throw exception on lowest level and catch at higher levels? - -from __future__ import absolute_import -from __future__ import print_function -import six from lxml import etree #, objectify -from aiida_fleur.tools.xml_util import get_xml_attribute, eval_xpath, eval_xpath2 +from masci_tools.util.xml.common_functions import eval_xpath, get_xml_attribute #convert_to_float #import time @@ -161,7 +157,7 @@ def extract_corelevels(outxmlfile, options=None): # 2. get all species from input # get element, name, coreStates # TODO why can this not be eval_xpath2? - species_nodes = eval_xpath(root, species_xpath, parser_info) + species_nodes = eval_xpath(root, species_xpath) species_atts = {} species_names = [] for species in species_nodes: @@ -171,9 +167,9 @@ def extract_corelevels(outxmlfile, options=None): species_atomicnumber = species.get('atomicNumber') species_magMom = species.get('magMom') #TODO sometimes not in inp.xml... what if it is not there - coreconfig = eval_xpath(species, coreconfig_xpath, parser_info) - valenceconfig = eval_xpath(species, valenceconfig_xpath, parser_info) - state_occ = eval_xpath2(species, state_occ_xpath, parser_info) + coreconfig = eval_xpath(species, coreconfig_xpath) + valenceconfig = eval_xpath(species, valenceconfig_xpath) + state_occ = eval_xpath(species, state_occ_xpath, list_return=True) #parse state occ state_results = [] @@ -198,7 +194,7 @@ def extract_corelevels(outxmlfile, options=None): #3. get number of atom types and their species from input atomtypes = [] - atomgroup_nodes = eval_xpath(root, atomgroup_xpath, parser_info) #/fleurinp/ + atomgroup_nodes = eval_xpath(root, atomgroup_xpath) #/fleurinp/ # always a list? for atomgroup in atomgroup_nodes: types_dict = {} @@ -210,9 +206,9 @@ def extract_corelevels(outxmlfile, options=None): coreconf = species_atts[group_species]['coreconfig'] valenceconf = species_atts[group_species]['valenceconfig'] stateocc = species_atts[group_species]['stateOccupation'] - a = eval_xpath2(atomgroup, relpos_xpath, - parser_info) + eval_xpath2(atomgroup, abspos_xpath, parser_info) + eval_xpath2( - atomgroup, filmpos_xpath, parser_info) # always list + a = eval_xpath(atomgroup, relpos_xpath, list_return=True) \ + + eval_xpath(atomgroup, abspos_xpath, list_return=True) \ + + eval_xpath(atomgroup, filmpos_xpath, list_return=True) # always list natoms = len(a) types_dict = { 'species': group_species, @@ -233,12 +229,12 @@ def extract_corelevels(outxmlfile, options=None): #5 init saving arrays: #6 parse corelevels: - iteration_nodes = eval_xpath2(root, iteration_xpath, parser_info) + iteration_nodes = eval_xpath(root, iteration_xpath, list_return=True) nIteration = len(iteration_nodes) if nIteration >= 1: iteration_to_parse = iteration_nodes[-1] #TODO:Optional all or other #print iteration_to_parse - corestatescards = eval_xpath2(iteration_to_parse, relcoreStates_xpath, parser_info) + corestatescards = eval_xpath(iteration_to_parse, relcoreStates_xpath, list_return=True) # maybe does not return a list... for atype in atomtypes: # spin=2 is already in there corelevels.append([]) @@ -294,14 +290,14 @@ def parse_state_card(corestateNode, iteration_node, parser_info=None): if parser_info is None: parser_info = {'parser_warnings': []} - atomtype = get_xml_attribute(corestateNode, atomtype_name, parser_info) + atomtype = get_xml_attribute(corestateNode, atomtype_name) - kinEnergy = get_xml_attribute(corestateNode, kinEnergy_name, parser_info) - vE2, suc = convert_to_float(kinEnergy, parser_info) - eigenvalueSum = get_xml_attribute(corestateNode, eigenvalueSum_name, parser_info) - vE2, suc = convert_to_float(eigenvalueSum, parser_info) + kinEnergy = get_xml_attribute(corestateNode, kinEnergy_name) + vE2, suc = convert_to_float(kinEnergy) + eigenvalueSum = get_xml_attribute(corestateNode, eigenvalueSum_name) + vE2, suc = convert_to_float(eigenvalueSum) - spin = get_xml_attribute(corestateNode, spin_name, parser_info) + spin = get_xml_attribute(corestateNode, spin_name) #print('spin {}'.format(spin)) #states = corestateNode.xpath( #for state in states: @@ -313,15 +309,15 @@ def parse_state_card(corestateNode, iteration_node, parser_info=None): # some only the first interation, then get all state tags of the corestate tag (atom depended) # parse each core state #Attention to spin states = [] - corestates = eval_xpath2(corestateNode, state_xpath) #, parser_info) + corestates = eval_xpath(corestateNode, state_xpath, list_return=True) #, parser_info) for corestate in corestates: # be careful that corestates is a list state_dict = {} - n_state = get_xml_attribute(corestate, n_name, parser_info) - l_state = get_xml_attribute(corestate, l_name, parser_info) - j_state = get_xml_attribute(corestate, j_name, parser_info) - energy, suc = convert_to_float(get_xml_attribute(corestate, energy_name, parser_info), parser_info) - weight, suc = convert_to_float(get_xml_attribute(corestate, weight_name, parser_info), parser_info) + n_state = get_xml_attribute(corestate, n_name) + l_state = get_xml_attribute(corestate, l_name) + j_state = get_xml_attribute(corestate, j_name) + energy, suc = convert_to_float(get_xml_attribute(corestate, energy_name)) + weight, suc = convert_to_float(get_xml_attribute(corestate, weight_name)) state_dict = {'n': n_state, 'l': l_state, 'j': j_state, 'energy': energy, 'weight': weight} states.append(state_dict) @@ -411,7 +407,7 @@ def clshifts_to_be(coreleveldict, reference_dict, warn=False): return_corelevel_dict = {} - for elem, corelevel_dict in six.iteritems(coreleveldict): + for elem, corelevel_dict in coreleveldict.items(): ref_el = reference_dict.get(elem, {}) if not ref_el: # no refernce for that element given @@ -420,7 +416,7 @@ def clshifts_to_be(coreleveldict, reference_dict, warn=False): continue return_corelevel_dict[elem] = {} - for corelevel_name, corelevel_list in six.iteritems(corelevel_dict): + for corelevel_name, corelevel_list in corelevel_dict.items(): ref_cl = ref_el.get(corelevel_name, []) if not ref_cl: # no reference corelevel given for that element if warn: diff --git a/aiida_fleur/tools/io_routines.py b/aiida_fleur/tools/io_routines.py index e824fa7dd..4dc922aca 100644 --- a/aiida_fleur/tools/io_routines.py +++ b/aiida_fleur/tools/io_routines.py @@ -14,10 +14,6 @@ For example collection of data or database evaluations, for other people. """ -from __future__ import absolute_import -from __future__ import print_function -import six - def write_results_to_file(headerstring, data, destination='./outputfile', seperator=' ', transpose=True): """ @@ -38,7 +34,7 @@ def write_results_to_file(headerstring, data, destination='./outputfile', sepera for item in datat: itemstring = '' for value in item: - if isinstance(value, (six.string_types, str)): + if isinstance(value, str): itemstring = itemstring + '{}{}'.format(value, seperator) else: itemstring = itemstring + '{0:0.8f}{1:s}'.format(float(value), seperator) @@ -127,7 +123,8 @@ def compress_fleuroutxml(outxmlfilepath, dest_file_path=None, delete_eig=True, i """ - from aiida_fleur.tools.xml_util import delete_tag, eval_xpath2 + from masci_tools.util.xml.common_functions import eval_xpath + from masci_tools.util.xml.xml_setters_basic import xml_delete_tag from lxml import etree xpath_eig = '/fleurOutput/scfLoop/iteration/eigenvalues' @@ -158,12 +155,12 @@ def compress_fleuroutxml(outxmlfilepath, dest_file_path=None, delete_eig=True, i # delete eigenvalues (all) if delete_eig: - new_etree = delete_tag(tree, xpath_eig) + new_etree = xml_delete_tag(tree, xpath_eig) # delete certain iterations if iterations_to_keep is not None: root = new_etree.getroot() - iteration_nodes = eval_xpath2(root, xpath_iter) + iteration_nodes = eval_xpath(root, xpath_iter, list_return=True) n_iters = len(iteration_nodes) print(n_iters) if iterations_to_keep < 0: @@ -178,7 +175,7 @@ def compress_fleuroutxml(outxmlfilepath, dest_file_path=None, delete_eig=True, i ' in the given out.xml file, I keep all.') else: print(delete_xpath) - new_etree = delete_tag(new_etree, delete_xpath) + new_etree = xml_delete_tag(new_etree, delete_xpath) if dest_file_path is None: dest_file_path = outxmlfilepath # overwrite file diff --git a/aiida_fleur/tools/plot/fleur.py b/aiida_fleur/tools/plot/fleur.py index 3b9cd8d52..a933adc2d 100644 --- a/aiida_fleur/tools/plot/fleur.py +++ b/aiida_fleur/tools/plot/fleur.py @@ -19,10 +19,7 @@ # INFO: AiiDAlab has implemented an extendable viewer class for data structures, # which might be some point moved to aiida-core and extensible over entrypoints. -from __future__ import absolute_import -from __future__ import print_function from pprint import pprint -import six import numpy as np #import matplotlib.pyplot as pp #from masci_tools.vis.plot_methods import * @@ -66,25 +63,21 @@ def set_plot_defaults(title_fontsize = 16, save_plots = False, #True, save_format = 'pdf'): ''' - from masci_tools.vis.plot_methods import set_plot_defaults - - save = False - show_dict = False - show = True - backend = 'matplotlib' - for key, val in six.iteritems(kwargs): - if key == 'save': - save = val - if key == 'show_dict': - show_dict = val - if key == 'backend': - backend = val - if key == 'show': - show = val + from masci_tools.vis.plot_methods import set_mpl_plot_defaults + from masci_tools.vis.bokeh_plots import set_bokeh_plot_defaults + + save = kwargs.pop('save', False) + show_dict = kwargs.pop('show_dict', False) + show = kwargs.pop('show', True) + backend = kwargs.pop('backend', 'matplotlib') + # # the rest we ignore for know #Just call set plot defaults # TODO, or rather parse it onto plot functions...? - set_plot_defaults(**kwargs) + if backend == 'matplotlib': + set_mpl_plot_defaults(**kwargs) + elif backend == 'bokeh': + set_bokeh_plot_defaults(**kwargs) all_plots = [] for arg in args: @@ -102,7 +95,7 @@ def set_plot_defaults(title_fontsize = 16, def plot_fleur_sn(node, show_dict=False, save=False, show=True, backend='bokeh'): """ - This methods takes any single AiiDA node and starts the standard visualisation for + This methods takes any single AiiDA node and starts the standard visualization for if it finds one """ #show_dic = show_dic @@ -110,7 +103,7 @@ def plot_fleur_sn(node, show_dict=False, save=False, show=True, backend='bokeh') if isinstance(node, int): #pk node = load_node(node) - if isinstance(node, (str, six.text_type)): #uuid + if isinstance(node, str): #, six.text_type)): #uuid node = load_node(node) #try if isinstance(node, Node): @@ -180,7 +173,7 @@ def plot_fleur_mn(nodelist, save=False, show=True, backend='bokeh'): # first find out what we have then how to visualize if isinstance(node, int): #pk node = load_node(node) - if isinstance(node, (str, six.text_type)): #uuid + if isinstance(node, str): #, six.text_type)): #uuid node = load_node(node) #try if isinstance(node, Node): @@ -208,7 +201,7 @@ def plot_fleur_mn(nodelist, save=False, show=True, backend='bokeh'): #print(all_nodes) all_plot_res = [] - for node_key, nodelist1 in six.iteritems(all_nodes): + for node_key, nodelist1 in all_nodes.items(): try: plotf = FUNCTIONS_DICT[node_key] except KeyError: @@ -235,9 +228,6 @@ def plot_fleur_scf_wc(nodes, labels=None, save=False, show=True, backend='bokeh' else: from masci_tools.vis.plot_methods import plot_convergence_results_m - if labels is None: - labels = [] - if isinstance(nodes, list): if len(nodes) >= 2: #return # TODO @@ -248,6 +238,9 @@ def plot_fleur_scf_wc(nodes, labels=None, save=False, show=True, backend='bokeh' nodes = [nodes] #[0]] #scf_wf = load_node(6513) + if labels is None: + labels = [node.pk for node in nodes] + iterations = [] distance_all_n = [] total_energy_n = [] @@ -284,23 +277,13 @@ def plot_fleur_scf_wc(nodes, labels=None, save=False, show=True, backend='bokeh' total_energy_n.append(total_energy) modes.append(mode) - #plot_convergence_results(distance_all, total_energy, iteration) - if labels: - plt = plot_convergence_results_m(distance_all_n, - total_energy_n, - iterations, - plot_labels=labels, - nodes=nodes_pk, - modes=modes, - show=show) - else: - plt = plot_convergence_results_m(distance_all_n, - total_energy_n, - iterations, - nodes=nodes_pk, - modes=modes, - show=show) - + plt = plot_convergence_results_m(iterations, + distance_all_n, + total_energy_n, + plot_label=labels, + nodes=nodes_pk, + modes=modes, + show=show) return plt @@ -324,8 +307,13 @@ def plot_fleur_dos_wc(node, labels=None, save=False, show=True, **kwargs): path_to_dosfile = output_d.get('dosfile', None) print(path_to_dosfile) if path_to_dosfile: - plot_dos(path_to_dosfile, only_total=False, show=show) - p1 = None # FIXME masci-tools should return something + data = np.loadtxt(path_to_dosfile, skiprows=0) + + energy = data[..., 0] + dos_labels = ['Total', 'Interstitial', 'MT-Total'] + dos_data = [data[:, 1], data[:, 2], data[:, 1] - data[:, 2]] + + p1 = plot_dos(energy, dos_data, show=show, plot_labels=dos_labels) else: print('Could not retrieve dos file path from output node') @@ -358,7 +346,7 @@ def plot_fleur_eos_wc(node, labels=None, save=False, show=True, **kwargs): scaling.append(outpara.get('scaling')) plotlables.append((r'gs_vol: {:.3} A^3, gs_scale {:.3}, data {}' ''.format(volume_gs, scale_gs, i))) plotlables.append(r'fit results {}'.format(i)) - plot_lattice_constant(Total_energy, scaling, multi=True, plotlables=plotlables, show=show) + plot_lattice_constant(scaling, Total_energy, multi=True, plot_label=plotlables, show=show) return # TODO else: node = node[0] @@ -374,7 +362,7 @@ def plot_fleur_eos_wc(node, labels=None, save=False, show=True, **kwargs): #fit_y = [] #fit_y = [parabola(scale2, fit[0], fit[1], fit[2]) for scale2 in scaling] - p1 = plot_lattice_constant(Total_energy, scaling, show=show) #, fit_y) + p1 = plot_lattice_constant(scaling, Total_energy, show=show) #, fit_y) return p1 @@ -399,12 +387,16 @@ def plot_fleur_band_wc(node, labels=None, save=False, show=True, **kwargs): print(path_to_bands_file) kpath = output_d.get('kpath', {}) #r"$\Gamma$": 0.00000, r"$H$" : 1.04590, # r"$N$" : 1.78546, r"$P$": 2.30841, r"$\Gamma1$" : 3.21419, r"$N1$" : 3.95375} ) - if path_to_bands_file: - plot_bands(path_to_bands_file, kpath) + data = np.loadtxt(path_to_bands_file, skiprows=0) + kdata = data[..., 0] + evdata = data[..., 1] + p1 = plot_bands(kdata, evdata, special_kpoints=kpath) else: print('Could not retrieve dos file path from output node') + return p1 + def plot_fleur_relax_wc(node, labels=None, save=False, show=True, **kwargs): """ @@ -432,7 +424,7 @@ def plot_fleur_corehole_wc(nodes, labels=None, save=False, show=True, **kwargs): def plot_fleur_initial_cls_wc(nodes, labels=None, save=False, show=True, **kwargs): """ - This methods takes AiiDA output parameter nodes from a initial_cls + This methods takes AiiDA output parameter nodes from a FleurInitialCLSWorkChain workchain and plots some information about corelevel shifts. (Spectra) """ @@ -450,8 +442,10 @@ def plot_fleur_initial_cls_wc(nodes, labels=None, save=False, show=True, **kwarg 'fleur_dos_wc': plot_fleur_dos_wc, 'fleur_band_wc': plot_fleur_band_wc, 'FleurBandWorkChain': plot_fleur_band_wc, - #'fleur_corehole_wc' : plot_fleur_corehole_wc, - #'fleur_initial_cls_wc' : plot_fleur_initial_cls_wc + #'fleur_corehole_wc' : plot_fleur_corehole_wc, #support of < 1.5 release + #'fleur_initial_cls_wc' : plot_fleur_initial_cls_wc, #support of < 1.5 release + #'FleurInitialCLSWorkChain' : plot_fleur_initial_cls_wc, + #'FleurCoreholeWorkChain' : plot_fleur_corehole_wc, } @@ -471,7 +465,7 @@ def clear_dict_empty_lists(to_clear_dict): if not isinstance(to_clear_dict, dict): return to_clear_dict - for key, value in six.iteritems(to_clear_dict): + for key, value in to_clear_dict.items(): if value: new_value = clear_dict_empty_lists(value) if new_value: diff --git a/aiida_fleur/tools/set_nmmpmat.py b/aiida_fleur/tools/set_nmmpmat.py deleted file mode 100644 index f3d47034f..000000000 --- a/aiida_fleur/tools/set_nmmpmat.py +++ /dev/null @@ -1,268 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################### -# Copyright (c), Forschungszentrum Jülich GmbH, IAS-1/PGI-1, Germany. # -# All rights reserved. # -# This file is part of the AiiDA-FLEUR package. # -# # -# The code is hosted on GitHub at https://github.com/JuDFTteam/aiida-fleur # -# For further information on the license, see the LICENSE.txt file # -# For further information please visit http://www.flapw.de or # -# http://aiida-fleur.readthedocs.io/en/develop/ # -############################################################################### -""" -This module contains useful methods for initializing or modifying a n_mmp_mat file -for LDA+U via the FleurinpModifier -""" -from __future__ import absolute_import -from __future__ import print_function -import numpy as np -from aiida_fleur.tools.xml_util import eval_xpath, eval_xpath2 -from aiida_fleur.tools.xml_util import get_xml_attribute, convert_to_int - -def set_nmmpmat(fleurinp_tree_copy, nmmp_lines_copy, species_name, orbital, spin,\ - occStates=None, denmat=None, phi=None, theta=None): - """Routine sets the block in the n_mmp_mat file specified by species_name, orbital and spin - to the desired density matrix - - :param fleurinp_tree_copy: an xmltree that represents inp.xml - :param nmmp_lines_copy: list of lines in the n_mmp_mat file - :param species_name: string, name of the species you want to change - :param orbital: integer, orbital quantum number of the LDA+U procedure to be modified - :param spin: integer, specifies which spin block should be modified - :param occStates: list, sets the diagonal elements of the density matrix and everything - else to zero - :param denmat: matrix, specify the density matrix explicitely - :param phi: float, optional angle (radian), by which to rotate the density matrix before writing it - :param theta: float, optional angle (radian), by which to rotate the density matrix before writing it - - :raises ValueError: If something in the input is wrong - :raises KeyError: If no LDA+U procedure is found on a species - """ - - #All lda+U procedures have to be considered since we need to keep the order - ldau_xpath = '/fleurInput/atomSpecies/species/ldaU' - magnetism_xpath = '/fleurInput/calculationSetup/magnetism' - - if species_name == 'all': - species_xpath = '/fleurInput/atomSpecies/species' - elif species_name[:4] == 'all-': #format all- - species_xpath = '/fleurInput/atomSpecies/species[contains(@name,"{}")]'.format(species_name[4:]) - else: - species_xpath = '/fleurInput/atomSpecies/species[@name = "{}"]'.format(species_name) - - all_species = eval_xpath2(fleurinp_tree_copy, species_xpath) - - #Get number of spins (TODO for develop version also read l_mtnocoPot) - mag_elem = eval_xpath(fleurinp_tree_copy, magnetism_xpath) - nspins = convert_to_int(get_xml_attribute(mag_elem, 'jspins'), suc_return=False) - - if spin > nspins: - raise ValueError(f'Invalid input: spin {spin} requested, but input has only {nspins} spins') - - all_ldau = eval_xpath2(fleurinp_tree_copy, ldau_xpath) - numRows = nspins * 14 * len(all_ldau) - - #Check that numRows matches the number of lines in nmmp_lines_copy - #If not either there was an n_mmp_mat file present in Fleurinp before and a lda+u calculation - #was added or removed or the n_mmp_mat file was initialized and after the fact lda+u procedures were added - #or removed. In both cases the resolution of this modification is very involved so we throw an error - if nmmp_lines_copy is not None: - #Remove blank lines - while '' in nmmp_lines_copy: - nmmp_lines_copy.remove('') - if numRows != len(nmmp_lines_copy): - raise ValueError('The number of lines in n_mmp_mat does not match the number expected from '+\ - 'the inp.xml file. Either remove the existing file before making modifications '+\ - 'and only use set_nmmpmat after all modifications to the inp.xml') - - if phi is not None or theta is not None: - if phi is None: - phi = 0.0 - if theta is None: - theta = 0.0 - d_wigner = get_wigner_matrix(orbital, phi, theta) - - for species in all_species: - current_name = get_xml_attribute(species, 'name') - - #Determine the place at which the given U procedure occurs - ldau_index = None - for index, ldau in enumerate(all_ldau): - ldau_species = get_xml_attribute(ldau.getparent(), 'name') - ldau_orbital = convert_to_int(get_xml_attribute(ldau, 'l'), suc_return=False) - if current_name == ldau_species and ldau_orbital == orbital: - ldau_index = index - - if ldau_index is None: - raise KeyError(f'No LDA+U procedure found on species {current_name} with l={orbital}') - - if occStates is not None: - #diagonal density matrix - denmatpad = np.zeros((7, 7), dtype=complex) - - #Fill out the outer states with zero - occStatespad = np.zeros(7, dtype=complex) - occStatespad[3 - orbital:4 + orbital] = occStates[:] - - for i, occ in enumerate(occStatespad): - denmatpad[i, i] = occ - elif denmat is not None: - #density matrix is completely specified - denmatpad = np.zeros((7, 7), dtype=complex) - denmatpad[3 - orbital:4 + orbital, 3 - orbital:4 + orbital] = denmat - else: - raise ValueError('Invalid definition of density matrix. Provide either occStates or denmat') - - if phi is not None and theta is not None: - #Rotate the density matrix - denmatpad = d_wigner.T.conj().dot(denmatpad.dot(d_wigner)) - - #check if fleurinp has a specified n_mmp_mat file if not initialize it with 0 - if nmmp_lines_copy is None: - nmmp_lines_copy = [] - for index in range(numRows): - nmmp_lines_copy.append(''.join(map(str, [f'{0.0:20.13f}' for x in range(7)]))) - - #Select the right block from n_mmp_mat and overwrite it with denmatpad - startRow = ((spin - 1) * len(all_ldau) + ldau_index) * 14 - for index in range(startRow, startRow + 14): - currentLine = index - startRow - currentRow = currentLine // 2 - if currentLine % 2 == 0: - #Line ends with a real part - nmmp_lines_copy[index] = ''.join(map(str, [f'{x.real:20.13f}{x.imag:20.13f}'\ - for x in denmatpad[currentRow, :3]])) +\ - f'{denmatpad[currentRow, 3].real:20.13f}' - else: - #Line begins with a imaginary part - nmmp_lines_copy[index] = f'{denmatpad[currentRow, 3].imag:20.13f}' +\ - ''.join(map(str, [f'{x.real:20.13f}{x.imag:20.13f}'\ - for x in denmatpad[currentRow, 4:]])) - - return nmmp_lines_copy - - -def validate_nmmpmat(fleurinp_tree, nmmp_lines): - """ - Checks that the given nmmp_lines is valid with the given fleurinp_tree - - Checks that the number of blocks is as expected from the inp.xml and each - block does not contain non-zero elements outside their size given by the - orbital quantum number in the inp.xml. Additionally the occupations, i.e. - diagonal elements are checked that they are in between 0 and the maximum - possible occupation - - :param fleurinp_tree_copy: an xmltree that represents inp.xml - :param nmmp_lines_copy: list of lines in the n_mmp_mat file - - :raises ValueError: if any of the above checks are violated. - """ - - #First check the number of ldau procedures - ldau_xpath = '/fleurInput/atomSpecies/species/ldaU' - magnetism_xpath = '/fleurInput/calculationSetup/magnetism' - - #Get number of spins (TODO for develop version also read l_mtnocoPot) - mag_elem = eval_xpath(fleurinp_tree, magnetism_xpath) - nspins = convert_to_int(get_xml_attribute(mag_elem, 'jspins'), suc_return=False) - - all_ldau = eval_xpath2(fleurinp_tree, ldau_xpath) - numRows = nspins * 14 * len(all_ldau) - - tol = 0.01 - if nspins > 1: - maxOcc = 1.0 - else: - maxOcc = 2.0 - - #Check that numRows matches the number of lines in nmmp_lines - if nmmp_lines is not None: - #Remove blank lines - while '' in nmmp_lines: - nmmp_lines.remove('') - if numRows != len(nmmp_lines): - raise ValueError('The number of lines in n_mmp_mat does not match the number expected from '+\ - 'the inp.xml file.') - else: - return - - #Now check for each block if the numbers make sense - #(no numbers outside the valid area and no nonsensical occupations) - for ldau_index, ldau in enumerate(all_ldau): - - orbital = convert_to_int(get_xml_attribute(ldau, 'l'), suc_return=False) - species_name = get_xml_attribute(ldau.getparent(), 'name') - - for spin in range(nspins): - startRow = (spin * len(all_ldau) + ldau_index) * 14 - - for index in range(startRow, startRow + 14): - currentLine = index - startRow - currentRow = currentLine // 2 - - line = nmmp_lines[index].split(' ') - while '' in line: - line.remove('') - nmmp = np.array([float(x) for x in line]) - - outside_val = False - if abs(currentRow - 3) > orbital: - if any(np.abs(nmmp) > 1e-12): - outside_val = True - - if currentLine % 2 == 0: - #m=-3 to m=0 real part - if any(np.abs(nmmp[:(3 - orbital) * 2]) > 1e-12): - outside_val = True - - else: - #m=0 imag part to m=3 - if any(np.abs(nmmp[orbital * 2 + 1:]) > 1e-12): - outside_val = True - - if outside_val: - raise ValueError(f'Found value outside of valid range in for species {species_name}, spin {spin+1}' - f' and l={orbital}') - - invalid_diag = False - if spin < 2: - if currentRow - 3 <= 0 and currentLine % 2 == 0: - if nmmp[currentRow * 2] < -tol or nmmp[currentRow * 2] > maxOcc + tol: - invalid_diag = True - else: - if nmmp[(currentRow - 3) * 2 - 1] < -tol or nmmp[(currentRow - 3) * 2 - 1] > maxOcc + tol: - invalid_diag = True - - if invalid_diag: - raise ValueError(f'Found invalid diagonal element for species {species_name}, spin {spin+1}' - f' and l={orbital}') - - -def get_wigner_matrix(l, phi, theta): - """Produces the wigner rotation matrix for the density matrix - - :param l: int, orbital quantum number - :param phi: float, angle (radian) corresponds to euler angle alpha - :param theta: float, angle (radian) corresponds to euler angle beta - """ - d_wigner = np.zeros((7, 7), dtype=complex) - for m in range(-l, l + 1): - for mp in range(-l, l + 1): - base = np.sqrt(fac(l + m) * fac(l - m) * fac(l + mp) * fac(l - mp)) - base *= np.exp(-1j * phi * mp) - - for x in range(max(0, m - mp), min(l - mp, l + m) + 1): - denom = fac(l - mp - x) * fac(l + m - x) * fac(x) * fac(x + mp - m) - - d_wigner[m + 3, mp + 3] += base/denom * (-1)**x * np.cos(theta/2.0)**(2*l+m-mp-2*x) \ - * np.sin(theta/2.0)**(2*x+mp-m) - - return d_wigner - - -def fac(n): - """Returns the factorial of n""" - if n < 2: - return 1 - else: - return n * fac(n - 1) diff --git a/aiida_fleur/tools/xml_aiida_modifiers.py b/aiida_fleur/tools/xml_aiida_modifiers.py new file mode 100644 index 000000000..661e92f92 --- /dev/null +++ b/aiida_fleur/tools/xml_aiida_modifiers.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +""" +This module defines XML modifying functions, that require an aiida node as input +""" + + +def set_kpointsdata_f(xmltree, schema_dict, kpointsdata_uuid, name=None, switch=False): + """This calc function writes all kpoints from a :class:`~aiida.orm.KpointsData` node + in the ``inp.xml`` file as a kpointslist. It replaces kpoints written in the + ``inp.xml`` file. Currently it is the users responsibility to provide a full + :class:`~aiida.orm.KpointsData` node with weights. + + :param fleurinp_tree_copy: fleurinp_tree_copy + :param kpointsdata_uuid: node identifier or :class:`~aiida.orm.KpointsData` node to be written into ``inp.xml`` + :return: modified xml tree + """ + # TODO: check on weights, + # also fleur allows for several kpoint sets, lists, paths and meshes, + # support this. + import numpy as np + from aiida.orm import KpointsData, load_node + from aiida.common.exceptions import InputValidationError + from masci_tools.util.xml.xml_setters_names import set_kpointlist + + if not isinstance(kpointsdata_uuid, KpointsData): + KpointsDataNode = load_node(kpointsdata_uuid) + else: + KpointsDataNode = kpointsdata_uuid + + if not isinstance(KpointsDataNode, KpointsData): + raise InputValidationError('The node given is not a valid KpointsData node.') + + try: + kpoints, weights = KpointsDataNode.get_kpoints(also_weights=True, cartesian=False) + except AttributeError: + kpoints = KpointsDataNode.get_kpoints(cartesian=False) + weights = np.ones(len(kpoints)) / len(kpoints) + + labels = KpointsDataNode.labels + + labels_dict = None + if labels is not None: + labels_dict = dict(labels) + + try: + KpointsDataNode.get_kpoints_mesh() + kpoint_type = 'mesh' + except AttributeError: + kpoint_type = 'path' + + if schema_dict.inp_version <= (0, 31): + xmltree = set_kpointlist(xmltree, schema_dict, kpoints, weights) + else: + xmltree = set_kpointlist(xmltree, + schema_dict, + kpoints, + weights, + special_labels=labels_dict, + kpoint_type=kpoint_type, + switch=switch) + + return xmltree + + +FLEURINPMODIFIER_EXTRA_FUNCS = {'schema_dict': {'set_kpointsdata': set_kpointsdata_f}} diff --git a/aiida_fleur/tools/xml_util.py b/aiida_fleur/tools/xml_util.py deleted file mode 100644 index 1c54d8ed5..000000000 --- a/aiida_fleur/tools/xml_util.py +++ /dev/null @@ -1,1971 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################### -# Copyright (c), Forschungszentrum Jülich GmbH, IAS-1/PGI-1, Germany. # -# All rights reserved. # -# This file is part of the AiiDA-FLEUR package. # -# # -# The code is hosted on GitHub at https://github.com/JuDFTteam/aiida-fleur # -# For further information on the license, see the LICENSE.txt file # -# For further information please visit http://www.flapw.de or # -# http://aiida-fleur.readthedocs.io/en/develop/ # -############################################################################### -""" -In this module contains useful methods for handling xml trees and files which are used -by the Fleur code and the fleur plugin. -""" -# TODO FEHLER meldungen, currently if a xpath expression is valid, but does not exists -# xpath returns []. Do we want this behavior? -# TODO finish implementation of create=False -# TODO: no aiida imports - -from __future__ import absolute_import -from __future__ import print_function -from lxml import etree -import six - -from aiida.common.exceptions import InputValidationError - - -def is_sequence(arg): - """ - Checks if arg is a sequence - """ - if isinstance(arg, str): - return False - elif hasattr(arg, '__iter__'): - return True - elif not hasattr(arg, 'strip') and hasattr(arg, '__getitem__'): - return True - else: - return False - - -##### CONVERTERS ############ - - -def convert_to_float(value_string, parser_info_out=None, suc_return=True): - """ - Tries to make a float out of a string. If it can't it logs a warning - and returns True or False if convertion worked or not. - - :param value_string: a string - :returns value: the new float or value_string: the string given - :returns: True or False - """ - if parser_info_out is None: - parser_info_out = {'parser_warnings': []} - try: - value = float(value_string) - except TypeError: - parser_info_out['parser_warnings'].append('Could not convert: "{}" to float, TypeError' ''.format(value_string)) - if suc_return: - return value_string, False - else: - return value_string - except ValueError: - parser_info_out['parser_warnings'].append('Could not convert: "{}" to float, ValueError' - ''.format(value_string)) - if suc_return: - return value_string, False - else: - return value_string - if suc_return: - return value, True - else: - return value - - -def convert_to_int(value_string, parser_info_out=None, suc_return=True): - """ - Tries to make a int out of a string. If it can't it logs a warning - and returns True or False if convertion worked or not. - - :param value_string: a string - :returns value: the new int or value_string: the string given - :returns: True or False, if suc_return=True - """ - if parser_info_out is None: - parser_info_out = {'parser_warnings': []} - try: - value = int(value_string) - except TypeError: - parser_info_out['parser_warnings'].append('Could not convert: "{}" to int, TypeError' ''.format(value_string)) - if suc_return: - return value_string, False - else: - return value_string - except ValueError: - parser_info_out['parser_warnings'].append('Could not convert: "{}" to int, ValueError' ''.format(value_string)) - if suc_return: - return value_string, False - else: - return value_string - if suc_return: - return value, True - else: - return value - - -def convert_htr_to_ev(value, parser_info_out=None): - """ - Multiplies the value given with the Hartree factor (converts htr to eV) - """ - from aiida_fleur.common.constants import HTR_TO_EV - # htr = 27.21138602 - if parser_info_out is None: - parser_info_out = {'parser_warnings': []} - - suc = False - value_to_save, suc = convert_to_float(value, parser_info_out=parser_info_out) - if suc: - return value_to_save * HTR_TO_EV - else: - return value - - -def convert_ev_to_htr(value, parser_info_out=None): - """ - Divides the value given with the Hartree factor (converts htr to eV) - """ - from aiida_fleur.common.constants import HTR_TO_EV - # htr = 27.21138602 - if parser_info_out is None: - parser_info_out = {'parser_warnings': []} - suc = False - value_to_save, suc = convert_to_float(value, parser_info_out=parser_info_out) - if suc: - return value_to_save / HTR_TO_EV - else: - return value - - -def convert_from_fortran_bool(stringbool): - """ - Converts a string in this case ('T', 'F', or 't', 'f') to True or False - - :param stringbool: a string ('t', 'f', 'F', 'T') - - :return: boolean (either True or False) - """ - true_items = ['True', 't', 'T'] - false_items = ['False', 'f', 'F'] - if isinstance(stringbool, str): - if stringbool in false_items: - return False - if stringbool in true_items: - return True - else: - raise InputValidationError("A string: {} for a boolean was given, which is not 'True'," - " 'False', 't', 'T', 'F' or 'f'".format(stringbool)) - elif isinstance(stringbool, bool): - return stringbool # no convertion needed... - else: - raise TypeError('convert_to_fortran_bool accepts only a string or ' 'bool as argument') - - -def convert_to_fortran_bool(boolean): - """ - Converts a Boolean as string to the format defined in the input - - :param boolean: either a boolean or a string ('True', 'False', 'F', 'T') - - :return: a string (either 't' or 'f') - """ - - if isinstance(boolean, bool): - if boolean: - new_string = 'T' - return new_string - else: - new_string = 'F' - return new_string - elif isinstance(boolean, str): # basestring): - if boolean in ('True', 't', 'T'): - new_string = 'T' - return new_string - elif boolean in ('False', 'f', 'F'): - new_string = 'F' - return new_string - else: - raise InputValidationError("A string: {} for a boolean was given, which is not 'True'," - "'False', 't', 'T', 'F' or 'f'".format(boolean)) - else: - raise TypeError('convert_to_fortran_bool accepts only a string or ' - 'bool as argument, given {} '.format(boolean)) - - -def convert_to_fortran_string(string): - """ - converts some parameter strings to the format for the inpgen - :param string: some string - :returns: string in right format (extra "") - """ - new_string = '"' + string + '"' - return new_string - - -def convert_fleur_lo(loelements): - """ - Converts lo xml elements from the inp.xml file into a lo string for the inpgen - """ - # Developer hint: Be careful with using '' and "", basestring and str are not the same... - # therefore other conversion methods might fail, or the wrong format could be written. - from aiida_fleur.tools.element_econfig_list import shell_map - - lo_string = '' - for element in loelements: - lo_type = get_xml_attribute(element, 'type') - if lo_type != 'SCLO': # non standard los not supported for now - continue - l_num = get_xml_attribute(element, 'l') - n_num = get_xml_attribute(element, 'n') - l_char = shell_map.get(int(l_num), '') - lostr = '{}{}'.format(n_num, l_char) - lo_string = lo_string + ' ' + lostr - return lo_string.strip() - - -def set_dict_or_not(para_dict, key, value): - """ - setter method for a dictionary that will not set the key, value pair. - if the key is [] or None. - """ - if value == [] or value is None: - return para_dict - else: - para_dict[key] = value - return para_dict - - -####### XML SETTERS GENERAL ############## - - -def xml_set_attribv_occ(xmltree, xpathn, attributename, attribv, occ=None, create=False): - """ - Routine sets the value of an attribute in the xml file on only the places - specified in occ - - :param xmltree: an xmltree that represents inp.xml - :param xpathn: a path to the attribute - :param attributename: an attribute name - :param attribv: an attribute value which will be set - :param occ: a list of integers specifying number of occurrence to be set - :param create: if True and there is no given xpath in the FleurinpData, creates it - - Comment: Element.set will add the attribute if it does not exist, - xpath expression has to exist - example: xml_set_first_attribv(tree, '/fleurInput/calculationSetup', 'band', 'T') - xml_set_first_attribv(tree, '/fleurInput/calculationSetup', 'dos', 'F') - """ - if occ is None: - occ = [0] - - root = xmltree.getroot() - nodes = eval_xpath3(root, xpathn, create=create) - - if not isinstance(attribv, type('')): - attribv = str(attribv) - for i, node in enumerate(nodes): - if i in occ: - node.set(attributename, attribv) - if -1 in occ: # 'all' - node.set(attributename, attribv) - - -def xml_set_first_attribv(xmltree, xpathn, attributename, attribv, create=False): - """ - Routine sets the value of the first found attribute in the xml file - - :param xmltree: an xmltree that represents inp.xml - :param xpathn: a path to the attribute - :param attributename: an attribute name - :param attribv: an attribute value which will be set - :param create: if True and there is no given xpath in the FleurinpData, creates it - - :return: None, or an etree - - Comment: Element.set will add the attribute if it does not exist, - xpath expression has to exist - example: xml_set_first_attribv(tree, '/fleurInput/calculationSetup', 'band', 'T') - xml_set_first_attribv(tree, '/fleurInput/calculationSetup', 'dos', 'F') - """ - - root = xmltree.getroot() - if isinstance(attribv, type('')): - eval_xpath3(root, xpathn, create=create)[0].set(attributename, attribv) - else: - eval_xpath3(root, xpathn, create=create)[0].set(attributename, str(attribv)) - # return xmltree - # ToDO check if worked. else exception, - - -def xml_set_all_attribv(xmltree, xpathn, attributename, attribv, create=False): - """ - Routine sets the value of an attribute in the xml file on all places it occurs - - :param xmltree: an xmltree that represents inp.xml - :param xpathn: a path to the attribute - :param attributename: an attribute name - :param attribv: an attribute value which will be set - :param create: if True and there is no given xpath in the FleurinpData, creates it - - :return: None, or an etree - - Comment: Element.set will add the attribute if it does not exist, - xpath expression has to exist - example: xml_set_first_attribv(tree, '/fleurInput/atomGroups/atomGroup/force', 'relaxXYZ', 'TTF') - xml_set_first_attribv(tree, '/fleurInput/atomGroups/atomGroup/force', 'calculate', 'F') - """ - - root = xmltree.getroot() - nodes = eval_xpath3(root, xpathn, create=create) - if is_sequence(attribv): - for i, node in enumerate(nodes): - node.set(attributename, str(attribv[i])) - else: - if not isinstance(attribv, str): # type(attribv) != type(''): - attribv = str(attribv) - for node in nodes: - node.set(attributename, attribv) - - -def xml_set_text(xmltree, xpathn, text, create=False, place_index=None, tag_order=None): - """ - Routine sets the text of a tag in the xml file - - :param xmltree: an xmltree that represents inp.xml - :param xpathn: a path to the attribute - :param text: text to be set - :param create: if True and there is no given xpath in the FleurinpData, creates it - :param place_index: if create=True, defines the place where to put a created tag - :param tag_order: if create=True, defines a tag order - - example: - - xml_set_text(tree, '/fleurInput/comment', 'Test Fleur calculation for AiiDA plug-in') - - but also coordinates and Bravais Matrix!: - - xml_set_text(tree, '/fleurInput/atomGroups/atomGroup/relPos','1.20000 PI/3 5.1-MYCrazyCostant') - """ - - root = xmltree.getroot() - node = eval_xpath3(root, xpathn, create=create, place_index=place_index, tag_order=tag_order) - if node: - node[0].text = text - # return xmltree - - -def xml_set_text_occ(xmltree, xpathn, text, create=False, occ=0, place_index=None, tag_order=None): - """ - Routine sets the text of a tag in the xml file - - :param xmltree: an xmltree that represents inp.xml - :param xpathn: a path to the attribute - :param text: text to be set - :param create: if True and there is no given xpath in the FleurinpData, creates it - :param occ: an integer that sets occurrence number to be set - :param place_index: if create=True, defines the place where to put a created tag - :param tag_order: if create=True, defines a tag order - """ - - root = xmltree.getroot() - node = eval_xpath3(root, xpathn, create=create, place_index=place_index, tag_order=tag_order) - if node: - node[occ].text = text - - -def xml_set_all_text(xmltree, xpathn, text, create=False, tag_order=None): - """ - Routine sets the text of a tag in the xml file - - :param xmltree: an xmltree that represents inp.xml - :param xpathn: a path to the attribute - :param text: text to be set - :param create: if True and there is no given xpath in the FleurinpData, creates it - :param place_index: if create=True, defines the place where to put a created tag - :param tag_order: if create=True, defines a tag order - """ - root = xmltree.getroot() - nodes = eval_xpath3(root, xpathn, create=create, tag_order=tag_order) - if is_sequence(text): - for i, node in enumerate(nodes): - node.text = text[i] - else: - for node in nodes: - node.text = text - - -def create_tag(xmlnode, xpath, newelement, create=False, place_index=None, tag_order=None): - """ - This method evaluates an xpath expresion and creates tag in an xmltree under the - returned nodes. If the path does exist things will be overwritten, or created. - Per default the new element is appended to the elements, but it can also be - inserted in a certain position or after certain other tags. - - :param xmlnode: an xmltree that represents inp.xml - :param xpathn: a path where to place a new tag - :param newelement: a tag name to be created - :param create: if True and there is no given xpath in the FleurinpData, creates it - :param place_index: defines the place where to put a created tag - :param tag_order: defines a tag order - """ - import copy - newelement_name = newelement - if not etree.iselement(newelement): - try: - newelement = etree.Element(newelement) - except ValueError as v: - raise ValueError('{}. If this is a species, are you sure this species exists ' - 'in your inp.xml?'.format(v)) from v - nodes = eval_xpath3(xmlnode, xpath, create=create) - if nodes: - for node_1 in nodes: - element_to_write = copy.deepcopy(newelement) - if place_index: - if tag_order: - # behind what shall I place it - try: - place_index = tag_order.index(newelement_name) - except ValueError as exc: - raise ValueError('Did not find element name in the tag_order list') from exc - behind_tags = tag_order[:place_index] - # check if children are in the same sequence as given in tag_order - tags = [] - for child in node_1.iterchildren(): - if child.tag not in tags: - tags.append(child.tag) - prev = -1 - for name in tags: - try: - current = tag_order.index(name) - except ValueError as exc: - raise ValueError('Did not find existing tag name in the tag_order list' - ': {}'.format(name)) from exc - if current > prev: - prev = current - else: - raise ValueError('Existing order does not correspond to tag_order list') - # get all names of tag existing tags - was_set = False - for tag in reversed(behind_tags): - for child in node_1.iterchildren(tag=tag, reversed=False): - # if tagname of elements==tag: - tag_index = node_1.index(child) - try: - node_1.insert(tag_index + 1, element_to_write) - except ValueError as exc: - raise ValueError('{}. If this is a species, are' - 'you sure this species exists in your inp.xml?' - ''.format(exc)) from exc - was_set = True - break - if was_set: - break - if not was_set: # just append - try: - node_1.insert(0, element_to_write) - except ValueError as exc: - raise ValueError('{}. If this is a species, are you' - ' sure this species exists in your inp.xml?' - ''.format(exc)) from exc - # (or remove all and write them again in right order?) - else: - try: - node_1.insert(place_index, element_to_write) - except ValueError as exc: - raise ValueError('{}. If this is a species, are you sure this species ' - 'exists in your inp.xml?'.format(exc)) from exc - else: - try: - node_1.append(element_to_write) - except ValueError as exc: - raise ValueError('{}. If this is a species, are you sure this species exists' - 'in your inp.xml?'.format(exc)) from exc - return xmlnode - - -def delete_att(xmltree, xpath, attrib): - """ - Deletes an xml tag in an xmletree. - - :param xmltree: an xmltree that represents inp.xml - :param xpathn: a path to the attribute to be deleted - :param attrib: the name of an attribute - """ - root = xmltree.getroot() - nodes = eval_xpath3(root, xpath) - if nodes: - for node in nodes: - try: - del node.attrib[attrib] - except BaseException: - pass - return xmltree - - -def delete_tag(xmltree, xpath): - """ - Deletes an xml tag in an xmletree. - - :param xmltree: an xmltree that represents inp.xml - :param xpathn: a path to the tag to be deleted - """ - root = xmltree.getroot() - if root is None: # eval will fail in this case - return xmltree - - nodes = eval_xpath3(root, xpath) - if nodes: - for node in nodes: - parent = node.getparent() - parent.remove(node) - return xmltree - - -def replace_tag(xmltree, xpath, newelement): - """ - replaces a xml tag by another tag on an xmletree in place - - :param xmltree: an xmltree that represents inp.xml - :param xpathn: a path to the tag to be replaced - :param newelement: a new tag - """ - root = xmltree.getroot() - - nodes = eval_xpath3(root, xpath) - if nodes: - for node in nodes: - parent = node.getparent() - index = parent.index(node) - parent.remove(node) - parent.insert(index, newelement) - - return xmltree - - -def get_inpgen_paranode_from_xml(inpxmlfile): - """ - This routine returns an AiiDA Parameter Data type produced from the inp.xml - file, which can be used by inpgen. - - :return: ParameterData node - """ - from aiida.orm import Dict - para_dict = get_inpgen_para_from_xml(inpxmlfile) - return Dict(dict=para_dict) - - -def get_inpgen_para_from_xml(inpxmlfile, inpgen_ready=True): - """ - This routine returns an python dictionary produced from the inp.xml - file, which can be used as a calc_parameters node by inpgen. - Be aware that inpgen does not take all information that is contained in an inp.xml file - - :param inpxmlfile: and xml etree of a inp.xml file - :param inpgen_ready: Bool, return a dict which can be inputed into inpgen while setting atoms - :return new_parameters: A Dict, which will lead to the same inp.xml (in case if other defaults, - which can not be controlled by input for inpgen, were changed) - - """ - - # TODO: convert econfig - # TODO: parse kpoints, somehow count is bad (if symmetry changes), mesh is not known, path cannot be specified - - # Disclaimer: this routine needs some xpath expressions. these are hardcoded here, - # therefore maintainance might be needed, if you want to circumvent this, you have - # to get all the paths from somewhere. - - ####### - # all hardcoded xpaths used and attributes names: - # input - film_xpath = '/fleurInput/atomGroups/atomGroup/filmPos/' # check for film pos - - # atom, for each species\ - species_xpath = '/fleurInput/atomSpecies/species' - atom_id_xpath = '' # is reconstruction possible at all now? - atom_z_xpath = '@atomicNumber' - atom_rmt_xpath = 'mtSphere/@radius' - atom_dx_xpath = 'mtSphere/@logIncrement' - atom_jri_xpath = 'mtSphere/@gridPoints' - atom_lmax_xpath = 'atomicCutoffs/@lmax' - atom_lnosph_xpath = 'atomicCutoffs/@lnonsphr' - #atom_ncst_xpath = '@coreStates' - atom_econfig_xpath = 'electronConfig' # converting todo - atom_bmu_xpath = '@magMom' - atom_lo_xpath = 'lo' # converting todo - atom_element_xpath = '@element' - atom_name_xpath = '@name' - - # comp - jspins_xpath = 'calculationSetup/magnetism/@jspins' - frcor_xpath = 'calculationSetup/coreElectrons/@frcor' - ctail_xpath = 'calculationSetup/coreElectrons/@ctail' - kcrel_xpath = 'calculationSetup/coreElectrons/@kcrel' - gmax_xpath = 'calculationSetup/cutoffs/@Gmax' - gmaxxc_xpath = 'calculationSetup/cutoffs/@GmaxXC' - kmax_xpath = 'calculationSetup/cutoffs/@Kmax' - - # exco - exco_xpath = 'xcFunctional/@name' - # film - - # soc - l_soc_xpath = '//calculationSetup/soc/@l_soc' - theta_xpath = '//calculationSetup/soc/@theta' - phi_xpath = '//calculationSetup/soc/@phi' - # qss - - # kpt - - title_xpath = '/fleurInput/comment/text()' # text - - ######## - new_parameters = {} - - #print('parsing inp.xml without XMLSchema') - #tree = etree.parse(inpxmlfile) - tree = inpxmlfile - root = tree.getroot() - - # Create the cards - - # &input # most things are not needed for AiiDA here. or we ignor them for now. - # film is set by the plugin depended on the structure - # symor per default = False? to avoid input which fleur can't take - - # &comp - # attrib = get_xml_attribute( - comp_dict = {} - comp_dict = set_dict_or_not(comp_dict, 'jspins', convert_to_int(eval_xpath(root, jspins_xpath), suc_return=False)) - comp_dict = set_dict_or_not(comp_dict, 'frcor', convert_from_fortran_bool(eval_xpath(root, frcor_xpath))) - comp_dict = set_dict_or_not(comp_dict, 'ctail', convert_from_fortran_bool(eval_xpath(root, ctail_xpath))) - comp_dict = set_dict_or_not(comp_dict, 'kcrel', eval_xpath(root, kcrel_xpath)) - comp_dict = set_dict_or_not(comp_dict, 'gmax', convert_to_float(eval_xpath(root, gmax_xpath), suc_return=False)) - comp_dict = set_dict_or_not(comp_dict, 'gmaxxc', convert_to_float(eval_xpath(root, gmaxxc_xpath), suc_return=False)) - comp_dict = set_dict_or_not(comp_dict, 'kmax', convert_to_float(eval_xpath(root, kmax_xpath), suc_return=False)) - new_parameters['comp'] = comp_dict - - # &atoms - species_list = eval_xpath2(root, species_xpath) - - for i, species in enumerate(species_list): - atom_dict = {} - atoms_name = 'atom{}'.format(i) - atom_z = convert_to_int(eval_xpath(species, atom_z_xpath), suc_return=False) - atom_rmt = convert_to_float(eval_xpath(species, atom_rmt_xpath), suc_return=False) - atom_dx = convert_to_float(eval_xpath(species, atom_dx_xpath), suc_return=False) - atom_jri = convert_to_int(eval_xpath(species, atom_jri_xpath), suc_return=False) - atom_lmax = convert_to_int(eval_xpath(species, atom_lmax_xpath), suc_return=False) - atom_lnosph = convert_to_int(eval_xpath(species, atom_lnosph_xpath), suc_return=False) - #atom_ncst = convert_to_int(eval_xpath(species, atom_ncst_xpath), suc_return=False) - atom_econfig = eval_xpath(species, atom_econfig_xpath) - atom_bmu = convert_to_float(eval_xpath(species, atom_bmu_xpath), suc_return=False) - atom_lo = eval_xpath(species, atom_lo_xpath) - atom_element = eval_xpath(species, atom_element_xpath) - atom_name_2 = eval_xpath(species, atom_name_xpath) - - if not inpgen_ready: - atom_dict = set_dict_or_not(atom_dict, 'z', atom_z) - #atom_dict = set_dict_or_not(atom_dict, 'name', atom_name_2) - #atom_dict = set_dict_or_not(atom_dict, 'ncst', atom_ncst) (deprecated) - atom_dict = set_dict_or_not(atom_dict, 'rmt', atom_rmt) - atom_dict = set_dict_or_not(atom_dict, 'dx', atom_dx) - atom_dict = set_dict_or_not(atom_dict, 'jri', atom_jri) - atom_dict = set_dict_or_not(atom_dict, 'lmax', atom_lmax) - atom_dict = set_dict_or_not(atom_dict, 'lnonsph', atom_lnosph) - - atom_dict = set_dict_or_not(atom_dict, 'econfig', atom_econfig) - atom_dict = set_dict_or_not(atom_dict, 'bmu', atom_bmu) - if atom_lo is not None: - atom_dict = set_dict_or_not(atom_dict, 'lo', convert_fleur_lo(atom_lo)) - atom_dict = set_dict_or_not(atom_dict, 'element', '{}'.format(atom_element)) - - new_parameters[atoms_name] = atom_dict - - # &soc - attrib = convert_from_fortran_bool(eval_xpath(root, l_soc_xpath)) - theta = convert_to_float(eval_xpath(root, theta_xpath), suc_return=False) - phi = convert_to_float(eval_xpath(root, phi_xpath), suc_return=False) - if attrib: - new_parameters['soc'] = {'theta': theta, 'phi': phi} - - # &kpt - #attrib = convert_from_fortran_bool(eval_xpath(root, l_soc_xpath)) - #theta = eval_xpath(root, theta_xpath) - #phi = eval_xpath(root, phi_xpath) - # if kpt: - # new_parameters['kpt'] = {'theta' : theta, 'phi' : phi} - # # ['nkpt', 'kpts', 'div1', 'div2', 'div3', 'tkb', 'tria'], - - # title - title = eval_xpath(root, title_xpath) # text - if title: - new_parameters['title'] = title.replace('\n', '').strip() - - # &exco - #TODO, easy - exco_dict = {} - exco_dict = set_dict_or_not(exco_dict, 'xctyp', eval_xpath(root, exco_xpath)) - # 'exco' : ['xctyp', 'relxc'], - new_parameters['exco'] = exco_dict - # &film - # TODO - - # &qss - # TODO - - # lattice, not supported? - - return new_parameters - - -####### XML SETTERS SPECIAL ######## - - -def set_species_label(fleurinp_tree_copy, at_label, attributedict, create=False): - """ - This method calls :func:`~aiida_fleur.tools.xml_util.set_species()` - method for a certain atom specie that corresponds to an atom with a given label - - :param fleurinp_tree_copy: xml etree of the inp.xml - :param at_label: string, a label of the atom which specie will be changed. 'all' to change all the species - :param attributedict: a python dict specifying what you want to change. - :param create: bool, if species does not exist create it and all subtags? - """ - - if at_label == 'all': - fleurinp_tree_copy = set_species(fleurinp_tree_copy, 'all', attributedict, create) - return fleurinp_tree_copy - - specie = '' - at_label = '{: >20}'.format(at_label) - all_groups = eval_xpath2(fleurinp_tree_copy, '/fleurInput/atomGroups/atomGroup') - - species_to_set = [] - - # set all species, where given label is present - for group in all_groups: - positions = eval_xpath2(group, 'filmPos') - if not positions: - positions = eval_xpath2(group, 'relPos') - for atom in positions: - atom_label = get_xml_attribute(atom, 'label') - if atom_label == at_label: - species_to_set.append(get_xml_attribute(group, 'species')) - - species_to_set = list(set(species_to_set)) - - for specie in species_to_set: - fleurinp_tree_copy = set_species(fleurinp_tree_copy, specie, attributedict, create) - - return fleurinp_tree_copy - - -def set_species(fleurinp_tree_copy, species_name, attributedict, create=False): - """ - Method to set parameters of a species tag of the fleur inp.xml file. - - :param fleurinp_tree_copy: xml etree of the inp.xml - :param species_name: string, name of the specie you want to change - :param attributedict: a python dict specifying what you want to change. - :param create: bool, if species does not exist create it and all subtags? - - :raises ValueError: if species name is non existent in inp.xml and should not be created. - also if other given tags are garbage. (errors from eval_xpath() methods) - - :return fleurinp_tree_copy: xml etree of the new inp.xml - - **attributedict** is a python dictionary containing dictionaries that specify attributes - to be set inside the certain specie. For example, if one wants to set a MT radius it - can be done via:: - - attributedict = {'mtSphere' : {'radius' : 2.2}} - - Another example:: - - 'attributedict': {'special': {'socscale': 0.0}} - - that switches SOC terms on a sertain specie. ``mtSphere``, ``atomicCutoffs``, - ``energyParameters``, ``lo``, ``electronConfig``, ``nocoParams``, ``ldaU`` and - ``special`` keys are supported. To find possible - keys of the inner dictionary please refer to the FLEUR documentation flapw.de - """ - # TODO lowercase everything - # TODO make a general specifier for species, not only the name i.e. also - # number, other parameters - if species_name == 'all': - xpath_species = '/fleurInput/atomSpecies/species' - elif species_name[:4] == 'all-': #format all- - xpath_species = '/fleurInput/atomSpecies/species[contains(@name,"{}")]'.format(species_name[4:]) - else: - xpath_species = '/fleurInput/atomSpecies/species[@name = "{}"]'.format(species_name) - - xpath_mt = '{}/mtSphere'.format(xpath_species) - xpath_atomic_cutoffs = '{}/atomicCutoffs'.format(xpath_species) - xpath_energy_parameters = '{}/energyParameters'.format(xpath_species) - xpath_lo = '{}/lo'.format(xpath_species) - xpath_electron_config = '{}/electronConfig'.format(xpath_species) - xpath_core_occ = '{}/electronConfig/stateOccupation'.format(xpath_species) - xpath_lda_u = '{}/ldaU'.format(xpath_species) - xpath_soc_scale = '{}/special'.format(xpath_species) - - # can we get this out of schema file? - species_seq = [ - 'mtSphere', 'atomicCutoffs', 'energyParameters', 'prodBasis', 'special', 'force', 'electronConfig', - 'nocoParams', 'ldaU', 'lo' - ] - - for key, val in six.iteritems(attributedict): - if key == 'mtSphere': # always in inp.xml - for attrib, value in six.iteritems(val): - xml_set_all_attribv(fleurinp_tree_copy, xpath_mt, attrib, value) - elif key == 'atomicCutoffs': # always in inp.xml - for attrib, value in six.iteritems(val): - xml_set_all_attribv(fleurinp_tree_copy, xpath_atomic_cutoffs, attrib, value) - elif key == 'energyParameters': # always in inp.xml - for attrib, value in six.iteritems(val): - xml_set_all_attribv(fleurinp_tree_copy, xpath_energy_parameters, attrib, value) - elif key == 'lo': # optional in inp.xml - # policy: we DELETE all LOs, and create new ones from the given parameters. - existinglos = eval_xpath3(fleurinp_tree_copy, xpath_lo) - for los in existinglos: - parent = los.getparent() - parent.remove(los) - - # there can be multible LO tags, so I expect either one or a list - if isinstance(val, dict): - create_tag(fleurinp_tree_copy, - xpath_species, - 'lo', - place_index=species_seq.index('lo'), - tag_order=species_seq) - for attrib, value in six.iteritems(val): - xml_set_all_attribv(fleurinp_tree_copy, xpath_lo, attrib, value, create=True) - else: # I expect a list of dicts - # lonodes = eval_xpath3(root, xpathlo)#, create=True, place_index=species_seq.index('lo'), tag_order=species_seq) - #nlonodes = len(lonodes) - # ggf create more lo tags of needed - los_need = len(val) # - nlonodes - for j in range(0, los_need): - create_tag(fleurinp_tree_copy, - xpath_species, - 'lo', - place_index=species_seq.index('lo'), - tag_order=species_seq) - for i, lodict in enumerate(val): - for attrib, value in six.iteritems(lodict): - sets = [] - for k in range(len(eval_xpath2(fleurinp_tree_copy, xpath_species + '/lo')) // los_need): - sets.append(k * los_need + i) - xml_set_attribv_occ(fleurinp_tree_copy, xpath_lo, attrib, value, occ=sets) - - elif key == 'electronConfig': - # eval electronConfig and ggf create tag at right place. - eval_xpath3(fleurinp_tree_copy, - xpath_electron_config, - create=True, - place_index=species_seq.index('electronConfig'), - tag_order=species_seq) - - for tag in ['coreConfig', 'valenceConfig', 'stateOccupation']: - for etag, edictlist in six.iteritems(val): - if not etag == tag: - continue - if etag == 'stateOccupation': # there can be multiple times stateOccupation - # policy: default we DELETE all existing occs and create new ones for the - # given input! - existingocc = eval_xpath3(fleurinp_tree_copy, xpath_core_occ) - for occ in existingocc: - parent = occ.getparent() - parent.remove(occ) - if isinstance(edictlist, dict): - for attrib, value in six.iteritems(edictlist): - xml_set_all_attribv(fleurinp_tree_copy, xpath_core_occ, attrib, value, create=True) - else: # I expect a list of dicts - nodes_need = len(edictlist) - for j in range(0, nodes_need): - create_tag(fleurinp_tree_copy, xpath_electron_config, 'stateOccupation', create=True) - for i, occdict in enumerate(edictlist): - # override them one after one - sets = [] - for k in range(len(eval_xpath2(fleurinp_tree_copy, xpath_core_occ)) // nodes_need): - sets.append(k * nodes_need + i) - for attrib, value in six.iteritems(occdict): - xml_set_attribv_occ(fleurinp_tree_copy, xpath_core_occ, attrib, value, occ=sets) - - else: - xpathconfig = xpath_electron_config + '/{}'.format(etag) - xml_set_all_text(fleurinp_tree_copy, - xpathconfig, - edictlist, - create=create, - tag_order=['coreConfig', 'valenceConfig', 'stateOccupation']) - elif key == 'ldaU': - #Same policy as los: delete existing ldaU and add the ldaU specified - existingldaus = eval_xpath3(fleurinp_tree_copy, xpath_lda_u) - for ldau in existingldaus: - parent = ldau.getparent() - parent.remove(ldau) - - if isinstance(val, dict): - create_tag(fleurinp_tree_copy, - xpath_species, - 'ldaU', - place_index=species_seq.index('ldaU'), - tag_order=species_seq) - for attrib, value in six.iteritems(val): - xml_set_all_attribv(fleurinp_tree_copy, xpath_lda_u, attrib, value, create=True) - else: #list of dicts - - ldaus_needed = len(val) - for j in range(0, ldaus_needed): - create_tag(fleurinp_tree_copy, - xpath_species, - 'ldaU', - place_index=species_seq.index('ldaU'), - tag_order=species_seq) - for i, ldaudict in enumerate(val): - for attrib, value in six.iteritems(ldaudict): - sets = [] - for k in range(len(eval_xpath2(fleurinp_tree_copy, xpath_species + '/ldaU')) // ldaus_needed): - sets.append(k * ldaus_needed + i) - xml_set_attribv_occ(fleurinp_tree_copy, xpath_lda_u, attrib, value, occ=sets) - - elif key == 'special': - eval_xpath3(fleurinp_tree_copy, - xpath_soc_scale, - create=True, - place_index=species_seq.index('special'), - tag_order=species_seq) - for attrib, value in six.iteritems(val): - xml_set_all_attribv(fleurinp_tree_copy, xpath_soc_scale, attrib, value, create=create) - else: - xml_set_all_attribv(fleurinp_tree_copy, xpath_species, key, val) - - return fleurinp_tree_copy - - -def shift_value_species_label(fleurinp_tree_copy, at_label, attr_name, value_given, mode='abs'): - """ - Shifts value of a specie by label - if at_label contains 'all' then applies to all species - - :param fleurinp_tree_copy: xml etree of the inp.xml - :param at_label: string, a label of the atom which specie will be changed. 'all' if set up all species - :param attr_name: name of the attribute to change - :param value_given: value to add or to multiply by - :param mode: 'rel' for multiplication or 'abs' for addition - """ - import numpy as np - specie = '' - if at_label != 'all': - at_label = '{: >20}'.format(at_label) - all_groups = eval_xpath2(fleurinp_tree_copy, '/fleurInput/atomGroups/atomGroup') - - species_to_set = [] - - for group in all_groups: - positions = eval_xpath2(group, 'filmPos') - if not positions: - positions = eval_xpath2(group, 'relPos') - for atom in positions: - atom_label = get_xml_attribute(atom, 'label') - if at_label in ['all', atom_label]: - species_to_set.append(get_xml_attribute(group, 'species')) - - species_to_set = list(set(species_to_set)) - - for specie in species_to_set: - - xpath_species = '/fleurInput/atomSpecies/species[@name = "{}"]'.format(specie) - - xpath_mt = '{}/mtSphere'.format(xpath_species) - xpath_atomic_cutoffs = '{}/atomicCutoffs'.format(xpath_species) - xpath_energy_parameters = '{}/energyParameters'.format(xpath_species) - xpath_final = 'initialise' - - if attr_name in ['radius', 'gridPoints', 'logIncrement']: - xpath_final = xpath_mt - elif attr_name in ['lmax', 'lnonsphr']: - xpath_final = xpath_atomic_cutoffs - elif attr_name in ['s', 'p', 'd', 'f']: - xpath_final = xpath_energy_parameters - - old_val = np.array(eval_xpath2(fleurinp_tree_copy, '/@'.join([xpath_final, attr_name]))) - - if old_val.size == 0: - print('Can not find {} attribute in the inp.xml, skip it'.format(attr_name)) - else: - old_val = old_val.astype('float') - - if mode == 'rel': - value = value_given * old_val - elif mode == 'abs': - value = value_given + old_val - else: - raise ValueError("Mode should be 'res' or 'abs' only") - - if attr_name in ['radius', 'logIncrement']: - value_to_write = value - else: - if not np.all(value == value.astype('int')): - raise ValueError('You are trying to write a float to an integer attribute') - value_to_write = value.astype('int') - - xml_set_all_attribv(fleurinp_tree_copy, xpath_final, attr_name, value_to_write) - - return fleurinp_tree_copy - - -def change_atomgr_att_label(fleurinp_tree_copy, attributedict, at_label): - """ - This method calls :func:`~aiida_fleur.tools.xml_util.change_atomgr_att()` - method for a certain atom specie that corresponds to an atom with a given label. - - :param fleurinp_tree_copy: xml etree of the inp.xml - :param at_label: string, a label of the atom which specie will be changed. 'all' to change all the species - :param attributedict: a python dict specifying what you want to change. - - :return fleurinp_tree_copy: xml etree of the new inp.xml - - **attributedict** is a python dictionary containing dictionaries that specify attributes - to be set inside the certain specie. For example, if one wants to set a beta noco parameter it - can be done via:: - - 'attributedict': {'nocoParams': [('beta', val)]} - - ``force`` and ``nocoParams`` keys are supported. - To find possible keys of the inner dictionary please refer to the FLEUR documentation flapw.de - """ - - if at_label == 'all': - fleurinp_tree_copy = change_atomgr_att(fleurinp_tree_copy, attributedict, position=None, species='all') - return fleurinp_tree_copy - - specie = '' - at_label = '{: >20}'.format(at_label) - all_groups = eval_xpath2(fleurinp_tree_copy, '/fleurInput/atomGroups/atomGroup') - - species_to_set = [] - - for group in all_groups: - positions = eval_xpath2(group, 'filmPos') - if not positions: - positions = eval_xpath2(group, 'relPos') - for atom in positions: - atom_label = get_xml_attribute(atom, 'label') - if atom_label == at_label: - species_to_set.append(get_xml_attribute(group, 'species')) - - species_to_set = list(set(species_to_set)) - - for specie in species_to_set: - fleurinp_tree_copy = change_atomgr_att(fleurinp_tree_copy, attributedict, position=None, species=specie) - - return fleurinp_tree_copy - - -def change_atomgr_att(fleurinp_tree_copy, attributedict, position=None, species=None): - """ - Method to set parameters of an atom group of the fleur inp.xml file. - - :param fleurinp_tree_copy: xml etree of the inp.xml - :param attributedict: a python dict specifying what you want to change. - :param position: position of an atom group to be changed. If equals to 'all', all species will be changed - :param species: atom groups, corresponding to the given specie will be changed - :param create: bool, if species does not exist create it and all subtags? - - :return fleurinp_tree_copy: xml etree of the new inp.xml - - **attributedict** is a python dictionary containing dictionaries that specify attributes - to be set inside the certain specie. For example, if one wants to set a beta noco parameter it - can be done via:: - - 'attributedict': {'nocoParams': [('beta', val)]} - - ``force`` and ``nocoParams`` keys are supported. - To find possible keys of the inner dictionary please refer to the FLEUR documentation flapw.de - """ - xpathatmgroup = '/fleurInput/atomGroups/atomGroup' - xpathforce = '{}/force'.format(xpathatmgroup) - xpathnocoParams = '{}/nocoParams'.format(xpathatmgroup) - - if not position and not species: # not specfied what to change - return fleurinp_tree_copy - - if position: - if not position == 'all': - xpathatmgroup = '/fleurInput/atomGroups/atomGroup[{}]'.format(position) - xpathforce = '{}/force'.format(xpathatmgroup) - xpathnocoParams = '{}/nocoParams'.format(xpathatmgroup) - if species: - if not species == 'all': - xpathatmgroup = '/fleurInput/atomGroups/atomGroup[@species = "{}"]'.format(species) - xpathforce = '{}/force'.format(xpathatmgroup) - xpathnocoParams = '{}/nocoParams'.format(xpathatmgroup) - - for key, val in six.iteritems(attributedict): - if key == 'force': - for attrib, value in val: - xml_set_all_attribv(fleurinp_tree_copy, xpathforce, attrib, value) - elif key == 'nocoParams': - for attrib, value in val: - xml_set_all_attribv(fleurinp_tree_copy, xpathnocoParams, attrib, value) - else: - xml_set_all_attribv(fleurinp_tree_copy, xpathatmgroup, attrib, value) - - return fleurinp_tree_copy - - -def set_inpchanges(fleurinp_tree_copy, change_dict): - """ - Makes given changes directly in the inp.xml file. Afterwards - updates the inp.xml file representation and the current inp_userchanges - dictionary with the keys provided in the 'change_dict' dictionary. - - :param fleurinp_tree_copy: a lxml tree that represents inp.xml - :param change_dict: a python dictionary with the keys to substitute. - It works like dict.update(), adding new keys and - overwriting existing keys. - - :returns new_tree: a lxml tree with applied changes - - An example of change_dict:: - - change_dict = {'itmax' : 1, - 'l_noco': True, - 'ctail': False, - 'l_ss': True} - - A full list of supported keys in the change_dict can be found in - :py:func:`~aiida_fleur.tools.xml_util.get_inpxml_file_structure()`:: - - 'comment': '/fleurInput/comment', - 'relPos': '/fleurInput/atomGroups/atomGroup/relPos', - 'filmPos': '/fleurInput/atomGroups/atomGroup/filmPos', - 'absPos': '/fleurInput/atomGroups/atomGroup/absPos', - 'qss': '/fleurInput/calculationSetup/nocoParams/qss', - 'l_ss': '/fleurInput/calculationSetup/nocoParams', - 'row-1': '/fleurInput/cell/bulkLattice/bravaisMatrix', - 'row-2': '/fleurInput/cell/bulkLattice/bravaisMatrix', - 'row-3': '/fleurInput/cell/bulkLattice/bravaisMatrix', - 'a1': '/fleurInput/cell/filmLattice/a1', # switches once - 'dos': '/fleurInput/output', - 'band': '/fleurInput/output', - 'secvar': '/fleurInput/calculationSetup/expertModes', - 'ctail': '/fleurInput/calculationSetup/coreElectrons', - 'frcor': '/fleurInput/calculationSetup/coreElectrons', - 'l_noco': '/fleurInput/calculationSetup/magnetism', - 'l_J': '/fleurInput/calculationSetup/magnetism', - 'swsp': '/fleurInput/calculationSetup/magnetism', - 'lflip': '/fleurInput/calculationSetup/magnetism', - 'off': '/fleurInput/calculationSetup/soc', - 'spav': '/fleurInput/calculationSetup/soc', - 'l_soc': '/fleurInput/calculationSetup/soc', - 'soc66': '/fleurInput/calculationSetup/soc', - 'pot8': '/fleurInput/calculationSetup/expertModes', - 'eig66': '/fleurInput/calculationSetup/expertModes', - 'l_f': '/fleurInput/calculationSetup/geometryOptimization', - 'gamma': '/fleurInput/calculationSetup/bzIntegration/kPointMesh', - 'gauss': '', - 'tria': '', - 'invs': '', - 'zrfs': '', - 'vchk': '/fleurInput/output/checks', - 'cdinf': '/fleurInput/output/checks', - 'disp': '/fleurInput/output/checks', - 'vacdos': '/fleurInput/output', - 'integ': '/fleurInput/output/vacuumDOS', - 'star': '/fleurInput/output/vacuumDOS', - 'iplot': '/fleurInput/output/plotting', - 'score': '/fleurInput/output/plotting', - 'plplot': '/fleurInput/output/plotting', - 'slice': '/fleurInput/output', - 'pallst': '/fleurInput/output/chargeDensitySlicing', - 'form66': '/fleurInput/output/specialOutput', - 'eonly': '/fleurInput/output/specialOutput', - 'bmt': '/fleurInput/output/specialOutput', - 'relativisticCorrections': '/fleurInput/xcFunctional', - 'calculate': '/fleurInput/atomGroups/atomGroup/force', - 'flipSpin': '/fleurInput/atomSpecies/species', - 'Kmax': '/fleurInput/calculationSetup/cutoffs', - 'Gmax': '/fleurInput/calculationSetup/cutoffs', - 'GmaxXC': '/fleurInput/calculationSetup/cutoffs', - 'numbands': '/fleurInput/calculationSetup/cutoffs', - 'itmax': '/fleurInput/calculationSetup/scfLoop', - 'minDistance': '/fleurInput/calculationSetup/scfLoop', - 'maxIterBroyd': '/fleurInput/calculationSetup/scfLoop', - 'imix': '/fleurInput/calculationSetup/scfLoop', - 'alpha': '/fleurInput/calculationSetup/scfLoop', - 'spinf': '/fleurInput/calculationSetup/scfLoop', - 'kcrel': '/fleurInput/calculationSetup/coreElectrons', - 'jspins': '/fleurInput/calculationSetup/magnetism', - 'theta': '/fleurInput/calculationSetup/soc', - 'phi': '/fleurInput/calculationSetup/soc', - 'gw': '/fleurInput/calculationSetup/expertModes', - 'lpr': '/fleurInput/calculationSetup/expertModes', - 'isec1': '/fleurInput/calculationSetup/expertModes', - 'forcemix': '/fleurInput/calculationSetup/geometryOptimization', - 'forcealpha': '/fleurInput/calculationSetup/geometryOptimization', - 'force_converged': '/fleurInput/calculationSetup/geometryOptimization', - 'qfix': '/fleurInput/calculationSetup/geometryOptimization', - 'epsdisp': '/fleurInput/calculationSetup/geometryOptimization', - 'epsforce': '/fleurInput/calculationSetup/geometryOptimization', - 'valenceElectrons': '/fleurInput/calculationSetup/bzIntegration', - 'mode': '/fleurInput/calculationSetup/bzIntegration', - 'fermiSmearingEnergy': '/fleurInput/calculationSetup/bzIntegration', - 'nx': '/fleurInput/calculationSetup/bzIntegration/kPointMesh', - 'ny': '/fleurInput/calculationSetup/bzIntegration/kPointMesh', - 'nz': '/fleurInput/calculationSetup/bzIntegration/kPointMesh', - 'count': '/fleurInput/calculationSetup/kPointCount', - 'ellow': '/fleurInput/calculationSetup/energyParameterLimits', - 'elup': '/fleurInput/calculationSetup', - 'filename': '/fleurInput/cell/symmetryFile', - 'scale': '/fleurInput/cell/bulkLattice', - 'ndir': '/fleurInput/output/densityOfStates', - 'minEnergy': '/fleurInput/output/densityOfStates', - 'maxEnergy': '/fleurInput/output/densityOfStates', - 'sigma': ' /fleurInput/output/densityOfStates', - 'layers': '/fleurInput/output/vacuumDOS', - 'nstars': '/fleurInput/output/vacuumDOS', - 'locx1': '/fleurInput/output/vacuumDOS', - 'locy1': '/fleurInput/output/vacuumDOS', - 'locx2': '/fleurInput/output/vacuumDOS', - 'locy2': '/fleurInput/output/vacuumDOS', - 'nstm': '/fleurInput/output/vacuumDOS', - 'tworkf': '/fleurInput/output/vacuumDOS', - 'numkpt': '/fleurInput/output/chargeDensitySlicing', - 'minEigenval': '/fleurInput/output/chargeDensitySlicing', - 'maxEigenval': '/fleurInput/output/chargeDensitySlicing', - 'nnne': '/fleurInput/output/chargeDensitySlicing', - 'dVac': '/fleurInput/cell/filmLattice', - 'dTilda': '/fleurInput/cell/filmLattice', - 'xcFunctional': '/fleurInput/xcFunctional/name', # other_attributes_more - 'name': {'/fleurInput/constantDefinitions', '/fleurInput/xcFunctional', - '/fleurInput/atomSpecies/species'}, - 'value': '/fleurInput/constantDefinitions', - 'element': '/fleurInput/atomSpecies/species', - 'atomicNumber': '/fleurInput/atomSpecies/species', - 'coreStates': '/fleurInput/atomSpecies/species', - 'magMom': '/fleurInput/atomSpecies/species', - 'radius': '/fleurInput/atomSpecies/species/mtSphere', - 'gridPoints': '/fleurInput/atomSpecies/species/mtSphere', - 'logIncrement': '/fleurInput/atomSpecies/species/mtSphere', - 'lmax': '/fleurInput/atomSpecies/species/atomicCutoffs', - 'lnonsphr': '/fleurInput/atomSpecies/species/atomicCutoffs', - 's': '/fleurInput/atomSpecies/species/energyParameters', - 'p': '/fleurInput/atomSpecies/species/energyParameters', - 'd': '/fleurInput/atomSpecies/species/energyParameters', - 'f': '/fleurInput/atomSpecies/species/energyParameters', - 'type': '/fleurInput/atomSpecies/species/lo', - 'l': '/fleurInput/atomSpecies/species/lo', - 'n': '/fleurInput/atomSpecies/species/lo', - 'eDeriv': '/fleurInput/atomSpecies/species/lo', - 'species': '/fleurInput/atomGroups/atomGroup', - 'relaxXYZ': '/fleurInput/atomGroups/atomGroup/force' - - """ - tree = fleurinp_tree_copy - # apply changes to etree - xmlinpstructure = get_inpxml_file_structure() - new_tree = write_new_fleur_xmlinp_file(tree, change_dict, xmlinpstructure) - - return new_tree - - -def shift_value(fleurinp_tree_copy, change_dict, mode='abs'): - """ - Shifts numertical values of some tags directly in the inp.xml file. - - :param fleurinp_tree_copy: a lxml tree that represents inp.xml - :param change_dict: a python dictionary with the keys to shift. - :param mode: 'abs' if change given is absolute, 'rel' if relative - - :returns new_tree: a lxml tree with shifted values - - An example of change_dict:: - - change_dict = {'itmax' : 1, 'dVac': -0.123} - """ - xmlinpstructure = get_inpxml_file_structure() - all_attrib_xpath = xmlinpstructure[12] - float_attributes_once = xmlinpstructure[4] - int_attributes_once = xmlinpstructure[3] - - change_to_write = {} - - for key, value_given in six.iteritems(change_dict): - if key not in float_attributes_once and key not in int_attributes_once: - raise ValueError('Given attribute name either does not exist or is not floar or int') - - key_path = all_attrib_xpath[key] - - old_val = eval_xpath2(fleurinp_tree_copy, '/@'.join([key_path, key])) - - if not old_val: - print('Can not find {} attribute in the inp.xml, skip it'.format(key)) - continue - - old_val = float(old_val[0]) - - if mode == 'rel': - value = value_given * old_val - elif mode == 'abs': - value = value_given + old_val - else: - raise ValueError("Mode should be 'res' or 'abs' only") - - if key in float_attributes_once: - change_to_write[key] = value - elif key in int_attributes_once: - if not value.is_integer(): - raise ValueError('You are trying to write a float to an integer attribute') - change_to_write[key] = int(value) - - new_tree = set_inpchanges(fleurinp_tree_copy, change_to_write) - return new_tree - - -def add_num_to_att(xmltree, xpathn, attributename, set_val, mode='abs', occ=None): - """ - Routine adds something to the value of an attribute in the xml file (should be a number here) - This is a lower-level version of :func:`~aiida_fleur.tools.xml_util.shift_value()` which - allows one to specife an arbitrary xml path. - - :param: an etree a xpath from root to the attribute and the attribute value - :param xpathn: an xml path to the attribute to change - :param attributename: a name of the attribute to change - :param set_val: a value to be added/multiplied to the previous value - :param mode: 'abs' if to add set_val, 'rel' if multiply - :param occ: a list of integers specifying number of occurrence to be set - - Comment: Element.set will add the attribute if it does not exist, - xpath expression has to exist - example: add_num_to_add(tree, '/fleurInput/bzIntegration', 'valenceElectrons', '1') - add_num_to_add(tree, '/fleurInput/bzIntegration', 'valenceElectrons', '1.1', mode='rel') - """ - - if occ is None: - occ = [0] - - # get attribute, add or multiply - # set attribute - attribval_node = eval_xpath(xmltree, xpathn) - # do some checks.. - attribval = get_xml_attribute(attribval_node, attributename) - print(attribval) - if attribval: - if mode == 'abs': - newattribv = float(attribval) + float(set_val) - elif mode == 'rel': - newattribv = float(attribval) * float(set_val) - else: - pass - # unknown mode - - xml_set_attribv_occ(xmltree, xpathn, attributename, newattribv, occ=[0], create=False) - else: - pass - # something was wrong, ... - return xmltree - - -def set_nkpts(fleurinp_tree_copy, count, gamma): - """ - Sets a k-point mesh directly into inp.xml - - :param fleurinp_tree_copy: a lxml tree that represents inp.xml - :param count: number of k-points - :param gamma: a fortran-type boolean that controls if the gamma-point should be included - in the k-point mesh - - :returns new_tree: a lxml tree with applied changes - """ - - kpointlist_xpath = '/fleurInput/calculationSetup/bzIntegration/kPointList' - #kpoint_xpath = '/fleurInput/calculationSetup/bzIntegration/kPoint*' - - tree = fleurinp_tree_copy - new_kpo = etree.Element('kPointCount', count='{}'.format(count), gamma='{}'.format(gamma)) - new_tree = replace_tag(tree, kpointlist_xpath, new_kpo) - - return new_tree - - -def set_kpath(fleurinp_tree_copy, kpath, count, gamma): - """ - Sets a k-path directly into inp.xml - - :param fleurinp_tree_copy: a lxml tree that represents inp.xml - :param kpath: a dictionary with kpoint name as key and k point coordinate as value - :param count: number of k-points - :param gamma: a fortran-type boolean that controls if the gamma-point should be included - in the k-point mesh - - :returns new_tree: a lxml tree with applied changes - """ - - kpointlist_xpath = '/fleurInput/calculationSetup/bzIntegration/altKPointSet/kPointCount' - #kpoint_xpath = '/fleurInput/calculationSetup/bzIntegration/kPoint*' - - tree = fleurinp_tree_copy - new_kpo = etree.Element('kPointCount', count='{}'.format(count), gamma='{}'.format(gamma)) - for key in kpath: - new_k = etree.Element('specialPoint', name='{}'.format(key)) - new_k.text = '{} {} {}'.format(kpath[key][0], kpath[key][1], kpath[key][2]) - new_kpo.append(new_k) - - new_tree = replace_tag(tree, kpointlist_xpath, new_kpo) - - return new_tree - - -####### XML GETTERS ######### -# TODO parser infos do not really work, might need to be returned, here - - -def eval_xpath(node, xpath, parser_info=None): - """ - Tries to evalutate an xpath expression. If it fails it logs it. - If seferal paths are found, return a list. If only one - returns the value. - - :param root node of an etree and an xpath expression (relative, or absolute) - :returns either nodes, or attributes, or text - """ - if parser_info is None: - parser_info = {'parser_warnings': []} - try: - return_value = node.xpath(xpath) - except etree.XPathEvalError: - parser_info['parser_warnings'].append('There was a XpathEvalError on the xpath: {} \n' - 'Either it does not exist, or something is wrong' - ' with the expression.'.format(xpath)) - # TODO maybe raise an error again to catch in upper routine, to know where exactly - return [] - if len(return_value) == 1: - return return_value[0] - else: - return return_value - - -def eval_xpath2(node, xpath, parser_info=None): - """ - Tries to evalutate an xpath expression. If it fails it logs it. - Always return a list. - - :param root node of an etree and an xpath expression (relative, or absolute) - :returns a node list - """ - if parser_info is None: - parser_info = {'parser_warnings': []} - try: - return_value = node.xpath(xpath) - except etree.XPathEvalError: - parser_info['parser_warnings'].append('There was a XpathEvalError on the xpath: {} \n' - 'Either it does not exist, or something is wrong' - 'with the expression.'.format(xpath)) - # TODO maybe raise an error again to catch in upper routine, to know where exactly - return [] - return return_value - - -def eval_xpath3(node, xpath, create=False, place_index=None, tag_order=None): - """ - Tries to evalutate an xpath expression. If it fails it logs it. - If create == True, creates a tag - - :param root node of an etree and an xpath expression (relative, or absolute) - :returns always a node list - """ - try: - return_value = node.xpath(xpath) - except etree.XPathEvalError as exc: - message = ('There was a XpathEvalError on the xpath: {} \n Either it does ' - 'not exist, or something is wrong with the expression.' - ''.format(xpath)) - raise etree.XPathEvalError(message) from exc - - if return_value == []: - if create: - x_pieces = [e for e in xpath.split('/') if e != ''] - #x_pieces = xpath.split('/') - xpathn = '' - for piece in x_pieces[:-1]: - xpathn = xpathn + '/' + piece - # this is REKURSIV! since create tag calls eval_xpath3 - create_tag(node, xpathn, x_pieces[-1], create=create, place_index=place_index, tag_order=tag_order) - return_value = node.xpath(xpath) - return return_value - else: - return return_value - else: - return return_value - - -def get_xml_attribute(node, attributename, parser_info_out=None): - """ - Get an attribute value from a node. - - :params node: a node from etree - :params attributename: a string with the attribute name. - :returns: either attributevalue, or None - """ - if parser_info_out is None: - parser_info_out = {'parser_warnings': []} - - if etree.iselement(node): - attrib_value = node.get(attributename) - if attrib_value: - return attrib_value - else: - if parser_info_out: - parser_info_out['parser_warnings'].append('Tried to get attribute: "{}" from element {}.\n ' - 'I recieved "{}", maybe the attribute does not exist' - ''.format(attributename, node, attrib_value)) - else: - print(('Can not get attributename: "{}" from node "{}", ' - 'because node is not an element of etree.' - ''.format(attributename, node))) - return None - else: # something doesn't work here, some nodes get through here - if parser_info_out: - parser_info_out['parser_warnings'].append('Can not get attributename: "{}" from node "{}", ' - 'because node is not an element of etree.' - ''.format(attributename, node)) - else: - print(('Can not get attributename: "{}" from node "{}", ' - 'because node is not an element of etree.' - ''.format(attributename, node))) - return None - - -# TODO this has to be done better. be able to write tags and -# certain attributes of attributes that occur possible more then once. -# HINT: This is not really used anymore. use fleurinpmodifier -def write_new_fleur_xmlinp_file(inp_file_xmltree, fleur_change_dic, xmlinpstructure): - """ - This modifies the xml-inp file. Makes all the changes wanted by - the user or sets some default values for certain modes - - :params inp_file_xmltree: xml-tree of the xml-inp file - :params fleur_change_dic: dictionary {attrib_name : value} with all the wanted changes. - - :returns: an etree of the xml-inp file with changes. - """ - # TODO rename, name is misleaded just changes the tree. - xmltree_new = inp_file_xmltree - - pos_switch_once = xmlinpstructure[0] - pos_switch_several = xmlinpstructure[1] - pos_attrib_once = xmlinpstructure[2] - pos_float_attributes_once = xmlinpstructure[4] - pos_attrib_several = xmlinpstructure[6] - pos_int_attributes_several = xmlinpstructure[7] - pos_text = xmlinpstructure[11] - pos_xpaths = xmlinpstructure[12] - expertkey = xmlinpstructure[13] - - for key in fleur_change_dic: - if key in pos_switch_once: - # TODO: a test here if path is plausible and if exist - # ggf. create tags and key.value is 'T' or 'F' if not convert, - # if garbage, exception - # convert user input into 'fleurbool' - fleur_bool = convert_to_fortran_bool(fleur_change_dic[key]) - - xpath_set = pos_xpaths[key] - # TODO: check if something in setup is inconsitent? - xml_set_first_attribv(xmltree_new, xpath_set, key, fleur_bool) - - elif key in pos_attrib_once: - # TODO: same here, check existance and plausiblility of xpath - xpath_set = pos_xpaths[key] - if key in pos_float_attributes_once: - newfloat = '{:.10f}'.format(fleur_change_dic[key]) - xml_set_first_attribv(xmltree_new, xpath_set, key, newfloat) - elif key == 'xcFunctional': - xml_set_first_attribv(xmltree_new, xpath_set, 'name', fleur_change_dic[key]) - else: - xml_set_first_attribv(xmltree_new, xpath_set, key, fleur_change_dic[key]) - elif key in pos_text: - # can be several times, therefore check - xpath_set = pos_xpaths[key] - xml_set_text(xmltree_new, xpath_set, fleur_change_dic[key]) - else: - raise InputValidationError("You try to set the key:'{}' to : '{}', but the key is unknown" - ' to the fleur plug-in'.format(key, fleur_change_dic[key])) - return xmltree_new - - -# TODO: maybe it is possible to use the xml, schema to dict libary of the QE people. -# So far it does not seem to do what we need. -def inpxml_todict(parent, xmlstr): - """ - Recursive operation which transforms an xml etree to - python nested dictionaries and lists. - Decision to add a list is if the tag name is in the given list tag_several - - :param parent: some xmltree, or xml element - :param xmlstr: structure/layout of the xml file in xmlstr is tags_several: - a list of the tags, which should be converted to a list, not - a dictionary(because they are known to occur more often, and - want to be accessed in a list later. - - :return: a python dictionary - """ - - xmlstructure = xmlstr - pos_switch_once1 = xmlstructure[0] - pos_switch_several1 = xmlstructure[1] - int_attributes_once1 = xmlstructure[3] - float_attributes_once1 = xmlstructure[4] - string_attributes_once1 = xmlstructure[5] - int_attributes_several1 = xmlstructure[7] - float_attributes_several1 = xmlstructure[8] - string_attributes_several1 = xmlstructure[9] - tags_several1 = xmlstructure[10] - pos_text1 = xmlstructure[11] - - return_dict = {} - if list(parent.items()): - return_dict = dict(list(parent.items())) - # Now we have to convert lazy fortan style into pretty things for the Database - for key in return_dict: - if key in pos_switch_once1 or (key in pos_switch_several1): - return_dict[key] = convert_from_fortran_bool(return_dict[key]) - elif key in int_attributes_once1 or (key in int_attributes_several1): - # TODO int several - try: - return_dict[key] = int(return_dict[key]) - except ValueError: - pass - elif key in float_attributes_once1 or (key in float_attributes_several1): - # TODO pressision? - try: - return_dict[key] = float(return_dict[key]) - except ValueError: - pass - elif key in string_attributes_once1 or (key in string_attributes_several1): - # TODO What attribute shall be set? all, one or several specific onces? - return_dict[key] = str(return_dict[key]) - elif key in pos_text1: - # Text is done by below check (parent.text) - pass - else: - pass - # this key is not know to plug-in TODO maybe make this a method - # of the parser and log this as warning, or add here make a log - # list, to which you always append messages, pass them back to - # the parser, who locks it then - # raise TypeError("Parser wanted to convert the key:'{}' with - # value '{}', from the inpxml file but the key is unknown to the - # fleur plug-in".format(key, return_dict[key])) - - if parent.text: # TODO more detal, exp: relPos - # has text, but we don't want all the '\n' s and empty stings in the database - if parent.text.strip() != '': # might not be the best solution - # set text - return_dict = parent.text.strip() - - for element in parent: - if element.tag in tags_several1: - # make a list, otherwise the tag will be overwritten in the dict - if element.tag not in return_dict: # is this the first occurence? - # create a list - return_dict[element.tag] = [] - return_dict[element.tag].append(inpxml_todict(element, xmlstructure)) - else: # occured before, a list already exists, therefore just add - return_dict[element.tag].append(inpxml_todict(element, xmlstructure)) - else: - # make dict - return_dict[element.tag] = inpxml_todict(element, xmlstructure) - - return return_dict - - -# This is probably only used to represent the whole inp.xml in the database for the fleurinpData attributes -# TODO this should be replaced by something else, maybe a class. that has a method to return certain -# list of possible xpaths from a schema file, or to validate a certain xpath expression and -# to allow to get SINGLE xpaths for certain attrbiutes. -# akk: tell me where 'DOS' is -# This might not be back compatible... i.e a certain plugin version will by this design only work -# with certain schema version -def get_inpxml_file_structure(): - """ - This routine returns the structure/layout of the 'inp.xml' file. - - Basicly the plug-in should know from this routine, what things are allowed - to be set and where, i.e all attributes and their xpaths. - As a developer make sure to use this routine always of you need information - about the inp.xml file structure. - Therefore, this plug-in should be easy to adjust to other codes with xml - files as input files. Just rewrite this routine. - - For now the structure of the xmlinp file for fleur is hardcoded. - If big changes are in the 'inp.xml' file, maintain this routine. - TODO: Maybe this is better done, by reading the xml schema datei instead. - And maybe it should also work without the schema file, do we want this? - - :param Nothing: TODO xml schema - - :return all_switches_once: list of all switches ('T' or 'F') which are allowed to be set - :return all_switches_several: list of all switches ('T' or 'F') which are allowed to be set - :return other_attributes_once: list of all attributes, which occur just once (can be tested) - :return other_attributes_several: list of all attributes, which can occur more then once - :return all_text: list of all text of tags, which can be set - :return all_attrib_xpath: - dictonary (attrib, xpath), of all possible attributes - with their xpath expression for the xmp inp - - :return expertkey: - keyname (should not be in any other list), which can be - used to set anything in the file, by hand, - (for experts, and that plug-in does not need to be directly maintained if - xmlinp gets a new switch) - """ - - # All attributes (allowed to change?) - - # switches can be 'T' ot 'F' # TODO: alphabetical sorting - all_switches_once = ('dos', 'band', 'secvar', 'ctail', 'frcor', 'l_noco', 'ctail', 'swsp', 'lflip', 'off', 'spav', - 'l_soc', 'soc66', 'pot8', 'eig66', 'gamma', 'gauss', 'tria', 'invs', 'invs2', 'zrfs', 'vchk', - 'cdinf', 'disp', 'vacdos', 'integ', 'star', 'score', 'plplot', 'slice', 'pallst', 'form66', - 'eonly', 'bmt', 'relativisticCorrections', 'l_J', 'l_f', 'l_ss', 'l_linMix') - - all_switches_several = ('calculate', 'flipSpin', 'l_amf') - - int_attributes_once = ('numbands', 'itmax', 'maxIterBroyd', 'kcrel', 'jspins', 'gw', 'isec1', 'nx', 'ny', 'nz', - 'ndir', 'layers', 'nstars', 'nstm', 'iplot', 'numkpt', 'nnne', 'lpr', 'count', 'qfix') - - float_attributes_once = ('Kmax', 'Gmax', 'GmaxXC', 'alpha', 'spinf', 'minDistance', 'theta', 'phi', 'epsdisp', - 'epsforce', 'valenceElectrons', 'fermiSmearingEnergy', 'ellow', 'elup', 'scale', 'dTilda', - 'dVac', 'minEnergy', 'maxEnergy', 'sigma', 'locx1', 'locy1', 'locx2', 'locy2', 'tworkf', - 'minEigenval', 'maxEigenval', 'forcealpha', 'force_converged', 'mixParam') - - string_attributes_once = ('imix', 'mode', 'filename', 'latnam', 'spgrp', 'xcFunctional', 'fleurInputVersion', - 'species', 'forcemix') - - other_attributes_once = tuple( - list(int_attributes_once) + list(float_attributes_once) + list(string_attributes_once)) - other_attributes_once1 = ('isec1', 'Kmax', 'Gmax', 'GmaxXC', 'numbands', 'itmax', 'maxIterBroyd', 'imix', 'alpha', - 'spinf', 'minDistance', 'kcrel', 'jspins', 'theta', 'phi', 'gw', 'lpr', 'epsdisp', - 'epsforce', 'valenceElectrons', 'mode', 'gauss', 'fermiSmearingEnergy', 'nx', 'ny', 'nz', - 'ellow', 'elup', 'filename', 'scale', 'dTilda', 'dVac', 'ndir', 'minEnergy', 'maxEnergy', - 'sigma', 'layers', 'nstars', 'locx1', 'locy1', 'locx2', 'locy2', 'nstm', 'tworkf', - 'numkpt', 'minEigenval', 'maxEigenval', 'nnne') - - int_attributes_several = ('atomicNumber', 'gridPoints', 'lmax', 'lnonsphr', 's', 'p', 'd', 'f', 'l', 'n', 'eDeriv', - 'coreStates') - float_attributes_several = ('value', 'magMom', 'radius', 'logIncrement', 'U', 'J') - string_attributes_several = ('name', 'element', 'coreStates', 'type', 'relaxXYZ') - other_attributes_several = ('name', 'value', 'element', 'atomicNumber', 'coreStates', 'magMom', 'radius', - 'gridPoints', 'logIncrement', 'lmax', 'lnonsphr', 's', 'p', 'd', 'f', 'species', 'type', - 'coreStates', 'l', 'n', 'eDeriv', 'relaxXYZ') - - # when parsing the xml file to a dict, these tags should become - # list(sets, or tuples) instead of dictionaries. - tags_several = ('atomGroup', 'relPos', 'absPos', 'filmPos', 'species', 'symOp', 'kPoint', 'ldaU', 'lo', - 'stateOccupation') - - all_text = { - 'comment': 1, - 'relPos': 3, - 'filmPos': 3, - 'absPos': 3, - 'row-1': 3, - 'row-2': 3, - 'row-3': 3, - 'a1': 1, - 'qss': 3 - } - # TODO all these (without comment) are floats, or float tuples. - # Should be converted to this in the databas - # changing the Bravais matrix should rather not be allowed I guess - - # all attribute xpaths - - # text xpaths(coordinates, bravaisMatrix) - # all switches once, several, all attributes once, several - all_attrib_xpath = { # text - 'comment': '/fleurInput/comment', - 'relPos': '/fleurInput/atomGroups/atomGroup/relPos', - 'filmPos': '/fleurInput/atomGroups/atomGroup/filmPos', - 'absPos': '/fleurInput/atomGroups/atomGroup/absPos', - 'qss': '/fleurInput/calculationSetup/nocoParams/qss', - 'l_ss': '/fleurInput/calculationSetup/nocoParams', - 'row-1': '/fleurInput/cell/bulkLattice/bravaisMatrix', - 'row-2': '/fleurInput/cell/bulkLattice/bravaisMatrix', - 'row-3': '/fleurInput/cell/bulkLattice/bravaisMatrix', - 'a1': '/fleurInput/cell/filmLattice/a1', # switches once - 'dos': '/fleurInput/output', - 'band': '/fleurInput/output', - 'secvar': '/fleurInput/calculationSetup/expertModes', - 'ctail': '/fleurInput/calculationSetup/coreElectrons', - 'frcor': '/fleurInput/calculationSetup/coreElectrons', - 'l_noco': '/fleurInput/calculationSetup/magnetism', - 'l_J': '/fleurInput/calculationSetup/magnetism', - 'swsp': '/fleurInput/calculationSetup/magnetism', - 'lflip': '/fleurInput/calculationSetup/magnetism', - 'off': '/fleurInput/calculationSetup/soc', - 'spav': '/fleurInput/calculationSetup/soc', - 'l_soc': '/fleurInput/calculationSetup/soc', - 'soc66': '/fleurInput/calculationSetup/soc', - 'pot8': '/fleurInput/calculationSetup/expertModes', - 'eig66': '/fleurInput/calculationSetup/expertModes', - 'l_f': '/fleurInput/calculationSetup/geometryOptimization', - 'gamma': '/fleurInput/calculationSetup/bzIntegration/kPointMesh', - 'l_linMix': '/fleurInput/calculationSetup/ldaU', - 'mixParam': '/fleurInput/calculationSetup/ldaU', - # 'invs': '', - # 'zrfs': '', - 'vchk': '/fleurInput/output/checks', - 'cdinf': '/fleurInput/output/checks', - 'disp': '/fleurInput/output/checks', - 'vacdos': '/fleurInput/output', - 'integ': '/fleurInput/output/vacuumDOS', - 'star': '/fleurInput/output/vacuumDOS', - 'iplot': '/fleurInput/output/plotting', - 'score': '/fleurInput/output/plotting', - 'plplot': '/fleurInput/output/plotting', - 'slice': '/fleurInput/output', - 'pallst': '/fleurInput/output/chargeDensitySlicing', - 'form66': '/fleurInput/output/specialOutput', - 'eonly': '/fleurInput/output/specialOutput', - 'bmt': '/fleurInput/output/specialOutput', - 'relativisticCorrections': '/fleurInput/xcFunctional', # ALL_Switches_several - 'calculate': '/fleurInput/atomGroups/atomGroup/force', - 'flipSpin': '/fleurInput/atomSpecies/species', # other_attributes_once - 'Kmax': '/fleurInput/calculationSetup/cutoffs', - 'Gmax': '/fleurInput/calculationSetup/cutoffs', - 'GmaxXC': '/fleurInput/calculationSetup/cutoffs', - 'numbands': '/fleurInput/calculationSetup/cutoffs', - 'itmax': '/fleurInput/calculationSetup/scfLoop', - 'minDistance': '/fleurInput/calculationSetup/scfLoop', - 'maxIterBroyd': '/fleurInput/calculationSetup/scfLoop', - 'imix': '/fleurInput/calculationSetup/scfLoop', - 'alpha': '/fleurInput/calculationSetup/scfLoop', - 'spinf': '/fleurInput/calculationSetup/scfLoop', - 'kcrel': '/fleurInput/calculationSetup/coreElectrons', - 'jspins': '/fleurInput/calculationSetup/magnetism', - 'theta': '/fleurInput/calculationSetup/soc', - 'phi': '/fleurInput/calculationSetup/soc', - 'gw': '/fleurInput/calculationSetup/expertModes', - 'lpr': '/fleurInput/calculationSetup/expertModes', - 'isec1': '/fleurInput/calculationSetup/expertModes', - 'forcemix': '/fleurInput/calculationSetup/geometryOptimization', - 'forcealpha': '/fleurInput/calculationSetup/geometryOptimization', - 'force_converged': '/fleurInput/calculationSetup/geometryOptimization', - 'qfix': '/fleurInput/calculationSetup/geometryOptimization', - 'epsdisp': '/fleurInput/calculationSetup/geometryOptimization', - 'epsforce': '/fleurInput/calculationSetup/geometryOptimization', - 'valenceElectrons': '/fleurInput/calculationSetup/bzIntegration', - 'mode': '/fleurInput/calculationSetup/bzIntegration', - 'fermiSmearingEnergy': '/fleurInput/calculationSetup/bzIntegration', - 'nx': '/fleurInput/calculationSetup/bzIntegration/kPointMesh', - 'ny': '/fleurInput/calculationSetup/bzIntegration/kPointMesh', - 'nz': '/fleurInput/calculationSetup/bzIntegration/kPointMesh', - 'count': '/ fleurInput/calculationSetup/bzIntegration/kPointList', - 'ellow': '/fleurInput/calculationSetup/energyParameterLimits', - 'elup': '/fleurInput/calculationSetup/energyParameterLimits', - #'filename': '/fleurInput/cell/symmetryFile', - 'scale': '/fleurInput/cell/bulkLattice', - # 'film_scale': '/fleurInput/cell/filmLattice', - 'ndir': '/fleurInput/output/densityOfStates', - 'minEnergy': '/fleurInput/output/densityOfStates', - 'maxEnergy': '/fleurInput/output/densityOfStates', - 'sigma': ' /fleurInput/output/densityOfStates', - 'layers': '/fleurInput/output/vacuumDOS', - 'nstars': '/fleurInput/output/vacuumDOS', - 'locx1': '/fleurInput/output/vacuumDOS', - 'locy1': '/fleurInput/output/vacuumDOS', - 'locx2': '/fleurInput/output/vacuumDOS', - 'locy2': '/fleurInput/output/vacuumDOS', - 'nstm': '/fleurInput/output/vacuumDOS', - 'tworkf': '/fleurInput/output/vacuumDOS', - 'numkpt': '/fleurInput/output/chargeDensitySlicing', - 'minEigenval': '/fleurInput/output/chargeDensitySlicing', - 'maxEigenval': '/fleurInput/output/chargeDensitySlicing', - 'nnne': '/fleurInput/output/chargeDensitySlicing', - 'dVac': '/fleurInput/cell/filmLattice', - 'dTilda': '/fleurInput/cell/filmLattice', - 'xcFunctional': '/fleurInput/xcFunctional', # other_attributes_more - # 'name': {'/fleurInput/constantDefinitions', '/fleurInput/xcFunctional', - # '/fleurInput/atomSpecies/species'}, - # 'value': '/fleurInput/constantDefinitions', - 'element': '/fleurInput/atomSpecies/species', - 'atomicNumber': '/fleurInput/atomSpecies/species', - 'coreStates': '/fleurInput/atomSpecies/species', - 'magMom': '/fleurInput/atomSpecies/species', - 'radius': '/fleurInput/atomSpecies/species/mtSphere', - 'gridPoints': '/fleurInput/atomSpecies/species/mtSphere', - 'logIncrement': '/fleurInput/atomSpecies/species/mtSphere', - 'lmax': '/fleurInput/atomSpecies/species/atomicCutoffs', - 'lnonsphr': '/fleurInput/atomSpecies/species/atomicCutoffs', - 's': '/fleurInput/atomSpecies/species/energyParameters', - 'p': '/fleurInput/atomSpecies/species/energyParameters', - 'd': '/fleurInput/atomSpecies/species/energyParameters', - 'f': '/fleurInput/atomSpecies/species/energyParameters', - 'type': '/fleurInput/atomSpecies/species/lo', - 'l': '/fleurInput/atomSpecies/species/lo', - 'n': '/fleurInput/atomSpecies/species/lo', - 'eDeriv': '/fleurInput/atomSpecies/species/lo', - 'species': '/fleurInput/atomGroups/atomGroup', - 'relaxXYZ': '/fleurInput/atomGroups/atomGroup/force' - } - - all_tag_xpaths = ('/fleurInput/constantDefinitions', '/fleurInput/calculationSetup', - '/fleurInput/calculationSetup/cutoffs', '/fleurInput/calculationSetup/scfLoop', - '/fleurInput/calculationSetup/coreElectrons', '/fleurInput/calculationSetup/magnetism', - '/fleurInput/calculationSetup/soc', '/fleurInput/calculationSetup/expertModes', - '/fleurInput/calculationSetup/geometryOptimization', '/fleurInput/calculationSetup/bzIntegration', - '/fleurInput/calculationSetup/kPointMesh', '/fleurInput/cell/symmetry', - '/fleurInput/cell/bravaisMatrix', '/fleurInput/calculationSetup/nocoParams', - '/fleurInput/xcFunctional', '/fleurInput/xcFunctional/xcParams', - '/fleurInput/atomSpecies/species', '/fleurInput/atomSpecies/species/mtSphere', - '/fleurInput/atomSpecies/species/atomicCutoffs', - '/fleurInput/atomSpecies/species/energyParameters', '/fleurInput/atomSpecies/species/coreConfig', - '/fleurInput/atomSpecies/species/coreOccupation', '/fleurInput/atomGroups/atomGroup', - '/fleurInput/atomGroups/atomGroup/relPos', '/fleurInput/atomGroups/atomGroup/absPos', - '/fleurInput/atomGroups/atomGroup/filmPos', '/fleurInput/output/checks', - '/fleurInput/output/densityOfStates', '/fleurInput/output/vacuumDOS', - '/fleurInput/output/plotting', '/fleurInput/output/chargeDensitySlicing', - '/fleurInput/output/specialOutput') - - expertkey = 'other' - returnlist = (all_switches_once, all_switches_several, other_attributes_once, int_attributes_once, - float_attributes_once, string_attributes_once, other_attributes_several, int_attributes_several, - float_attributes_several, string_attributes_several, tags_several, all_text, all_attrib_xpath, - expertkey) - return returnlist - - -def clear_xml(tree): - """ - Removes comments and executes xinclude tags of an - xml tree. - - :param tree: an xml-tree which will be processes - :return cleared_tree: an xml-tree without comments and with replaced xinclude tags - """ - import copy - - cleared_tree = copy.deepcopy(tree) - - # replace XInclude parts to validate against schema - cleared_tree.xinclude() - - # get rid of xml:base attribute in the relaxation part - relax = eval_xpath(cleared_tree, '/fleurInput/relaxation') - if relax != []: - for attribute in relax.keys(): - if 'base' in attribute: - cleared_tree = delete_att(cleared_tree, '/fleurInput/relaxation', attribute) - - # remove comments from inp.xml - comments = cleared_tree.xpath('//comment()') - for comment in comments: - com_parent = comment.getparent() - com_parent.remove(comment) - - return cleared_tree diff --git a/aiida_fleur/workflows/banddos.py b/aiida_fleur/workflows/banddos.py index 2ed2f6349..a1a3d8927 100644 --- a/aiida_fleur/workflows/banddos.py +++ b/aiida_fleur/workflows/banddos.py @@ -14,27 +14,25 @@ electron bandstructure. """ # TODO alow certain kpoint path, or kpoint node, so far auto -# TODO alternative parse a structure and run scf -from __future__ import absolute_import -from __future__ import print_function -import os.path import copy -import six +from lxml import etree +from ase.dft.kpoints import bandpath +import numpy as np -from aiida.plugins import DataFactory -from aiida.orm import Code, StructureData, Dict, RemoteData -from aiida.orm import load_node, CalcJobNode +from aiida.orm import Code, Dict, RemoteData, KpointsData +from aiida.orm import load_node, CalcJobNode, FolderData from aiida.engine import WorkChain, ToContext, if_ from aiida.engine import calcfunction as cf from aiida.common.exceptions import NotExistent from aiida.common import AttributeDict +from aiida.tools.data.array.kpoints import get_explicit_kpoints_path from aiida_fleur.workflows.scf import FleurScfWorkChain from aiida_fleur.workflows.base_fleur import FleurBaseWorkChain from aiida_fleur.data.fleurinpmodifier import FleurinpModifier from aiida_fleur.tools.common_fleur_wf import get_inputs_fleur -from aiida_fleur.tools.common_fleur_wf import test_and_get_codenode, is_code -from aiida_fleur.data.fleurinp import FleurinpData +from aiida_fleur.tools.common_fleur_wf import test_and_get_codenode +from aiida_fleur.data.fleurinp import FleurinpData, get_fleurinp_from_remote_data class FleurBandDosWorkChain(WorkChain): @@ -47,7 +45,7 @@ class FleurBandDosWorkChain(WorkChain): # wf_parameters: { 'tria', 'nkpts', 'sigma', 'emin', 'emax'} # defaults : tria = True, nkpts = 800, sigma=0.005, emin= , emax = - _workflowversion = '0.3.5' + _workflowversion = '0.4.1' _default_options = { 'resources': { @@ -61,41 +59,61 @@ class FleurBandDosWorkChain(WorkChain): 'environment_variables': {} } _default_wf_para = { - 'fleur_runmax': 4, - 'kpath': 'auto', - # 'nkpts' : 800, + 'kpath': 'auto', #seek (aiida), fleur (only Max4) or string to pass to ase + 'mode': 'band', + 'klistname': None, + 'kpoints_number': None, + 'kpoints_distance': None, + 'kpoints_explicit': None, #dictionary containing a list of kpoints, weights + #and additional arguments to pass to set_kpointlist 'sigma': 0.005, 'emin': -0.50, - 'emax': 0.90 + 'emax': 0.90, + 'add_comp_para': { + 'serial': False, + 'only_even_MPI': False, + 'max_queue_nodes': 20, + 'max_queue_wallclock_sec': 86400 + }, + 'inpxml_changes': [], } @classmethod def define(cls, spec): super().define(spec) - # spec.expose_inputs(FleurScfWorkChain, namespace='scf') + spec.expose_inputs(FleurScfWorkChain, + namespace_options={ + 'required': False, + 'populate_defaults': False + }, + namespace='scf') spec.input('wf_parameters', valid_type=Dict, required=False) spec.input('fleur', valid_type=Code, required=True) - spec.input('remote', valid_type=RemoteData, required=True) + spec.input('remote', valid_type=RemoteData, required=False) spec.input('fleurinp', valid_type=FleurinpData, required=False) + spec.input('kpoints', valid_type=KpointsData, required=False) spec.input('options', valid_type=Dict, required=False) - spec.outline( - cls.start, - if_(cls.scf_needed)( - cls.converge_scf, - cls.create_new_fleurinp, - cls.run_fleur, - ).else_( - cls.create_new_fleurinp, - cls.run_fleur, - ), cls.return_results) + spec.outline(cls.start, + if_(cls.scf_needed)( + cls.converge_scf, + cls.banddos_after_scf, + ).else_( + cls.banddos_wo_scf, + ), cls.return_results) spec.output('output_banddos_wc_para', valid_type=Dict) + spec.output('last_calc_retrieved', valid_type=FolderData) + spec.exit_code(230, 'ERROR_INVALID_INPUT_PARAM', message='Invalid workchain parameters.') + spec.exit_code(231, 'ERROR_INVALID_INPUT_CONFIG', message='Invalid input configuration.') spec.exit_code(233, 'ERROR_INVALID_CODE_PROVIDED', message='Invalid code node specified, check inpgen and fleur code nodes.') - spec.exit_code(231, 'ERROR_INVALID_INPUT_CONFIG', message='Invalid input configuration.') + spec.exit_code(235, 'ERROR_CHANGING_FLEURINPUT_FAILED', message='Input file modification failed.') + spec.exit_code(236, 'ERROR_INVALID_INPUT_FILE', message="Input file was corrupted after user's modifications.") + spec.exit_code(334, 'ERROR_SCF_CALCULATION_FAILED', message='SCF calculation failed.') + spec.exit_code(335, 'ERROR_SCF_CALCULATION_NOREMOTE', message='Found no SCF calculation remote repository.') def start(self): ''' @@ -108,15 +126,12 @@ def start(self): #print("Workchain node identifiers: ")#'{}' #"".format(ProcessRegistry().current_calc_node)) - self.ctx.fleurinp_scf = None self.ctx.scf_needed = False - self.ctx.fleurinp_banddos = None - self.ctx.last_calc = None + self.ctx.banddos_calc = None self.ctx.successful = False self.ctx.info = [] self.ctx.warnings = [] self.ctx.errors = [] - self.ctx.calcs = [] inputs = self.inputs @@ -126,11 +141,9 @@ def start(self): else: wf_dict = wf_default - for key, val in six.iteritems(wf_default): + for key, val in wf_default.items(): wf_dict[key] = wf_dict.get(key, val) self.ctx.wf_dict = wf_dict - # if MPI in code name, execute parallel - self.ctx.serial = self.ctx.wf_dict.get('serial', False) defaultoptions = self._default_options if 'options' in inputs: @@ -139,72 +152,189 @@ def start(self): options = defaultoptions # extend options given by user using defaults - for key, val in six.iteritems(defaultoptions): + for key, val in defaultoptions.items(): options[key] = options.get(key, val) self.ctx.options = options - # set values, or defaults - self.ctx.max_number_runs = self.ctx.wf_dict.get('fleur_runmax', 4) - - # if 'scf' in self.inputs: - # self.ctx.scf_needed = True - # if 'remote' in self.inputs.scf: - # error = "ERROR: you gave SCF input + remote" - # self.control_end_wc(error) - # return self.exit_codes.ERROR_INVALID_INPUT_CONFIG - # if 'structure' and 'fleurinp' in self.inputs.scf: - # error = "ERROR: you gave SCF input structure and fleurinp" - # self.control_end_wc(error) - # return self.exit_codes.ERROR_INVALID_INPUT_CONFIG - # if 'structure' in self.inputs.scf: - # if 'inpgen' not in self.inputs: - # error = "ERROR: you gave SCF input structure and not inpgen" - # self.control_end_wc(error) - # return self.exit_codes.ERROR_INVALID_INPUT_CONFIG - # elif 'remote' not in self.inputs: - # error = "ERROR: you gave neither SCF input nor remote" - # self.control_end_wc(error) - # return self.exit_codes.ERROR_INVALID_INPUT_CONFIG - # else: - # self.ctx.scf_needed = False - - def create_new_fleurinp(self): + if 'fleur' in inputs: + try: + test_and_get_codenode(inputs.fleur, 'fleur.fleur', use_exceptions=True) + except ValueError: + error = 'The code you provided for FLEUR does not use the plugin fleur.fleur' + self.control_end_wc(error) + return self.exit_codes.ERROR_INVALID_CODE_PROVIDED + + if 'scf' in inputs: + self.ctx.scf_needed = True + if 'remote' in inputs: + error = 'ERROR: you gave SCF input + remote for the BandDOS calculation' + self.control_end_wc(error) + return self.exit_codes.ERROR_INVALID_INPUT_CONFIG + if 'fleurinp' in inputs: + error = 'ERROR: you gave SCF input + fleurinp for the BandDOS calculation' + self.control_end_wc(error) + return self.exit_codes.ERROR_INVALID_INPUT_CONFIG + elif 'remote' not in inputs: + error = 'ERROR: you gave neither SCF input nor remote' + self.control_end_wc(error) + return self.exit_codes.ERROR_INVALID_INPUT_CONFIG + else: + self.ctx.scf_needed = False + + if wf_dict['mode'] == 'dos' and wf_dict['kpath'] not in ('auto', 'skip'): + error = 'ERROR: you specified the DOS mode but provided a non default kpath argument' + self.control_end_wc(error) + return self.exit_codes.ERROR_INVALID_INPUT_PARAM + + if wf_dict['kpoints_number'] is not None and wf_dict['kpoints_number'] is not None: + error = 'ERROR: Only provide either the distance or number for the kpoints' + self.control_end_wc(error) + return self.exit_codes.ERROR_INVALID_INPUT_PARAM + + def change_fleurinp(self): """ create a new fleurinp from the old with certain parameters """ # TODO allow change of kpoint mesh?, tria? wf_dict = self.ctx.wf_dict - if 'fleurinp' not in self.inputs: - for i in self.inputs.remote.get_incoming(): - if isinstance(i.node, CalcJobNode): - self.ctx.fleurinp_scf = load_node(i.node.pk).get_incoming().get_node_by_label('fleurinpdata') + if self.ctx.scf_needed: + try: + fleurin = self.ctx.scf.outputs.fleurinp + except NotExistent: + error = 'Fleurinp generated in the SCF calculation is not found.' + self.control_end_wc(error) + return self.exit_codes.ERROR_SCF_CALCULATION_FAILED else: - self.ctx.fleurinp_scf = self.inputs.fleurinp + if 'fleurinp' not in self.inputs: + fleurin = get_fleurinp_from_remote_data(self.inputs.remote) + else: + fleurin = self.inputs.fleurinp # how can the user say he want to use the given kpoint mesh, ZZ nkpts : False/0 - fleurmode = FleurinpModifier(self.ctx.fleurinp_scf) + fleurmode = FleurinpModifier(fleurin) + + fchanges = wf_dict.get('inpxml_changes', []) + # apply further user dependend changes + if fchanges: + try: + fleurmode.add_task_list(fchanges) + except (ValueError, TypeError) as exc: + error = ('ERROR: Changing the inp.xml file failed. Tried to apply inpxml_changes' + f', which failed with {exc}. I abort, good luck next time!') + self.control_end_wc(error) + return self.exit_codes.ERROR_CHANGING_FLEURINPUT_FAILED + + kpath = wf_dict['kpath'] + explicit = wf_dict['kpoints_explicit'] + distance = wf_dict['kpoints_distance'] + nkpts = wf_dict['kpoints_number'] + listname = wf_dict['klistname'] + + if explicit is not None: + try: + fleurmode.set_kpointlist(**explicit) + except (ValueError, TypeError) as exc: + error = ('ERROR: Changing the inp.xml file failed. Tried to apply kpoints_explicit' + f', which failed with {exc}. I abort, good luck next time!') + self.control_end_wc(error) + return self.exit_codes.ERROR_CHANGING_FLEURINPUT_FAILED + + if listname is None: + if wf_dict.get('mode') == 'band': + listname = 'path-2' + + if nkpts is None and distance is None: + nkpts = 500 + + if 'kpoints' in self.inputs: + fleurmode.set_kpointsdata(self.inputs.kpoints, switch=True) + + if kpath == 'auto': + if fleurin.inp_version >= '0.32' and listname is not None: + fleurmode.switch_kpointset(listname) + elif isinstance(kpath, dict): + if fleurin.inp_version < '0.32': + if distance is not None: + raise ValueError('set_kpath only supports specifying the number of points for the kpoints') + fleurmode.set_kpath(kpath, nkpts) + else: + raise ValueError('set_kpath is only supported for inputs up to Max4') + elif kpath == 'seek': + #Use aiida functionality + struc = fleurin.get_structuredata() + + if distance is not None: + output = get_explicit_kpoints_path(struc, reference_distance=distance) + else: + output = get_explicit_kpoints_path(struc) + primitive_struc = output['primitive_structure'] + + #check if primitive_structure and input structure are identical: + maxdiff_cell = sum(abs(np.array(primitive_struc.cell) - np.array(struc.cell))).max() + + if maxdiff_cell > 3e-9: + self.report(f'Error in cell : {maxdiff_cell}') + self.report( + 'WARNING: The structure data from the fleurinp is not the primitive structure type, which is mandatory in some cases' + ) + + output['explicit_kpoints'].store() + + fleurmode.set_kpointsdata(output['explicit_kpoints'], switch=True) + + elif kpath == 'skip': + return + else: + #Use ase + struc = fleurin.get_structuredata() + + path = bandpath(kpath, cell=struc.cell, npoints=nkpts, density=distance) + + special_points = path.special_points + + labels = [] + for label, special_kpoint in special_points.items(): + for index, kpoint in enumerate(path.kpts): + if sum(abs(np.array(special_kpoint) - np.array(kpoint))).max() < 1e-12: + labels.append((index, label)) + labels = sorted(labels, key=lambda x: x[0]) + + kpts = KpointsData() + kpts.set_cell(struc.cell) + kpts.pbc = struc.pbc + weights = np.ones(len(path.kpts)) / len(path.kpts) + kpts.set_kpoints(kpoints=path.kpts, cartesian=False, weights=weights, labels=labels) - nkpts = wf_dict.get('nkpts', 500) - sigma = wf_dict.get('sigma', 0.005) - emin = wf_dict.get('emin', -0.30) - emax = wf_dict.get('emax', 0.80) + kpts.store() + fleurmode.set_kpointsdata(kpts, switch=True) + + sigma = wf_dict['sigma'] + emin = wf_dict['emin'] + emax = wf_dict['emax'] + + if fleurin.inp_version < '0.32': + if wf_dict.get('mode') == 'dos': + fleurmode.set_inpchanges({'ndir': -1}) if wf_dict.get('mode') == 'dos': - change_dict = {'dos': True, 'ndir': -1, 'minEnergy': emin, 'maxEnergy': emax, 'sigma': sigma} + change_dict = {'dos': True, 'minEnergy': emin, 'maxEnergy': emax, 'sigma': sigma} else: - change_dict = {'band': True, 'ndir': 0, 'minEnergy': emin, 'maxEnergy': emax, 'sigma': sigma} - + change_dict = {'band': True, 'minEnergy': emin, 'maxEnergy': emax, 'sigma': sigma} fleurmode.set_inpchanges(change_dict) - if wf_dict.get('kpath') != 'auto': - fleurmode.set_kpath(wf_dict.get('kpath'), nkpts) + try: + fleurmode.show(display=False, validate=True) + except etree.DocumentInvalid: + error = ('ERROR: input, user wanted inp.xml changes did not validate') + self.control_end_wc(error) + return self.exit_codes.ERROR_INVALID_INPUT_FILE + except ValueError as exc: + error = ('ERROR: input, user wanted inp.xml changes could not be applied.' + f'The following error was raised {exc}') + self.control_end_wc(error) + return self.exit_codes.ERROR_CHANGING_FLEURINPUT_FAILED - # if nkpts: - # fleurmode.set_nkpts(count=nkpts) - #fleurinp_new.replace_tag() - - fleurmode.show(validate=True, display=False) # needed? fleurinp_new = fleurmode.freeze() self.ctx.fleurinp_banddos = fleurinp_new @@ -218,28 +348,112 @@ def converge_scf(self): """ Converge charge density. """ - # TODO: implement - return 0 + inputs = self.get_inputs_scf() + res = self.submit(FleurScfWorkChain, **inputs) + return ToContext(scf=res) - def run_fleur(self): + def banddos_after_scf(self): """ - run a FLEUR calculation + This method submits the BandDOS calculation after the initial SCF calculation """ - self.report('INFO: run FLEUR') - # inputs = self.get_inputs_scf() + calc = self.ctx.scf + + if not calc.is_finished_ok: + message = ('The SCF calculation was not successful.') + self.control_end_wc(message) + return self.exit_codes.ERROR_SCF_CALCULATION_FAILED + + try: + outpara_node = calc.outputs.output_scf_wc_para + except NotExistent: + message = ('The SCF calculation failed, no scf output node.') + self.control_end_wc(message) + return self.exit_codes.ERROR_SCF_CALCULATION_FAILED + + outpara = outpara_node.get_dict() + + if 'total_energy' not in outpara: + message = ('Did not manage to extract float total energy from the SCF calculation.') + self.control_end_wc(message) + return self.exit_codes.ERROR_SCF_CALCULATION_FAILED + + self.report('INFO: run BandDOS calculation') + + status = self.change_fleurinp() + if status: + return status + fleurin = self.ctx.fleurinp_banddos - remote = self.inputs.remote + + # Do not copy mixing_history* files from the parent + settings = {'remove_from_remotecopy_list': ['mixing_history*']} + + # Retrieve remote folder of the reference calculation + pk_last = 0 + scf_ref_node = load_node(calc.pk) + for i in scf_ref_node.called: + if i.node_type == 'process.workflow.workchain.WorkChainNode.': + if i.process_class is FleurBaseWorkChain: + if pk_last < i.pk: + pk_last = i.pk + try: + remote = load_node(pk_last).outputs.remote_folder + except AttributeError: + message = ('Found no remote folder of the reference scf calculation.') + self.control_end_wc(message) + return self.exit_codes.ERROR_SCF_CALCULATION_NOREMOTE + + label = 'bansddos_calculation' + description = 'Bandstructure or DOS is calculated for the given structure' + code = self.inputs.fleur options = self.ctx.options.copy() + inputs_builder = get_inputs_fleur(code, + remote, + fleurin, + options, + label, + description, + settings, + add_comp_para=self.ctx.wf_dict['add_comp_para']) + future = self.submit(FleurBaseWorkChain, **inputs_builder) + return ToContext(banddos_calc=future) + + def banddos_wo_scf(self): + """ + This method submits the BandDOS calculation without a previous SCF calculation + """ + self.report('INFO: run BandDOS calculation') + + status = self.change_fleurinp() + if status: + return status + + fleurin = self.ctx.fleurinp_banddos + + # Do not copy mixing_history* files from the parent + settings = {'remove_from_remotecopy_list': ['mixing_history*']} + + # Retrieve remote folder from the inputs + remote = self.inputs.remote + label = 'bansddos_calculation' description = 'Bandstructure or DOS is calculated for the given structure' - inputs = get_inputs_fleur(code, remote, fleurin, options, label, description, serial=self.ctx.serial) - future = self.submit(FleurBaseWorkChain, **inputs) - self.ctx.calcs.append(future) + code = self.inputs.fleur + options = self.ctx.options.copy() - return ToContext(last_calc=future) + inputs_builder = get_inputs_fleur(code, + remote, + fleurin, + options, + label, + description, + settings, + add_comp_para=self.ctx.wf_dict['add_comp_para']) + future = self.submit(FleurBaseWorkChain, **inputs_builder) + return ToContext(banddos_calc=future) def get_inputs_scf(self): """ @@ -247,8 +461,6 @@ def get_inputs_scf(self): wf_param, options, calculation parameters, codes, structure """ input_scf = AttributeDict(self.exposed_inputs(FleurScfWorkChain, namespace='scf')) - input_scf.fleurinp = self.ctx.fleurinp_banddos - return input_scf def return_results(self): @@ -256,22 +468,22 @@ def return_results(self): return the results of the calculations ''' # TODO more here - self.report('Band workflow Done') - self.report('A bandstructure was calculated for fleurinpdata {} and is found under pk={}, ' - 'calculation {}'.format(self.ctx.fleurinp_scf, self.ctx.last_calc.pk, self.ctx.last_calc)) + self.report('BandDOS workflow Done') + self.report(f'A bandstructure was calculated and is found under pk={self.ctx.banddos_calc.pk}, ' + f'calculation {self.ctx.banddos_calc}') from aiida_fleur.tools.common_fleur_wf import find_last_submitted_calcjob - if self.ctx.last_calc: + if self.ctx.banddos_calc: try: - last_calc_uuid = find_last_submitted_calcjob(self.ctx.last_calc) + last_calc_uuid = find_last_submitted_calcjob(self.ctx.banddos_calc) except NotExistent: last_calc_uuid = None else: last_calc_uuid = None try: # if something failed, we still might be able to retrieve something - last_calc_out = self.ctx.last_calc.outputs.output_parameters - retrieved = self.ctx.last_calc.outputs.retrieved + last_calc_out = self.ctx.banddos_calc.outputs.output_parameters + retrieved = self.ctx.banddos_calc.outputs.retrieved last_calc_out_dict = last_calc_out.get_dict() except (NotExistent, AttributeError): last_calc_out = None @@ -280,15 +492,17 @@ def return_results(self): #check if band file exists: if not succesful = False #TODO be careful with general bands.X - # bandfilename = 'bands.1' # ['bands.1', 'bands.2', ...] + bandfiles = ['bands.1', 'bands.2', 'banddos.hdf'] - # bandfile =retrieved.open(bandfilename).name + bandfile_res = [] + if retrieved: + bandfile_res = retrieved.list_object_names() - # if os.path.isfile(bandfile): - # self.ctx.successful = True - # else: - # bandfile = None - # self.report('!NO bandstructure file was found, something went wrong!') + for name in bandfiles: + if name in bandfile_res: + self.ctx.successful = True + if not self.ctx.successful: + self.report('!NO bandstructure file was found, something went wrong!') # # get efermi from last calculation scf_results = None @@ -318,7 +532,8 @@ def return_results(self): outputnode_dict['Warnings'] = self.ctx.warnings outputnode_dict['successful'] = self.ctx.successful outputnode_dict['last_calc_uuid'] = last_calc_uuid - outputnode_dict['last_calc_pk'] = self.ctx.last_calc.pk + outputnode_dict['last_calc_pk'] = self.ctx.banddos_calc.pk + outputnode_dict['mode'] = self.ctx.wf_dict.get('mode') outputnode_dict['fermi_energy_band'] = efermi_band outputnode_dict['bandgap_band'] = bandgap_band outputnode_dict['fermi_energy_scf'] = efermi_scf @@ -327,7 +542,6 @@ def return_results(self): outputnode_dict['diff_bandgap'] = diff_bandgap outputnode_dict['bandgap_units'] = 'eV' outputnode_dict['fermi_energy_units'] = 'Htr' - # outputnode_dict['bandfile'] = bandfile outputnode_t = Dict(dict=outputnode_dict) if last_calc_out: @@ -337,8 +551,11 @@ def return_results(self): else: outdict = create_band_result_node(outpara=outputnode_t) + if retrieved: + outdict['last_calc_retrieved'] = retrieved + #TODO parse Bandstructure - for link_name, node in six.iteritems(outdict): + for link_name, node in outdict.items(): self.out(link_name, node) def control_end_wc(self, errormsg): @@ -360,7 +577,7 @@ def create_band_result_node(**kwargs): So far it is just also parsed in as argument, because so far we are to lazy to put most of the code overworked from return_results in here. """ - for key, val in six.iteritems(kwargs): + for key, val in kwargs.items(): if key == 'outpara': # should be always there outpara = val outdict = {} diff --git a/aiida_fleur/workflows/base_fleur.py b/aiida_fleur/workflows/base_fleur.py index daadc01df..73a2a72d3 100644 --- a/aiida_fleur/workflows/base_fleur.py +++ b/aiida_fleur/workflows/base_fleur.py @@ -59,11 +59,20 @@ def define(cls, spec): non_db=True, help='Calculation description.') spec.input('label', valid_type=six.string_types, required=False, non_db=True, help='Calculation label.') - spec.input('only_even_MPI', - valid_type=orm.Bool, - default=lambda: orm.Bool(False), - help='Set to true if you want to suppress odd number of MPI processes in parallelisation.' - 'This might speedup a calculation for machines having even number of sockets per node.') + spec.input( + 'add_comp_para', + valid_type=orm.Dict, + default=lambda: orm.Dict(dict={ + 'only_even_MPI': False, + 'max_queue_nodes': 20, + 'max_queue_wallclock_sec': 86400 + }), + help='Gives additional control over computational parameters' + 'only_even_MPI: set to true if you want to suppress odd number of MPI processes in parallelisation.' + 'This might speedup a calculation for machines having even number of sockets per node.' + 'max_queue_nodes: maximal number of nodes allowed on the remote machine. Used only to automatically solve some FLEUR failures.' + 'max_queue_wallclock_sec: maximal wallclock time allowed on the remote machine. Used only to automatically solve some FLEUR failures.' + ) spec.outline( cls.setup, @@ -110,7 +119,12 @@ def validate_inputs(self): 'metadata': AttributeDict() }) - self.ctx.inputs.metadata.options = self.inputs.options.get_dict() + self.ctx.max_queue_nodes = self.inputs.add_comp_para['max_queue_nodes'] + self.ctx.max_queue_wallclock_sec = self.inputs.add_comp_para['max_queue_wallclock_sec'] + + input_options = self.inputs.options.get_dict() + self.ctx.optimize_resources = input_options.pop('optimize_resources', False) + self.ctx.inputs.metadata.options = input_options if 'parent_folder' in self.inputs: self.ctx.inputs.parent_folder = self.inputs.parent_folder @@ -129,6 +143,10 @@ def validate_inputs(self): else: self.ctx.inputs.settings = {} + if not self.ctx.optimize_resources: + self.ctx.can_be_optimised = False # set this for handlers to not change resources + return + resources_input = self.ctx.inputs.metadata.options['resources'] try: self.ctx.num_machines = int(resources_input['num_machines']) @@ -165,15 +183,15 @@ def check_kpts(self): machines = self.ctx.num_machines mpi_proc = self.ctx.num_mpiprocs_per_machine omp_per_mpi = self.ctx.num_cores_per_mpiproc + only_even_MPI = self.inputs.add_comp_para['only_even_MPI'] try: - adv_nodes, adv_mpi_tasks, adv_omp_per_mpi, message = optimize_calc_options( - machines, - mpi_proc, - omp_per_mpi, - self.ctx.use_omp, - self.ctx.suggest_mpi_omp_ratio, - fleurinp, - only_even_MPI=self.inputs.only_even_MPI) + adv_nodes, adv_mpi_tasks, adv_omp_per_mpi, message = optimize_calc_options(machines, + mpi_proc, + omp_per_mpi, + self.ctx.use_omp, + self.ctx.suggest_mpi_omp_ratio, + fleurinp, + only_even_MPI=only_even_MPI) except ValueError as exc: raise Warning('Not optimal computational resources, load less than 60%') from exc @@ -183,13 +201,11 @@ def check_kpts(self): self.ctx.inputs.metadata.options['resources']['num_mpiprocs_per_machine'] = adv_mpi_tasks if self.ctx.use_omp: self.ctx.inputs.metadata.options['resources']['num_cores_per_mpiproc'] = adv_omp_per_mpi - # if self.ctx.inputs.metadata.options['environment_variables']: - # self.ctx.inputs.metadata.options['environment_variables']['OMP_NUM_THREADS'] = str( - # adv_omp_per_mpi) - # else: - # self.ctx.inputs.metadata.options['environment_variables'] = {} - # self.ctx.inputs.metadata.options['environment_variables']['OMP_NUM_THREADS'] = str( - # adv_omp_per_mpi) + if self.ctx.inputs.metadata.options['environment_variables']: + self.ctx.inputs.metadata.options['environment_variables']['OMP_NUM_THREADS'] = str(adv_omp_per_mpi) + else: + self.ctx.inputs.metadata.options['environment_variables'] = {} + self.ctx.inputs.metadata.options['environment_variables']['OMP_NUM_THREADS'] = str(adv_omp_per_mpi) @register_error_handler(FleurBaseWorkChain, 1) @@ -301,7 +317,13 @@ def _handle_not_enough_memory(self, calculation): self.ctx.is_finished = False self.report('Calculation failed due to lack of memory, I resubmit it with twice larger' ' amount of computational nodes and smaller MPI/OMP ratio') - self.ctx.num_machines = self.ctx.num_machines * 2 + + # increase number of nodes + propose_nodes = self.ctx.num_machines * 2 + if propose_nodes > self.ctx.max_queue_nodes: + propose_nodes = self.ctx.max_queue_nodes + self.ctx.num_machines = propose_nodes + self.ctx.suggest_mpi_omp_ratio = self.ctx.suggest_mpi_omp_ratio / 2 self.check_kpts() @@ -326,23 +348,47 @@ def _handle_time_limits(self, calculation): """ If calculation fails due to time limits, we simply resubmit it. """ + from aiida.common.exceptions import NotExistent if calculation.exit_status in FleurProcess.get_exit_statuses(['ERROR_TIME_LIMIT']): + # if previous calculation failed for the same reason, do not restart + try: + prev_calculation_remote = calculation.get_incoming().get_node_by_label('parent_folder') + prev_calculation_status = prev_calculation_remote.get_incoming().all()[-1].exit_status + if prev_calculation_status in FleurProcess.get_exit_statuses(['ERROR_TIME_LIMIT']): + self.ctx.is_finished = True + return ErrorHandlerReport(True, True) + except NotExistent: + pass + self.report('FleurCalculation failed due to time limits, I restart it from where it ended') - remote = calculation.get_outgoing().get_node_by_label('remote_folder') + # increase wallclock time + propose_wallclock = self.ctx.inputs.metadata.options['max_wallclock_seconds'] * 2 + if propose_wallclock > self.ctx.max_queue_wallclock_sec: + propose_wallclock = self.ctx.max_queue_wallclock_sec + self.ctx.inputs.metadata.options['max_wallclock_seconds'] = propose_wallclock - # if previous calculation failed for the same reason, do not restart - prev_calculation_status = remote.get_incoming().all()[-1].exit_status - if prev_calculation_status in FleurProcess.get_exit_statuses(['ERROR_TIME_LIMIT']): - self.ctx.is_finished = True - return ErrorHandlerReport(True, True) + # increase number of nodes + propose_nodes = self.ctx.num_machines * 2 + if propose_nodes > self.ctx.max_queue_nodes: + propose_nodes = self.ctx.max_queue_nodes + self.ctx.num_machines = propose_nodes - # however, if it is the first time, resubmit profiding inp.xml and cdn from the remote folder + remote = calculation.get_outgoing().get_node_by_label('remote_folder') + + # resubmit providing inp.xml and cdn from the remote folder self.ctx.is_finished = False - self.ctx.inputs.parent_folder = remote + if 'fleurinpdata' in self.ctx.inputs: - del self.ctx.inputs.fleurinpdata + modes = self.ctx.inputs.fleurinpdata.get_fleur_modes() + if not (modes['force_theorem'] or modes['dos'] or modes['band']): + # in modes listed above it makes no sense copying cdn.hdf + self.ctx.inputs.parent_folder = remote + del self.ctx.inputs.fleurinpdata + else: + # it is harder to extract modes in this case - simply try to reuse cdn.hdf and hope it works + self.ctx.inputs.parent_folder = remote return ErrorHandlerReport(True, True) diff --git a/aiida_fleur/workflows/corehole.py b/aiida_fleur/workflows/corehole.py index 243cb3bb1..639e6b4c8 100644 --- a/aiida_fleur/workflows/corehole.py +++ b/aiida_fleur/workflows/corehole.py @@ -22,9 +22,7 @@ # TODO corelevel workflow, rename species of 0,0,0 position in inp.xml #import os.path -from __future__ import absolute_import -from __future__ import print_function -import six + import re import numpy as np from pprint import pprint @@ -49,7 +47,7 @@ from aiida_fleur.data.fleurinp import FleurinpData -class fleur_corehole_wc(WorkChain): +class FleurCoreholeWorkChain(WorkChain): """ Turn key solution for a corehole calculation with the FLEUR code. Has different protocols for different core-hole types (valence, charge). @@ -114,7 +112,7 @@ class fleur_corehole_wc(WorkChain): # Hints: # 1. This workflow does not work with local codes! - _workflowversion = '0.4.0' + _workflowversion = '0.5.0' _default_options = { 'resources': { 'num_machines': 1, @@ -140,7 +138,12 @@ class fleur_corehole_wc(WorkChain): #'relax_para' : None, # parameter dict for the relaxation 'scf_para': None, # wf parameter dict for the scfs 'same_para': True, # enforce the same atom parameter/cutoffs on the corehole calc and ref - 'serial': True, # run fleur in serial, or parallel? + 'add_comp_para': { + 'serial': False, + 'only_even_MPI': False, + 'max_queue_nodes': 20, + 'max_queue_wallclock_sec': 86400 + }, # run fleur in serial, or parallel? #'job_limit' : 100 # enforce the workflow not to spawn more scfs wcs then this number(which is roughly the number of fleur jobs) 'magnetic': True } @@ -200,7 +203,7 @@ def check_input(self): Do some input checks. Further input checks are done in further workflow steps """ # TODO: document parameters - self.report('started fleur_corehole_wc version {} ' + self.report('started FleurCoreholeWorkChain version {} ' 'Workchain node identifiers: ' #{}" ''.format(self._workflowversion)) #, ProcessRegistry().current_calc_node)) @@ -222,7 +225,7 @@ def check_input(self): wf_dict = inputs.wf_parameters.get_dict() self.ctx.method = wf_dict.get('method', 'valence') self.ctx.joblimit = wf_dict.get('joblimit') - self.ctx.serial = wf_dict.get('serial') + self.ctx.add_comp_para = wf_dict['add_comp_para'] self.ctx.same_para = wf_dict.get('same_para') self.ctx.scf_para = wf_dict.get('scf_para', {}) self.ctx.be_to_calc = wf_dict.get('corelevel') @@ -239,7 +242,7 @@ def check_input(self): options = self.inputs.options.get_dict() else: options = defaultoptions - for key, val in six.iteritems(defaultoptions): + for key, val in defaultoptions.items(): options[key] = options.get(key, val) self.ctx.options = options @@ -314,7 +317,7 @@ def create_supercell(self): # overwrite label and description of new structure supercell_s.label = '{}x{}x{} of {}'.format(supercell_base[0], supercell_base[1], supercell_base[2], self.ctx.base_structure_relax.uuid) - supercell_s.description = supercell_s.description + ' created in a fleur_corehole_wc' + supercell_s.description = supercell_s.description + ' created in a FleurCoreholeWorkChain' self.ctx.ref_supercell = supercell_s calc_para = self.ctx.ref_para if calc_para is None: @@ -342,7 +345,8 @@ def create_coreholes(self): # TODO if this becomes to long split """ - self.report('INFO: In create_coreholes of fleur_corehole_wc. ' 'Preparing everything for calculation launches.') + self.report('INFO: In create_coreholes of FleurCoreholeWorkChain. ' + 'Preparing everything for calculation launches.') ########### init variables ############## @@ -396,7 +400,7 @@ def create_coreholes(self): # 1. Find out what atoms to put coreholes on self.report('Atoms to calculate : {}'.format(atoms_toc)) for atom_info in atoms_toc: - if isinstance(atom_info, (str, six.text_type)): #basestring): + if isinstance(atom_info, str): # , six.text_type)): #basestring): if atom_info == 'all': # add all symmetry equivivalent atoms of structure to create coreholes #coreholes_atoms = base_atoms_sites @@ -453,7 +457,7 @@ def create_coreholes(self): # 2. now check what type of corelevel shall we create on those atoms self.report('Corelevels to calculate : {}'.format(corelevels_toc)) for corel in corelevels_toc: - if isinstance(corel, (str, six.text_type)): #basestring): + if isinstance(corel, str): # , six.text_type)): #basestring): # split string (Be1s) s.replace(';',' ')... could get rid of re elm_cl = re.split('[, ;:-]', corel) #print(elm_cl) @@ -629,7 +633,7 @@ def create_coreholes(self): else: wf_parameter = para #print(wf_parameter) - wf_parameter['serial'] = self.ctx.serial + wf_parameter['add_comp_para'] = self.ctx.add_comp_para wf_parameter['inpxml_changes'] = corehole['inpxml_changes'] wf_parameters = Dict(dict=wf_parameter) @@ -669,14 +673,14 @@ def run_ref_scf(self): """ # TODO: idea instead of a list, just use a dictionary... - self.report('INFO: In run_ref_scf fleur_corehole_wc') - print('INFO: In run_ref_scf fleur_corehole_wc') + self.report('INFO: In run_ref_scf FleurCoreholeWorkChain') + print('INFO: In run_ref_scf FleurCoreholeWorkChain') para = self.ctx.scf_para if para is None: wf_parameter = {} else: wf_parameter = para - wf_parameter['serial'] = self.ctx.serial + wf_parameter['add_comp_para'] = self.ctx.add_comp_para wf_parameters = Dict(dict=wf_parameter) options = Dict(dict=self.ctx.options) ''' @@ -714,8 +718,8 @@ def run_ref_scf(self): ''' #res_all = [] calcs = {} - scf_label = 'corehole_wc ref cell' - scf_desc = '|corehole_wc|' + scf_label = 'FleurCoreholeWorkChain ref cell' + scf_desc = '|FleurCoreholeWorkChain|' i = 0 for node in self.ctx.calcs_ref_torun: # usually just 1, but we leave the default. #print node @@ -791,7 +795,7 @@ def relaxation_needed(self): If the structures should be relaxed, check if their Forces are below a certain threshold, otherwise throw them in the relaxation wf. """ - self.report('In relaxation fleur_corehole_wc') + self.report('In relaxation FleurCoreholeWorkChain') if self.ctx.relax: # TODO check all forces of calculations forces_fine = True @@ -803,7 +807,7 @@ def relax(self): """ Do structural relaxation for certain structures. """ - self.report('In relax fleur_corehole_wc workflow') + self.report('In relax FleurCoreholeWorkChain workflow') self.ctx.base_structure_relax = self.ctx.base_structure #for calc in self.ctx.dos_to_calc: # pass @@ -813,22 +817,22 @@ def run_scfs(self): """ Run a scf for the all corehole calculations in parallel super cell """ - self.report('INFO: In run_scfs fleur_corehole_wc') - print('INFO: In run_scfs fleur_corehole_wc') + self.report('INFO: In run_scfs FleurCoreholeWorkChain') + print('INFO: In run_scfs FleurCoreholeWorkChain') para = self.ctx.scf_para if para is None: wf_parameter = {} else: wf_parameter = para - wf_parameter['serial'] = self.ctx.serial + wf_parameter['add_comp_para'] = self.ctx.add_comp_para #wf_parameter['queue_name'] = self.ctx.queue #wf_parameter['custom_scheduler_commands'] = self.ctx.custom_scheduler_commands wf_parameters = Dict(dict=wf_parameter) options = Dict(dict=self.ctx.options) #res_all = [] calcs = {} - scf_label = 'corehole_wc cell' - scf_desc = '|corehole_wc|' + scf_label = 'FleurCoreholeWorkChain cell' + scf_desc = '|FleurCoreholeWorkChain|' # now in parallel #print self.ctx.ref_calcs_torun i = 0 # @@ -897,7 +901,7 @@ def collect_results(self): # TODO: what about partial collection? # if some calc failed do not abort, but collect the others. - message = ('INFO: Collecting results of fleur_corehole_wc workflow') + message = ('INFO: Collecting results of FleurCoreholeWorkChain workflow') self.report(message) all_CLS = {} @@ -1031,9 +1035,9 @@ def return_results(self): #outdict = {} #outdict['output_eos_wc_para'] = ouputnode - for k, v in six.iteritems(outdict): + for k, v in outdict.items(): self.out(k, v) - msg = ('INFO: fleur_corehole_wc workflow Done') + msg = ('INFO: FleurCoreholeWorkChain workflow Done') self.report(msg) def control_end_wc(self, errormsg): diff --git a/aiida_fleur/workflows/dmi.py b/aiida_fleur/workflows/dmi.py index 00433726e..40dcc5b3a 100644 --- a/aiida_fleur/workflows/dmi.py +++ b/aiida_fleur/workflows/dmi.py @@ -25,7 +25,7 @@ from aiida.engine import WorkChain, ToContext, if_ from aiida.engine import calcfunction as cf -from aiida.orm import Code, load_node, CalcJobNode +from aiida.orm import Code, load_node from aiida.orm import RemoteData, Dict from aiida.common import AttributeDict from aiida.common.exceptions import NotExistent @@ -36,7 +36,7 @@ from aiida_fleur.data.fleurinpmodifier import FleurinpModifier from aiida_fleur.workflows.base_fleur import FleurBaseWorkChain from aiida_fleur.common.constants import HTR_TO_EV -from aiida_fleur.data.fleurinp import FleurinpData +from aiida_fleur.data.fleurinp import FleurinpData, get_fleurinp_from_remote_data class FleurDMIWorkChain(WorkChain): @@ -59,8 +59,12 @@ class FleurDMIWorkChain(WorkChain): } _default_wf_para = { - 'serial': False, - 'only_even_MPI': False, + 'add_comp_para': { + 'serial': False, + 'only_even_MPI': False, + 'max_queue_nodes': 20, + 'max_queue_wallclock_sec': 86400 + }, 'beta': { 'all': 1.57079 }, @@ -76,7 +80,12 @@ class FleurDMIWorkChain(WorkChain): @classmethod def define(cls, spec): super().define(spec) - spec.expose_inputs(FleurScfWorkChain, namespace='scf') + spec.expose_inputs(FleurScfWorkChain, + namespace_options={ + 'required': False, + 'populate_defaults': False + }, + namespace='scf') spec.input('wf_parameters', valid_type=Dict, required=False) spec.input('fleur', valid_type=Code, required=True) spec.input('remote', valid_type=RemoteData, required=False) @@ -179,7 +188,7 @@ def start(self): return self.exit_codes.ERROR_INVALID_CODE_PROVIDED # Check if user gave an input setup making any sense - if inputs.scf: + if 'scf' in inputs: self.ctx.scf_needed = True if 'remote' in inputs: error = 'ERROR: you gave SCF input + remote for the FT' @@ -238,7 +247,9 @@ def get_inputs_scf(self): for key, val in six.iteritems(self.ctx.wf_dict.get('beta')): scf_wf_dict['inpxml_changes'].append(('set_atomgr_att_label', { 'attributedict': { - 'nocoParams': [('beta', val)] + 'nocoParams': { + 'beta': val + } }, 'atom_label': key })) @@ -275,12 +286,7 @@ def change_fleurinp(self): else: # In this case only remote is given # fleurinp data has to be generated from the remote inp.xml file - remote_node = self.inputs.remote - for link in remote_node.get_incoming().all(): - if isinstance(link.node, CalcJobNode): - parent_calc_node = link.node - retrieved_node = parent_calc_node.get_outgoing().get_node_by_label('retrieved') - fleurin = FleurinpData(files=['inp.xml'], node=retrieved_node) + fleurin = get_fleurinp_from_remote_data(self.inputs.remote) # copy inpchanges from wf parameters fchanges = self.ctx.wf_dict.get('inpxml_changes', []) @@ -319,6 +325,7 @@ def change_fleurinp(self): 'itmax': 1, 'l_noco': True, 'ctail': False, + 'spav': True, # 'l_soc': True, 'l_ss': True } @@ -328,7 +335,9 @@ def change_fleurinp(self): for key, val in six.iteritems(self.ctx.wf_dict.get('beta')): fchanges.append(('set_atomgr_att_label', { 'attributedict': { - 'nocoParams': [('beta', val)] + 'nocoParams': { + 'beta': val + } }, 'atom_label': key })) @@ -448,8 +457,7 @@ def force_after_scf(self): label, description, settings, - serial=self.ctx.wf_dict['serial'], - only_even_MPI=self.ctx.wf_dict['only_even_MPI']) + add_comp_para=self.ctx.wf_dict['add_comp_para']) future = self.submit(FleurBaseWorkChain, **inputs_builder) return ToContext(f_t=future) @@ -484,8 +492,7 @@ def force_wo_scf(self): label, description, settings, - serial=self.ctx.wf_dict['serial'], - only_even_MPI=self.ctx.wf_dict['only_even_MPI']) + add_comp_para=self.ctx.wf_dict['add_comp_para']) future = self.submit(FleurBaseWorkChain, **inputs_builder) return ToContext(f_t=future) @@ -514,13 +521,13 @@ def get_results(self): try: out_dict = calculation.outputs.output_parameters.dict - t_energydict = out_dict.dmi_force_evSum + t_energydict = out_dict.dmi_force_evsum mae_thetas = out_dict.dmi_force_theta mae_phis = out_dict.dmi_force_phi num_ang = out_dict.dmi_force_angles num_q_vectors = out_dict.dmi_force_qs q_vectors = [self.ctx.wf_dict['q_vectors'][x - 1] for x in out_dict.dmi_force_q] - e_u = out_dict.energy_units + e_u = out_dict.dmi_force_units for i in six.moves.range((num_q_vectors - 1) * (num_ang), -1, -num_ang): ref_enrg = t_energydict.pop(i) diff --git a/aiida_fleur/workflows/dos.py b/aiida_fleur/workflows/dos.py index ce6ee4229..053f67b3a 100644 --- a/aiida_fleur/workflows/dos.py +++ b/aiida_fleur/workflows/dos.py @@ -54,7 +54,19 @@ class fleur_dos_wc(WorkChain): 'import_sys_environment': False, 'environment_variables': {} } - _default_wf_para = {'tria': True, 'nkpts': 800, 'sigma': 0.005, 'emin': -0.30, 'emax': 0.80} + _default_wf_para = { + 'tria': True, + 'nkpts': 800, + 'sigma': 0.005, + 'emin': -0.30, + 'emax': 0.80, + 'add_comp_para': { + 'serial': False, + 'only_even_MPI': False, + 'max_queue_nodes': 20, + 'max_queue_wallclock_sec': 86400 + } + } @classmethod def define(cls, spec): @@ -154,7 +166,7 @@ def run_fleur(self): options = self.ctx.options - inputs = get_inputs_fleur(code, remote, fleurin, options, serial=self.ctx.serial) + inputs = get_inputs_fleur(code, remote, fleurin, options, add_comp_para=self.ctx.wf_dict['add_comp_para']) future = submit(FleurCalculation, **inputs) return ToContext(last_calc=future) # calcs.append(future), diff --git a/aiida_fleur/workflows/eos.py b/aiida_fleur/workflows/eos.py index 475742011..d009eaff6 100644 --- a/aiida_fleur/workflows/eos.py +++ b/aiida_fleur/workflows/eos.py @@ -53,9 +53,9 @@ class FleurEosWorkChain(WorkChain): about general succeed, fit results and so on. """ - _workflowversion = '0.4.0' + _workflowversion = '0.5.0' - _default_wf_para = {'points': 9, 'step': 0.002, 'guess': 1.00} + _default_wf_para = {'points': 9, 'step': 0.002, 'guess': 1.00, 'enforce_same_para': True} _default_options = FleurScfWorkChain._default_options @classmethod @@ -69,13 +69,16 @@ def define(cls, spec): spec.input('wf_parameters', valid_type=Dict, required=False) spec.input('structure', valid_type=StructureData, required=True) - spec.outline(cls.start, cls.structures, cls.converge_scf, cls.return_results) + spec.outline(cls.start, cls.structures, cls.run_first, cls.inspect_first, cls.converge_scf, cls.return_results) spec.output('output_eos_wc_para', valid_type=Dict) spec.output('output_eos_wc_structure', valid_type=StructureData) # exit codes spec.exit_code(230, 'ERROR_INVALID_INPUT_PARAM', message='Invalid workchain parameters.') + spec.exit_code(400, + 'ERROR_SUB_PROCESS_FAILED', + message='At least one of the SCF sub processes did not finish successfully.') def start(self): """ @@ -127,6 +130,7 @@ def start(self): self.ctx.guess = wf_dict.get('guess', 1.00) self.ctx.serial = wf_dict.get('serial', False) # True self.ctx.max_number_runs = wf_dict.get('fleur_runmax', 4) + self.ctx.enforce_para = wf_dict.get('enforce_same_para', True) def structures(self): """ @@ -147,19 +151,58 @@ def structures(self): # since cf this has to be a dict, we sort to assure ordering of scale self.ctx.structures = [struc_dict[key] for key in sorted(struc_dict)] + def run_first(self): + """ + Launch the first fleur SCF workchain + """ + calcs = {} + + i = 0 + struc = self.ctx.structures[i] + inputs = self.get_inputs_scf_first() + inputs.structure = struc + natoms = len(struc.sites) + label = str(self.ctx.scalelist[i]) + label_c = '|eos| fleur_scf_wc' + description = '|FleurEosWorkChain|fleur_scf_wc|scale {}, {}'.format(label, i) + + self.ctx.volume.append(struc.get_cell_volume()) + self.ctx.volume_peratom[label] = struc.get_cell_volume() / natoms + self.ctx.structures_uuids.append(struc.uuid) + + result = self.submit(FleurScfWorkChain, **inputs) + self.ctx.labels.append(label) + calcs[label] = result + + return ToContext(**calcs) + + def inspect_first(self): + """ + Check if the first calculation failed and + """ + label = self.ctx.labels[0] + first_scf = self.ctx[label] + if not first_scf.is_finished_ok: + self.report('Initial sub process did not finish successful so aborting the workchain.') + # return self.exit_codes.ERROR_SUB_PROCESS_FAILED.format(cls=self.inputs.sub_process_class) # pylint: disable=no-member + return self.exit_codes.ERROR_SUB_PROCESS_FAILED + + fleurinp = first_scf.outputs.fleurinp + self.ctx.first_calc_parameters = fleurinp.get_parameterdata() + def converge_scf(self): """ Launch fleur_scfs from the generated structures """ calcs = {} - for i, struc in enumerate(self.ctx.structures): + for i, struc in enumerate(self.ctx.structures[1:]): inputs = self.get_inputs_scf() inputs.structure = struc natoms = len(struc.sites) - label = str(self.ctx.scalelist[i]) + label = str(self.ctx.scalelist[i + 1]) label_c = '|eos| fleur_scf_wc' - description = '|FleurEosWorkChain|fleur_scf_wc|scale {}, {}'.format(label, i) + description = '|FleurEosWorkChain|fleur_scf_wc|scale {}, {}'.format(label, i + 1) #inputs.label = label_c #inputs.description = description @@ -173,12 +216,25 @@ def converge_scf(self): return ToContext(**calcs) + def get_inputs_scf_first(self): + """ + get and 'produce' the inputs for a scf-cycle + """ + input_scf = AttributeDict(self.exposed_inputs(FleurScfWorkChain, namespace='scf')) + + return input_scf + def get_inputs_scf(self): """ get and 'produce' the inputs for a scf-cycle """ input_scf = AttributeDict(self.exposed_inputs(FleurScfWorkChain, namespace='scf')) + # ensure that all are run with the same FLAPW parameters + if ('calc_parameters' not in input_scf) and self.ctx.enforce_para: + # TODO maybe merge with user given calcparameters... + input_scf['calc_parameters'] = self.ctx.first_calc_parameters + return input_scf def return_results(self): @@ -439,7 +495,7 @@ def birch_murnaghan_fit(energies, volumes): fitdata = np.polyfit(volumes[:]**(-2. / 3.), energies[:], 3, full=True) ssr = fitdata[1] sst = np.sum((energies[:] - np.average(energies[:]))**2.) - print(ssr, sst, energies) + #print(ssr, sst, energies) if sst == 0: residuals0 = -1 else: diff --git a/aiida_fleur/workflows/initial_cls.py b/aiida_fleur/workflows/initial_cls.py index b6c373da5..2738a3304 100644 --- a/aiida_fleur/workflows/initial_cls.py +++ b/aiida_fleur/workflows/initial_cls.py @@ -10,7 +10,7 @@ # http://aiida-fleur.readthedocs.io/en/develop/ # ############################################################################### """ -This is the worklfow 'initial_cls' using the Fleur code calculating +This is the worklfow FleurInitialCLSWorkChain 'initial_cls' using the Fleur code calculating corelevel shifts with different methods. """ #TODO parsing of eigenvalues of LOS! @@ -22,7 +22,6 @@ # TODO: maybe launch all scfs at the same time # TODO: gives only a warning currently if ref not found. # but should lead to error if no ref is found for what should be calculated -from __future__ import absolute_import from string import digits from aiida.engine import submit from aiida.engine import ToContext, WorkChain, if_ @@ -37,10 +36,9 @@ from aiida_fleur.workflows.scf import FleurScfWorkChain from aiida_fleur.tools.common_fleur_wf_util import get_natoms_element from aiida_fleur.data.fleurinp import FleurinpData -import six -class fleur_initial_cls_wc(WorkChain): +class FleurInitialCLSWorkChain(WorkChain): """ Turn key solution for the calculation of core level shift """ @@ -71,7 +69,7 @@ class fleur_initial_cls_wc(WorkChain): # 'relax_para' : 'default' # 'calculate_doses' : False # 'dos_para' : 'default' - _workflowversion = '0.4.0' + _workflowversion = '0.5.0' _default_wf_para = { 'references': {}, 'relax': True, @@ -79,7 +77,12 @@ class fleur_initial_cls_wc(WorkChain): 'relax_para': 'default', 'scf_para': 'default', 'same_para': True, - 'serial': False + 'add_comp_para': { + 'serial': False, + 'only_even_MPI': False, + 'max_queue_nodes': 20, + 'max_queue_wallclock_sec': 86400 + } } _default_options = { @@ -167,7 +170,7 @@ def check_input(self): wf_dict = self.inputs.wf_parameters.get_dict() default = self._default_wf_para - self.ctx.serial = wf_dict.get('serial', default.get('serial')) + self.ctx.add_comp_para = wf_dict.get('add_para_calc', default.get('add_para_calc')) self.ctx.same_para = wf_dict.get('same_para', default.get('same_para')) self.ctx.scf_para = wf_dict.get('scf_para', default.get('scf_para')) self.ctx.relax = wf_dict.get('relax', default.get('relax')) @@ -179,7 +182,7 @@ def check_input(self): options = self.inputs.options.get_dict() else: options = defaultoptions - for key, val in six.iteritems(defaultoptions): + for key, val in defaultoptions.items(): options[key] = options.get(key, val) self.ctx.options = options @@ -402,7 +405,7 @@ def run_fleur_scfs(self): wf_parameter = {} else: wf_parameter = para - wf_parameter['serial'] = self.ctx.serial + wf_parameter['add_comp_para'] = self.ctx.add_comp_para #wf_parameter['options'] = self.ctx.options wf_parameters = Dict(dict=wf_parameter) resall = {} @@ -508,7 +511,7 @@ def find_parameters(self): #self.ctx.ref_calcs_torun.append(ref_el) # for entry in ref[elem] find parameter node - for elm, struc in six.iteritems(self.ctx.ref): + for elm, struc in self.ctx.ref.items(): #print(elm, struc) #self.ctx.ref_calcs_torun.append(ref_el) pass @@ -531,7 +534,7 @@ def run_scfs_ref(self): wf_parameter = {} else: wf_parameter = para - wf_parameter['serial'] = self.ctx.serial + wf_parameter['add_comp_para'] = self.ctx.add_comp_para # TODO maybe use less resources, or default of one machine #wf_parameter['options'] = self.ctx.options wf_parameters = Dict(dict=wf_parameter) @@ -682,7 +685,7 @@ def collect_results(self): #first substract efermi from corelevel of reference structures # TODO check if both values, corelevel and efermi are in eV - for compound, atomtypes_list in six.iteritems(ref_atomtypes): + for compound, atomtypes_list in ref_atomtypes.items(): # atomtype_list contains a list of dicts of all atomtypes from compound x # get corelevels of compound x cls_all_atomtyps = ref_all_corelevel[compound] @@ -702,7 +705,7 @@ def collect_results(self): #now substract efermi from corelevel of compound structure #and calculate core level shifts - for compound, cls_atomtypes_list in six.iteritems(all_corelevel): + for compound, cls_atomtypes_list in all_corelevel.items(): #init, otherwise other types will override for i, atomtype in enumerate(atomtypes[compound]): elm = atomtype.get('element', None) @@ -740,13 +743,13 @@ def collect_results(self): # have been calculated. # convert total_en dict to list, why? total_en_list = [] - for key, val in six.iteritems(total_en): + for key, val in total_en.items(): total_en_list.append([key, val]) if self.ctx.calculate_formation_energy: # the reference total energy is for the whole structure with several atoms, # we need it per atom ref_total_en_norm = {} - for key, val in six.iteritems(ref_total_en): + for key, val in ref_total_en.items(): elm_dict = get_natoms_element(key) ref_total_en_norm[list(elm_dict.keys())[0]] = 1.0 * val / list(elm_dict.values())[0] #print(ref_total_en_norm) @@ -841,7 +844,7 @@ def return_results(self): #outdict = {} #outdict['output_initial_cls_wc_para'] = outputnode #print outdict - for k, v in six.iteritems(outdict): + for k, v in outdict.items(): self.out(k, v) msg = ('INFO: Initial_state_CLS workflow Done') self.report(msg) @@ -1177,10 +1180,10 @@ def clshifts_to_be(coreleveldict, reference_dict): """ return_corelevel_dict = {} - for elem, corelevel_dict in six.iteritems(coreleveldict): + for elem, corelevel_dict in coreleveldict.items(): ref_el = reference_dict.get(elem, {}) return_corelevel_dict[elem] = {} - for corelevel_name, corelevel_list in six.iteritems(corelevel_dict): + for corelevel_name, corelevel_list in corelevel_dict.items(): ref_cl = ref_el.get(corelevel_name, []) be_all = [] nref = len(ref_cl) diff --git a/aiida_fleur/workflows/mae.py b/aiida_fleur/workflows/mae.py index 7d0e17da3..472898bcf 100644 --- a/aiida_fleur/workflows/mae.py +++ b/aiida_fleur/workflows/mae.py @@ -23,7 +23,7 @@ from aiida.engine import WorkChain, ToContext, if_ from aiida.engine import calcfunction as cf -from aiida.orm import Code, load_node, CalcJobNode +from aiida.orm import Code, load_node from aiida.orm import RemoteData, Dict from aiida.common import AttributeDict from aiida.common.exceptions import NotExistent @@ -32,7 +32,7 @@ from aiida_fleur.workflows.scf import FleurScfWorkChain from aiida_fleur.workflows.base_fleur import FleurBaseWorkChain from aiida_fleur.data.fleurinpmodifier import FleurinpModifier -from aiida_fleur.data.fleurinp import FleurinpData +from aiida_fleur.data.fleurinp import FleurinpData, get_fleurinp_from_remote_data from aiida_fleur.common.constants import HTR_TO_EV @@ -60,8 +60,12 @@ class FleurMaeWorkChain(WorkChain): 'use_soc_ref': False, 'sqas_theta': [0.0, 1.57079, 1.57079], 'sqas_phi': [0.0, 0.0, 1.57079], - 'serial': False, - 'only_even_MPI': False, + 'add_comp_para': { + 'serial': False, + 'only_even_MPI': False, + 'max_queue_nodes': 20, + 'max_queue_wallclock_sec': 86400 + }, 'soc_off': [], 'inpxml_changes': [], } @@ -69,7 +73,12 @@ class FleurMaeWorkChain(WorkChain): @classmethod def define(cls, spec): super().define(spec) - spec.expose_inputs(FleurScfWorkChain, namespace='scf') + spec.expose_inputs(FleurScfWorkChain, + namespace_options={ + 'required': False, + 'populate_defaults': False + }, + namespace='scf') spec.input('wf_parameters', valid_type=Dict, required=False) spec.input('fleur', valid_type=Code, required=True) spec.input('remote', valid_type=RemoteData, required=False) @@ -177,7 +186,7 @@ def start(self): return self.exit_codes.ERROR_INVALID_CODE_PROVIDED # Check if user gave an input setup making any sense - if inputs.scf: + if 'scf' in inputs: self.ctx.scf_needed = True if 'remote' in inputs: error = 'ERROR: you gave SCF input + remote for the FT' @@ -262,12 +271,7 @@ def change_fleurinp(self): else: # In this case only remote is given # fleurinp data has to be generated from the remote inp.xml file - remote_node = self.inputs.remote - for link in remote_node.get_incoming().all(): - if isinstance(link.node, CalcJobNode): - parent_calc_node = link.node - retrieved_node = parent_calc_node.get_outgoing().get_node_by_label('retrieved') - fleurin = FleurinpData(files=['inp.xml'], node=retrieved_node) + fleurin = get_fleurinp_from_remote_data(self.inputs.remote) # copy default changes fchanges = self.ctx.wf_dict.get('inpxml_changes', []) @@ -402,8 +406,7 @@ def force_after_scf(self): label, description, settings, - serial=self.ctx.wf_dict['serial'], - only_even_MPI=self.ctx.wf_dict['only_even_MPI']) + add_comp_para=self.ctx.wf_dict['add_comp_para']) future = self.submit(FleurBaseWorkChain, **inputs_builder) return ToContext(f_t=future) @@ -438,8 +441,7 @@ def force_wo_scf(self): label, description, settings, - serial=self.ctx.wf_dict['serial'], - only_even_MPI=self.ctx.wf_dict['only_even_MPI']) + add_comp_para=self.ctx.wf_dict['add_comp_para']) future = self.submit(FleurBaseWorkChain, **inputs_builder) return ToContext(f_t=future) @@ -468,10 +470,10 @@ def get_results(self): fleurout = calculation.outputs.output_parameters fleur_output_uuid = fleurout.uuid out_dict = fleurout.dict - t_energydict = out_dict.mae_force_evSum + t_energydict = out_dict.mae_force_evsum mae_thetas = out_dict.mae_force_theta mae_phis = out_dict.mae_force_phi - e_u = out_dict.energy_units + e_u = out_dict.mae_force_units minenergy = min(t_energydict) diff --git a/aiida_fleur/workflows/relax.py b/aiida_fleur/workflows/relax.py index 1cdd36bb6..1fb59fd77 100644 --- a/aiida_fleur/workflows/relax.py +++ b/aiida_fleur/workflows/relax.py @@ -45,7 +45,7 @@ class FleurRelaxWorkChain(WorkChain): 'run_final_scf': False, # Run a final scf on the final relaxed structure 'break_symmetry': False, # Break the symmetry for the relaxation each atom own type 'change_mixing_criterion': 0.025, # After the force is smaller switch mixing scheme - 'atoms_off': [], # Species to be switched off, '49' is reserved + 'atoms_off': [], # Species to be switched off, '49999' is reserved 'relaxation_type': 'atoms' # others include None and maybe in the future volume # None would run an scf only } @@ -136,8 +136,8 @@ def start(self): wf_dict[key] = wf_dict.get(key, val) self.ctx.wf_dict = wf_dict - if '49' in wf_dict['atoms_off']: - error = '"49" label for atoms_off is reserved for internal use' + if '49999' in wf_dict['atoms_off']: + error = '"49999" label for atoms_off is reserved for internal use' self.report(error) return self.exit_codes.ERROR_INVALID_INPUT_PARAM @@ -169,6 +169,13 @@ def start(self): self.report('Error: Wrong input: inpgen missing for final scf.') return self.exit_codes.ERROR_INPGEN_MISSING + # initialize contents to avoid access failures + self.ctx.total_energy_last = None #total_energy + self.ctx.total_energy_units = None #total_energy_units + self.ctx.final_cell = None + self.ctx.final_atom_positions = None #atom_positions + self.ctx.atomtype_info = None + def should_relax(self): """ Should we run a relaxation or only a final scf @@ -225,7 +232,9 @@ def get_inputs_first_scf(self): if self.ctx.wf_dict['film_distance_relaxation']: scf_wf_dict['inpxml_changes'].append(('set_atomgr_att', { 'attributedict': { - 'force': [('relaxXYZ', 'FFT')] + 'force': { + 'relaxXYZ': 'FFT' + } }, 'species': 'all' })) @@ -233,16 +242,20 @@ def get_inputs_first_scf(self): for specie_off in self.ctx.wf_dict['atoms_off']: scf_wf_dict['inpxml_changes'].append(('set_atomgr_att_label', { 'attributedict': { - 'force': [('relaxXYZ', 'FFF')] + 'force': { + 'relaxXYZ': 'FFF' + } }, 'atom_label': specie_off })) scf_wf_dict['inpxml_changes'].append(('set_atomgr_att_label', { 'attributedict': { - 'force': [('relaxXYZ', 'FFF')] + 'force': { + 'relaxXYZ': 'FFF' + } }, - 'atom_label': '49' + 'atom_label': '49999' })) input_scf.wf_parameters = Dict(dict=scf_wf_dict) @@ -459,12 +472,8 @@ def get_results_relax(self): else: pass self.ctx.final_structure = structure - self.ctx.total_energy_last = None #total_energy - self.ctx.total_energy_units = None #total_energy_units self.ctx.final_cell = structure.cell - self.ctx.final_atom_positions = None #atom_positions - self.ctx.atomtype_info = None - + # The others are already put to None return try: @@ -490,14 +499,14 @@ def get_results_relax(self): self.ctx.final_atom_positions = atom_positions self.ctx.atomtype_info = atomtype_info - if film == 'True': + if film: self.ctx.pbc = (True, True, False) else: self.ctx.pbc = (True, True, True) # we build the structure here, that way we can run an scf afterwards # construct it in a way which preserves the species information from the initial input structure - if self.ctx.final_cell: + if self.ctx.final_cell is not None: np_cell = np.array(self.ctx.final_cell) * BOHR_A structure = StructureData(cell=np_cell.tolist()) #self.report('############ {}'.format(atomtype_info)) @@ -511,7 +520,7 @@ def get_results_relax(self): symbols=element, name=species_name) else: # assume z-direction is orthogonal to xy - structure.append_atom(position=(pos_abs[0], pos_abs[1], atom[3] * BOHR_A), + structure.append_atom(position=(pos_abs[0], pos_abs[1], atom[2] * BOHR_A), symbols=element, name=species_name) diff --git a/aiida_fleur/workflows/scf.py b/aiida_fleur/workflows/scf.py index f48eebd09..6ca9c461e 100644 --- a/aiida_fleur/workflows/scf.py +++ b/aiida_fleur/workflows/scf.py @@ -17,11 +17,9 @@ # TODO: make smarter, ggf delete mixing_history or restart with more or less iterations # you can use the pattern of the density convergence for this # TODO: maybe write dict schema for wf_parameter inputs, how? -from __future__ import absolute_import from lxml import etree -import six -from aiida.orm import Code, load_node, CalcJobNode +from aiida.orm import Code, load_node from aiida.orm import StructureData, RemoteData, Dict, Bool, Float from aiida.engine import WorkChain, while_, if_, ToContext from aiida.engine import calcfunction as cf @@ -30,12 +28,13 @@ from aiida_fleur.data.fleurinpmodifier import FleurinpModifier from aiida_fleur.tools.common_fleur_wf import get_inputs_fleur, get_inputs_inpgen from aiida_fleur.tools.common_fleur_wf import test_and_get_codenode -from aiida_fleur.tools.xml_util import eval_xpath2, get_xml_attribute from aiida_fleur.tools.common_fleur_wf import find_last_submitted_calcjob from aiida_fleur.tools.create_kpoints_from_distance import create_kpoints_from_distance_parameter from aiida_fleur.workflows.base_fleur import FleurBaseWorkChain -from aiida_fleur.data.fleurinp import FleurinpData +from aiida_fleur.data.fleurinp import FleurinpData, get_fleurinp_from_remote_data + +from masci_tools.io.parsers.fleur import outxml_parser class FleurScfWorkChain(WorkChain): @@ -71,8 +70,12 @@ class FleurScfWorkChain(WorkChain): 'kpoints_force_odd': False, 'kpoints_force_false': False, 'mode': 'density', # 'density', 'energy' or 'force' - 'serial': False, - 'only_even_MPI': False, + 'add_comp_para': { + 'serial': False, + 'only_even_MPI': False, + 'max_queue_nodes': 20, + 'max_queue_wallclock_sec': 86400 + }, 'itmax_per_run': 30, 'force_dict': { 'qfix': 2, @@ -84,6 +87,7 @@ class FleurScfWorkChain(WorkChain): } _default_options = { + 'optimize_resources': True, 'resources': { 'num_machines': 1, 'num_mpiprocs_per_machine': 1 @@ -150,11 +154,10 @@ def start(self): else: wf_dict = wf_default - for key, val in six.iteritems(wf_default): + for key, val in wf_default.items(): wf_dict[key] = wf_dict.get(key, val) self.ctx.wf_dict = wf_dict - self.ctx.serial = self.ctx.wf_dict.get('serial', False) fleur = self.inputs.fleur fleur_extras = fleur.extras inpgen_extras = None @@ -166,7 +169,7 @@ def start(self): user_options = {} if 'options' in self.inputs: user_options = self.inputs.options.get_dict() - + ''' # extend options by code defaults given in code extras # Maybe do full recursive merge if 'queue_defaults' in fleur_extras: @@ -175,7 +178,7 @@ def start(self): defaults_queue = qd.get(queue, {}) for key, val in defaultoptions.items(): defaultoptions[key] = defaults_queue.get(key, val) - + ''' if 'options' in self.inputs: options = user_options else: @@ -184,7 +187,7 @@ def start(self): # and queue does not matter in case of direct scheduler # extend options given by user using defaults - for key, val in six.iteritems(defaultoptions): + for key, val in defaultoptions.items(): options[key] = options.get(key, val) self.ctx.options = options @@ -280,8 +283,8 @@ def validate_input(self): # check the mode in wf_dict mode = self.ctx.wf_dict.get('mode') - if mode not in ['force', 'density', 'energy']: - error = ('ERROR: Wrong mode of convergence' + ": one of 'force', 'density' or 'energy' was expected.") + if mode not in ['force', 'density', 'energy', 'gw']: + error = ('ERROR: Wrong mode of convergence' + ": one of 'force', 'density', 'energy' or 'gw' was expected.") return self.exit_codes.ERROR_INVALID_INPUT_PARAM max_iters = self.ctx.wf_dict.get('itmax_per_run') @@ -393,21 +396,8 @@ def change_fleurinp(self): elif 'remote_data' in inputs: # In this case only remote_data for input structure is given # fleurinp data has to be generated from the remote inp.xml file to use change_fleurinp - remote_node = self.inputs.remote_data - for link in remote_node.get_incoming().all(): - if isinstance(link.node, CalcJobNode): - parent_calc_node = link.node - retrieved_node = parent_calc_node.get_outgoing().get_node_by_label('retrieved') - try: - if self.ctx.wf_dict['use_relax_xml']: - fleurin = FleurinpData(files=['inp.xml', 'relax.xml'], node=retrieved_node) - self.report('INFO: generated FleurinpData from inp.xml and relax.xml') - else: - raise ValueError - except ValueError: - fleurin = FleurinpData(files=['inp.xml'], node=retrieved_node) - self.report('INFO: generated FleurinpData from inp.xml') - fleurin.store() + fleurin = get_fleurinp_from_remote_data(self.inputs.remote_data, store=True) + self.report(f'INFO: generated FleurinpData from files {fleurin}') wf_dict = self.ctx.wf_dict force_dict = wf_dict.get('force_dict') @@ -436,31 +426,27 @@ def change_fleurinp(self): dist = 0.0 fleurmode.set_inpchanges({'itmax': self.ctx.default_itmax, 'minDistance': dist}) - avail_ac_dict = fleurmode.get_avail_actions() + elif converge_mode == 'gw': + dist = 0.0 + fleurmode.set_inpchanges({'itmax': self.ctx.default_itmax, 'minDistance': dist, 'gw': 1}) + if 'settings' in self.inputs: + self.inputs.settings.append({'additional_retrieve_list': ['basis.hdf', 'pot.hdf', 'ecore']}) + self.inputs.settings.append({'additional_remotecopy_list': ['basis.hdf', 'pot.hdf', 'ecore']}) + else: + self.inputs.settings = { + 'additional_retrieve_list': ['basis.hdf', 'pot.hdf', 'ecore'], + 'additional_remotecopy_list': ['basis.hdf', 'pot.hdf', 'ecore'] + } # apply further user dependend changes if fchanges: - for change in fchanges: - function = change[0] - para = change[1] - method = avail_ac_dict.get(function, None) - if not method: - error = ("ERROR: Input 'inpxml_changes', function {} " - 'is not known to fleurinpmodifier class, ' - 'please check/test your input. I abort...' - ''.format(function)) - self.control_end_wc(error) - return self.exit_codes.ERROR_CHANGING_FLEURINPUT_FAILED - - else: # apply change - try: - method(**para) - except ValueError as vale: - error = ('ERROR: Changing the inp.xml file failed. Tried to apply {}' - ', which failed with {}. I abort, good luck next time!' - ''.format(change, vale)) - self.control_end_wc(error) - return self.exit_codes.ERROR_CHANGING_FLEURINPUT_FAILED + try: + fleurmode.add_task_list(fchanges) + except (ValueError, TypeError) as exc: + error = ('ERROR: Changing the inp.xml file failed. Tried to apply inpxml_changes' + f', which failed with {exc}. I abort, good luck next time!') + self.control_end_wc(error) + return self.exit_codes.ERROR_CHANGING_FLEURINPUT_FAILED # validate? try: @@ -469,6 +455,11 @@ def change_fleurinp(self): error = ('ERROR: input, user wanted inp.xml changes did not validate') self.report(error) return self.exit_codes.ERROR_INVALID_INPUT_FILE + except ValueError as exc: + error = ('ERROR: input, user wanted inp.xml changes could not be applied.' + f'The following error was raised {exc}') + self.control_end_wc(error) + return self.exit_codes.ERROR_CHANGING_FLEURINPUT_FAILED # apply out = fleurmode.freeze() @@ -520,8 +511,7 @@ def run_fleur(self): label, description, settings, - serial=self.ctx.serial, - only_even_MPI=self.ctx.wf_dict['only_even_MPI']) + add_comp_para=self.ctx.wf_dict['add_comp_para']) future = self.submit(FleurBaseWorkChain, **inputs_builder) self.ctx.loop_count = self.ctx.loop_count + 1 self.report('INFO: run FLEUR number: {}'.format(self.ctx.loop_count)) @@ -561,55 +551,39 @@ def get_res(self): """ self.report('INFO: get results FLEUR') - xpath_energy = '/fleurOutput/scfLoop/iteration/totalEnergy/@value' - xpath_iter = '/fleurOutput/scfLoop/iteration' - xpath_force = 'totalForcesOnRepresentativeAtoms/forceTotal' - - # be aware of magnetism - xpath_distance = '/fleurOutput/scfLoop/iteration/densityConvergence/chargeDensity/@distance' - overallchargedensity_xpath = ('/fleurOutput/scfLoop/iteration/densityConvergence/' - 'overallChargeDensity/@distance') - mode = self.ctx.wf_dict.get('mode') if self.ctx.parse_last: last_base_wc = self.ctx.last_base_wc fleur_calcjob = load_node(find_last_submitted_calcjob(last_base_wc)) walltime = last_base_wc.outputs.output_parameters.dict.walltime + if isinstance(walltime, int): self.ctx.total_wall_time = self.ctx.total_wall_time + walltime - with fleur_calcjob.outputs.retrieved.open(fleur_calcjob.process_class._OUTXML_FILE_NAME, - 'r') as outxmlfile_opened: - tree = etree.parse(outxmlfile_opened) - root = tree.getroot() - - energies = eval_xpath2(root, xpath_energy) - for energy in energies: - self.ctx.total_energy.append(float(energy)) - - overall_distances = eval_xpath2(root, overallchargedensity_xpath) - if not overall_distances: - distances = eval_xpath2(root, xpath_distance) - for distance in distances: - self.ctx.distance.append(float(distance)) + with fleur_calcjob.outputs.retrieved.open(fleur_calcjob.process_class._OUTXML_FILE_NAME, 'r') as outxmlfile: + output_dict = outxml_parser(outxmlfile, + minimal_mode=True, + list_return=True, + iteration_to_parse='all', + ignore_validation=True) + + energies = output_dict.get('energy_hartree', []) + if energies is not None: + self.ctx.total_energy.extend(energies) + + if 'overall_density_convergence' in output_dict: + distances = output_dict['overall_density_convergence'] else: - for distance in overall_distances: - self.ctx.distance.append(float(distance)) + distances = output_dict.get('density_convergence', []) + + if distances is not None: + self.ctx.distance.extend(distances) if mode == 'force': - iter_all = eval_xpath2(root, xpath_iter) - for iteration in iter_all: - forces = eval_xpath2(iteration, xpath_force) - forces_in_iter = [] - for force in forces: - force_x = float(get_xml_attribute(force, 'F_x')) - force_y = float(get_xml_attribute(force, 'F_y')) - force_z = float(get_xml_attribute(force, 'F_z')) - - forces_in_iter.append(force_x) - forces_in_iter.append(force_y) - forces_in_iter.append(force_z) - - self.ctx.all_forces.append(forces_in_iter) + forces = output_dict.get('force_atoms', []) + if forces is not None: + for force_iter in forces: + self.ctx.all_forces.append([force for atom, force in force_iter]) + else: errormsg = 'ERROR: scf wc was not successful, check log for details' self.control_end_wc(errormsg) @@ -641,14 +615,15 @@ def condition(self): if mode == 'force': forces = self.ctx.all_forces if len(forces) >= 2: - self.ctx.forcediff = max([abs(forces[-1][i] - forces[-2][i]) for i in range(len(forces[-1]))]) + self.ctx.forcediff = max( + [abs(forces[-1][i][k] - forces[-2][i][k]) for i in range(len(forces[-1])) for k in range(3)]) else: self.ctx.forcediff = 'can not be determined' if mode == 'density': if self.ctx.wf_dict.get('density_converged') >= self.ctx.last_charge_density: return False - elif mode == 'energy': + elif mode in ('energy', 'gw'): if self.ctx.wf_dict.get('energy_converged') >= self.ctx.energydiff: return False elif mode == 'force': @@ -773,7 +748,7 @@ def return_results(self): outdict['last_fleur_calc_output'] = last_calc_out #outdict['output_scf_wc_para'] = outputnode - for link_name, node in six.iteritems(outdict): + for link_name, node in outdict.items(): self.out(link_name, node) if not self.ctx.reached_conv: @@ -800,7 +775,7 @@ def create_scf_result_node(**kwargs): So far it is just also parsed in as argument, because so far we are to lazy to put most of the code overworked from return_results in here. """ - for key, val in six.iteritems(kwargs): + for key, val in kwargs.items(): if key == 'outpara': # should be always there outpara = val outdict = {} diff --git a/aiida_fleur/workflows/ssdisp.py b/aiida_fleur/workflows/ssdisp.py index a0b544848..0ea2c7c09 100644 --- a/aiida_fleur/workflows/ssdisp.py +++ b/aiida_fleur/workflows/ssdisp.py @@ -23,7 +23,7 @@ from aiida.engine import WorkChain, ToContext, if_ from aiida.engine import calcfunction as cf -from aiida.orm import Code, load_node, CalcJobNode +from aiida.orm import Code, load_node from aiida.orm import RemoteData, Dict from aiida.common import AttributeDict from aiida.common.exceptions import NotExistent @@ -34,7 +34,7 @@ from aiida_fleur.data.fleurinpmodifier import FleurinpModifier from aiida_fleur.workflows.base_fleur import FleurBaseWorkChain from aiida_fleur.common.constants import HTR_TO_EV -from aiida_fleur.data.fleurinp import FleurinpData +from aiida_fleur.data.fleurinp import FleurinpData, get_fleurinp_from_remote_data class FleurSSDispWorkChain(WorkChain): @@ -63,15 +63,24 @@ class FleurSSDispWorkChain(WorkChain): 'prop_dir': [1.0, 0.0, 0.0], 'q_vectors': [[0.0, 0.0, 0.0], [0.125, 0.0, 0.0], [0.250, 0.0, 0.0], [0.375, 0.0, 0.0]], 'ref_qss': [0.0, 0.0, 0.0], - 'serial': False, - 'only_even_MPI': False, + 'add_comp_para': { + 'serial': False, + 'only_even_MPI': False, + 'max_queue_nodes': 20, + 'max_queue_wallclock_sec': 86400 + }, 'inpxml_changes': [] } @classmethod def define(cls, spec): super().define(spec) - spec.expose_inputs(FleurScfWorkChain, namespace='scf') + spec.expose_inputs(FleurScfWorkChain, + namespace_options={ + 'required': False, + 'populate_defaults': False + }, + namespace='scf') spec.input('wf_parameters', valid_type=Dict, required=False) spec.input('fleur', valid_type=Code, required=True) spec.input('remote', valid_type=RemoteData, required=False) @@ -162,7 +171,7 @@ def start(self): return self.exit_codes.ERROR_INVALID_CODE_PROVIDED # Check if user gave an input setup making any sense - if inputs.scf: + if 'scf' in inputs: self.ctx.scf_needed = True if 'remote' in inputs: error = 'ERROR: you gave SCF input + remote for the FT' @@ -223,7 +232,9 @@ def get_inputs_scf(self): for key, val in six.iteritems(self.ctx.wf_dict.get('beta')): scf_wf_dict['inpxml_changes'].append(('set_atomgr_att_label', { 'attributedict': { - 'nocoParams': [('beta', val)] + 'nocoParams': { + 'beta': val + } }, 'atom_label': key })) @@ -262,12 +273,7 @@ def change_fleurinp(self): if 'fleurinp' in self.inputs: # use the given fleurinp fleurin = self.inputs.fleurinp else: # or generate a new one from inp.xml located in the remote folder - remote_node = self.inputs.remote - for link in remote_node.get_incoming().all(): - if isinstance(link.node, CalcJobNode): - parent_calc_node = link.node - retrieved_node = parent_calc_node.get_outgoing().get_node_by_label('retrieved') - fleurin = FleurinpData(files=['inp.xml'], node=retrieved_node) + fleurin = get_fleurinp_from_remote_data(self.inputs.remote) # copy inpchanges from wf parameters fchanges = self.ctx.wf_dict.get('inpxml_changes', []) @@ -300,7 +306,9 @@ def change_fleurinp(self): for key, val in six.iteritems(self.ctx.wf_dict.get('beta')): fchanges.append(('set_atomgr_att_label', { 'attributedict': { - 'nocoParams': [('beta', val)] + 'nocoParams': { + 'beta': val + } }, 'atom_label': key })) @@ -408,8 +416,7 @@ def force_after_scf(self): label, description, settings, - serial=self.ctx.wf_dict['serial'], - only_even_MPI=self.ctx.wf_dict['only_even_MPI']) + add_comp_para=self.ctx.wf_dict['add_comp_para']) future = self.submit(FleurBaseWorkChain, **inputs_builder) return ToContext(f_t=future) @@ -444,8 +451,7 @@ def force_wo_scf(self): label, description, settings, - serial=self.ctx.wf_dict['serial'], - only_even_MPI=self.ctx.wf_dict['only_even_MPI']) + add_comp_para=self.ctx.wf_dict['add_comp_para']) future = self.submit(FleurBaseWorkChain, **inputs_builder) return ToContext(f_t=future) @@ -468,8 +474,8 @@ def get_results(self): try: out_dict = calculation.outputs.output_parameters.dict - t_energydict = out_dict.spst_force_evSum - e_u = out_dict.energy_units + t_energydict = out_dict.spst_force_evsum + e_u = out_dict.spst_force_units # Find a minimal value of SpSp and count it as 0 minenergy = min(t_energydict) diff --git a/aiida_fleur/workflows/ssdisp_conv.py b/aiida_fleur/workflows/ssdisp_conv.py index ee9e06b52..c14c348f9 100644 --- a/aiida_fleur/workflows/ssdisp_conv.py +++ b/aiida_fleur/workflows/ssdisp_conv.py @@ -138,7 +138,9 @@ def get_inputs_scf(self): for key, val in six.iteritems(self.ctx.wf_dict.get('beta')): scf_wf_dict['inpxml_changes'].append(('set_atomgr_att_label', { 'attributedict': { - 'nocoParams': [('beta', val)] + 'nocoParams': { + 'beta': val + } }, 'atom_label': key })) diff --git a/aiida_fleur/workflows/strain.py b/aiida_fleur/workflows/strain.py new file mode 100644 index 000000000..4f6863e35 --- /dev/null +++ b/aiida_fleur/workflows/strain.py @@ -0,0 +1,467 @@ +# -*- coding: utf-8 -*- +############################################################################### +# Copyright (c), Forschungszentrum Jülich GmbH, IAS-1/PGI-1, Germany. # +# All rights reserved. # +# This file is part of the AiiDA-FLEUR package. # +# # +# The code is hosted on GitHub at https://github.com/JuDFTteam/aiida-fleur # +# For further information on the license, see the LICENSE.txt file # +# For further information please visit http://www.flapw.de or # +# http://aiida-fleur.readthedocs.io/en/develop/ # +############################################################################### +""" +In this module you find the workflow 'FleurStrainWorkChain' for the calculation of +of deformation potential +""" + +from __future__ import absolute_import +from __future__ import print_function +import numpy as np + +import six + +from aiida.plugins import DataFactory +from aiida.orm import Code, load_node +from aiida.orm import Float, StructureData, Dict +from aiida.engine import WorkChain, ToContext # ,Outputs +from aiida.engine import calcfunction as cf + +from aiida_fleur.tools.StructureData_util import rescale, rescale_nowf, is_structure +from aiida_fleur.workflows.scf import FleurScfWorkChain +from aiida_fleur.tools.common_fleur_wf import test_and_get_codenode +from aiida_fleur.tools.common_fleur_wf_util import check_eos_energies + +# pylint: disable=invalid-name +FleurInpData = DataFactory('fleur.fleurinp') +# pylint: enable=invalid-name + + +class FleurStrainWorkChain(WorkChain): + """ + This workflow calculates the deformation potential a structure = -BdEg/dP = d(Eg)/d(ln(V)). + Calculates several unit cells with different volumes. + A Birch_Murnaghan equation of states fit determines the Bulk modulus(B) and the + ground-state volume of the cell. + + :params wf_parameters: Dict node, optional 'wf_parameters', protocol specifying parameter dict + :params structure: StructureData node, 'structure' crystal structure + :params calc_parameters: Dict node, optional 'calc_parameters' parameters for inpgen + :params inpgen: Code node, + :params fleur: Code node, + + + :return output_strain_wc_para: Dict node, contains relevant output information. + about general succeed, fit results and so on. + """ + + _workflowversion = '0.3.5' + + _default_options = { + 'resources': { + 'num_machines': 1 + }, + 'max_wallclock_seconds': 6 * 60 * 60, + 'queue_name': '', + 'custom_scheduler_commands': '', + 'import_sys_environment': False, + 'environment_variables': {} + } + + _wf_default = { + 'fleur_runmax': 4, + 'density_converged': 0.02, + 'serial': False, + 'itmax_per_run': 30, + 'inpxml_changes': [], + 'points': 3, + 'step': 0.02, + 'guess': 1.00 + } + + _scf_keys = ['fleur_runmax', 'density_converged', 'serial', 'itmax_per_run', 'inpxml_changes'] + + @classmethod + def define(cls, spec): + super(FleurStrainWorkChain, cls).define(spec) + spec.input('wf_parameters', valid_type=Dict, required=False) + spec.input('structure', valid_type=StructureData, required=True) + spec.input('calc_parameters', valid_type=Dict, required=False) + spec.input('inpgen', valid_type=Code, required=True) + spec.input('fleur', valid_type=Code, required=True) + spec.input('options', valid_type=Dict, required=False) + spec.input('settings', valid_type=Dict, required=False) + + spec.outline(cls.start, cls.structures, cls.converge_scf, cls.return_results) + + spec.output('output_strain_wc_para', valid_type=Dict) + + # exit codes + spec.exit_code(331, + 'ERROR_INVALID_CODE_PROVIDED', + message='Invalid code node specified, check inpgen and fleur code nodes.') + + def start(self): + """ + check parameters, what conditions? complete? + check input nodes + """ + self.report('Started strain workflow version {}'.format(self._workflowversion)) + + self.ctx.last_calc2 = None + self.ctx.calcs = [] + self.ctx.calcs_future = [] + self.ctx.structures = [] + self.ctx.temp_calc = None + self.ctx.structurs_uuids = [] + self.ctx.scalelist = [] + self.ctx.volume = [] + self.ctx.volume_peratom = {} + self.ctx.org_volume = -1 # avoid div 0 + self.ctx.labels = [] + self.ctx.successful = True + self.ctx.info = [] + self.ctx.warnings = [] + self.ctx.errors = [] + # TODO get all successful from convergence, if all True this + + # initialize the dictionary using defaults if no wf parameters are given + wf_default = self._wf_default + if 'wf_parameters' in self.inputs: + wf_dict = self.inputs.wf_parameters.get_dict() + else: + wf_dict = wf_default + + # extend wf parameters given by user using defaults + for key, val in six.iteritems(wf_default): + wf_dict[key] = wf_dict.get(key, val) + self.ctx.wf_dict = wf_dict + + self.ctx.points = wf_dict.get('points', 3) + self.ctx.step = wf_dict.get('step', 0.02) + self.ctx.guess = wf_dict.get('guess', 1.00) + self.ctx.serial = wf_dict.get('serial', False) # True + self.ctx.max_number_runs = wf_dict.get('fleur_runmax', 4) + + # initialize the dictionary using defaults if no options are given + defaultoptions = self._default_options + if 'options' in self.inputs: + options = self.inputs.options.get_dict() + else: + options = defaultoptions + + # extend options given by user using defaults + for key, val in six.iteritems(defaultoptions): + options[key] = options.get(key, val) + self.ctx.options = options + + # Check if user gave valid inpgen and fleur executables + inputs = self.inputs + if 'inpgen' in inputs: + try: + test_and_get_codenode(inputs.inpgen, 'fleur.inpgen', use_exceptions=True) + except ValueError: + error = ('The code you provided for inpgen of FLEUR does not use the plugin fleur.inpgen') + self.control_end_wc(error) + return self.exit_codes.ERROR_INVALID_CODE_PROVIDED + + if 'fleur' in inputs: + try: + test_and_get_codenode(inputs.fleur, 'fleur.fleur', use_exceptions=True) + except ValueError: + error = ('The code you provided for FLEUR does not use the plugin fleur.fleur') + self.control_end_wc(error) + return self.exit_codes.ERROR_INVALID_CODE_PROVIDED + + def structures(self): + """ + Creates structure data nodes with different Volume (lattice constants) + """ + points = self.ctx.points + step = self.ctx.step + guess = self.ctx.guess + startscale = guess - (points - 1) / 2 * step + + for point in range(points): + self.ctx.scalelist.append(startscale + point * step) + + self.report('scaling factors which will be calculated:{}'.format(self.ctx.scalelist)) + self.ctx.org_volume = self.inputs.structure.get_cell_volume() + self.ctx.structurs = strain_structures(self.inputs.structure, self.ctx.scalelist) + + def converge_scf(self): + """ + Launch fleur_scfs from the generated structures + """ + calcs = {} + + for i, struc in enumerate(self.ctx.structurs): + inputs = self.get_inputs_scf() + inputs['structure'] = struc + natoms = len(struc.sites) + label = str(self.ctx.scalelist[i]) + label_c = '|strain| fleur_scf_wc' + description = '|FleurStrainWorkChain|fleur_scf_wc|scale {}, {}'.format(label, i) + # inputs['label'] = label_c + # inputs['description'] = description + + self.ctx.volume.append(struc.get_cell_volume()) + self.ctx.volume_peratom[label] = struc.get_cell_volume() / natoms + self.ctx.structurs_uuids.append(struc.uuid) + + result = self.submit(FleurScfWorkChain, **inputs) + self.ctx.labels.append(label) + calcs[label] = result + + return ToContext(**calcs) + + def get_inputs_scf(self): + """ + get and 'produce' the inputs for a scf-cycle + """ + inputs = {} + + scf_wf_param = {} + for key in self._scf_keys: + scf_wf_param[key] = self.ctx.wf_dict.get(key) + inputs['wf_parameters'] = scf_wf_param + + inputs['options'] = self.ctx.options + + try: + calc_para = self.inputs.calc_parameters.get_dict() + except AttributeError: + calc_para = {} + inputs['calc_parameters'] = calc_para + + inputs['inpgen'] = self.inputs.inpgen + inputs['fleur'] = self.inputs.fleur + + inputs['options'] = Dict(dict=inputs['options']) + inputs['wf_parameters'] = Dict(dict=inputs['wf_parameters']) + inputs['calc_parameters'] = Dict(dict=inputs['calc_parameters']) + + return inputs + + def return_results(self): + """ + Return the results of the calculations (scf workchains) and do a + polynomial fit + """ + distancelist = [] + t_energylist = [] + t_energylist_peratom = [] + bandgaplist = [] + calc_uuids = [] + vol_peratom_success = [] + natoms = len(self.inputs.structure.sites) + htr_to_ev = 27.21138602 + + for label in self.ctx.labels: + calc = self.ctx[label] + + if not calc.is_finished_ok: + message = ('One SCF workflow was not successful: {}'.format(label)) + self.ctx.warnings.append(message) + self.ctx.successful = False + continue + + try: + _ = calc.outputs.output_scf_wc_para + except KeyError: + message = ('One SCF workflow failed, no scf output node: {}.' ' I skip this one.'.format(label)) + self.ctx.errors.append(message) + self.ctx.successful = False + continue + + outpara = calc.outputs.output_scf_wc_para.get_dict() + + t_e = outpara.get('total_energy', float('nan')) + e_u = outpara.get('total_energy_units', 'eV') + if e_u in ('Htr', 'htr'): + t_e = t_e * htr_to_ev + dis = outpara.get('distance_charge', float('nan')) + dis_u = outpara.get('distance_charge_units') + t_energylist.append(t_e) + t_energylist_peratom.append(t_e / natoms) + vol_peratom_success.append(self.ctx.volume_peratom[label]) + distancelist.append(dis) + calc_uuid = outpara.get('last_calc_uuid') + calc_uuids.append(calc_uuid) + bandgaplist.append(load_node(calc_uuid).res.bandgap) + + en_array = np.array(t_energylist_peratom) + vol_array = np.array(vol_peratom_success) + eg_array = np.array(bandgaplist) + vol_unitcell_array = vol_array * natoms + + if len(en_array): + volume, bulk_modulus, bulk_deriv, residuals = birch_murnaghan_fit(en_array, vol_array) + dprime = deformation_potential(vol_unitcell_array, eg_array) + + volumes = self.ctx.volume + gs_scale = volume * natoms / self.ctx.org_volume + if (volume * natoms < volumes[0]) or (volume * natoms > volumes[-1]): + warn = ('Groundstate volume was not in the scaling range.') + hint = ('Consider rerunning around point {}'.format(gs_scale)) + self.ctx.info.append(hint) + self.ctx.warnings.append(warn) + else: + volumes = None + gs_scale = None + residuals = None + volume = 0 + bulk_modulus = None + bulk_deriv = None + dprime = None + + # if (self.inputs.structure.get_extra('local_name')): + # local_name=self.inputs.structure.get_extra('local_name') + # else: + # local_name='' + + outputnode_dict = {} + outputnode_dict['workflow_name'] = self.__class__.__name__ + outputnode_dict['workflow_version'] = self._workflowversion + outputnode_dict['material'] = self.inputs.structure.get_formula() + outputnode_dict['kind_names'] = self.inputs.structure.get_kind_names() + # outputnode_dict['local_name'] = local_name + outputnode_dict['deformation_potential'] = dprime + outputnode_dict['scaling'] = self.ctx.scalelist + outputnode_dict['scaling_gs'] = gs_scale + outputnode_dict['initial_structure'] = self.inputs.structure.uuid + outputnode_dict['volume_gs'] = volume * natoms + outputnode_dict['volumes'] = volumes + outputnode_dict['volume_units'] = 'A^3' + outputnode_dict['natoms'] = natoms + outputnode_dict['total_energy'] = t_energylist + outputnode_dict['total_energy_units'] = e_u + outputnode_dict['bandgaps'] = bandgaplist + outputnode_dict['structures'] = self.ctx.structurs_uuids + outputnode_dict['calculations'] = calc_uuids + outputnode_dict['distance_charge'] = distancelist + outputnode_dict['distance_charge_units'] = dis_u + outputnode_dict['nsteps'] = self.ctx.points + # outputnode_dict['guess']= self.ctx.guess, + outputnode_dict['stepsize'] = self.ctx.step + outputnode_dict['residuals'] = residuals + outputnode_dict['bulk_deriv'] = bulk_deriv + outputnode_dict['bulk_modulus'] = bulk_modulus * 160.217733 # * echarge * 1.0e21,#GPa + outputnode_dict['bulk_modulus_units'] = 'GPa' + outputnode_dict['info'] = self.ctx.info + outputnode_dict['warnings'] = self.ctx.warnings + outputnode_dict['errors'] = self.ctx.errors + + if self.ctx.successful: + self.report('Done, Strain calculation complete') + else: + self.report('Done, but something went wrong.... Probably some individual calculation failed or' + ' a scf-cycle did not reach the desired distance.') + + outputnode_t = Dict(dict=outputnode_dict) + outdict = create_strain_result_node(outpara=outputnode_t) + + # create link to work-chain node + for link_name, node in six.iteritems(outdict): + self.out(link_name, node) + + def control_end_wc(self, errormsg): + """ + Controlled way to shut-down the work-chain. It will initialize the output nodes + The shut-down of the work-chain will has to be done afterwards + """ + self.ctx.successful = False + self.report(errormsg) + self.ctx.errors.append(errormsg) + self.return_results() + + +@cf +def create_strain_result_node(**kwargs): + """ + Pseudo wf, to create the right graph structure of AiiDA. + This work function will create the output node in the database. + It also connects the output_node to all nodes the information comes from. + """ + for key, val in six.iteritems(kwargs): + if key == 'outpara': + outpara = val + outdict = {} + outputnode = outpara.clone() + outputnode.label = 'output_strain_wc_para' + outputnode.description = ('Contains results and information of a FleurStrainWorkChain run.') + outdict['output_strain_wc_para'] = outputnode + + return outdict + + +def strain_structures(inp_structure, scalelist): + """ + Creates many re-scaled StructureData nodes out of a crystal structure. + Keeps the provenance in the database. + + :param StructureData, a StructureData node (pk, sor uuid) + :param scale-list, list of floats, scaling factors for the cell + + :returns: list of New StructureData nodes with rescalled structure, which are linked to input + Structure + """ + structure = is_structure(inp_structure) + if not structure: + # TODO: log something (test if it gets here at all) + return None + re_structures = [] + + for scale in scalelist: + structure_rescaled = rescale(structure, Float(scale)) # this is a wf + re_structures.append(structure_rescaled) + + return re_structures + + +# pylint: disable=invalid-name +def birch_murnaghan_fit(energies, volumes): + """ + least squares fit of a Birch-Murnaghan equation of state curve. From delta project + containing in its columns the volumes in A^3/atom and energies in eV/atom + # The following code is based on the source code of eos.py from the Atomic + # Simulation Environment (ASE) . + :params energies: list (numpy arrays!) of total energies eV/atom + :params volumes: list (numpy arrays!) of volumes in A^3/atom + + #volume, bulk_modulus, bulk_deriv, residuals = Birch_Murnaghan_fit(data) + """ + fitdata = np.polyfit(volumes[:]**(-2. / 3.), energies[:], 3, full=True) + ssr = fitdata[1] + sst = np.sum((energies[:] - np.average(energies[:]))**2.) + + residuals0 = ssr / sst + deriv0 = np.poly1d(fitdata[0]) + deriv1 = np.polyder(deriv0, 1) + deriv2 = np.polyder(deriv1, 1) + deriv3 = np.polyder(deriv2, 1) + + volume0 = 0 + x = 0 + for x in np.roots(deriv1): + if x > 0 and deriv2(x) > 0: + volume0 = x**(-3. / 2.) + break + + if volume0 == 0: + print('Error: No minimum could be found') + return None + + derivV2 = 4. / 9. * x**5. * deriv2(x) + derivV3 = (-20. / 9. * x**(13. / 2.) * deriv2(x) - 8. / 27. * x**(15. / 2.) * deriv3(x)) + bulk_modulus0 = derivV2 / x**(3. / 2.) + bulk_deriv0 = -1 - x**(-3. / 2.) * derivV3 / derivV2 + + return volume0, bulk_modulus0, bulk_deriv0, residuals0 + + +def deformation_potential(volume, bandgap): + """Calculate the deformation potential""" + xs = np.log(volume) + ys = bandgap + dprime = (((np.mean(xs) * np.mean(ys)) - np.mean(xs * ys)) / ((np.mean(xs)**2) - np.mean(xs**2))) + return dprime diff --git a/docs/Makefile b/docs/Makefile index d4f5b57ef..8b705c5ae 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -10,7 +10,7 @@ BUILDDIR = build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source +ALLSPHINXOPTS = -a -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source diff --git a/docs/README.rst b/docs/README.rst index 093130e18..1c29ea3ec 100644 --- a/docs/README.rst +++ b/docs/README.rst @@ -3,13 +3,18 @@ run:: make html +or run:: + + sphinx-build -b html -d build/doctrees -nW source build/html + This generates a html documentation tree under docs/build/html You can browse to docs/build/html/index.html to see the documentation in html format. .. note:: However, that this requires to have AiiDA already installed - on your computer (and sphinx installed, too). + on your computer (and sphinx installed, too). + All requirements should be met however by installing 'pip -e .[docs]'. If you received a distribution file, you should already find the compiled documentation in docs/build/html/index.html. @@ -18,6 +23,3 @@ in html format. using:: sudo pip install sphinx_rtd_theme - - -TODO: find a way to document the test, they should not run, when the documentation is build :-) diff --git a/docs/b_code b/docs/b_code deleted file mode 100644 index f4be38111..000000000 --- a/docs/b_code +++ /dev/null @@ -1,53 +0,0 @@ -Plug-in Classes -=============== - - -Fleur input generator plug-in -+++++++++++++++++++++++++++++ - - -Fleurinputgen Calculation -------------------------- -.. automodule:: aiida_fleur.calculations.fleurinputgen - :members: - -Fleurinputgen Parser --------------------- -.. automodule:: aiida_fleur.parsers.fleur_inputgen - :members: - - -Fleur input Data structure -++++++++++++++++++++++++++ - - -FleurinpData ------------- -.. automodule:: aiida_fleur.data.fleurinp - :members: - :special-members: __init__ - -.. automodule:: aiida_fleur.data.fleurinpmodefier - - -Fleur plug-in -+++++++++++++ - -Fleur Calculation ------------------ -.. automodule:: aiida_fleur.calculations.fleur - :members: - -Fleur Parser ------------- -.. automodule:: aiida_fleur.parsers.fleur - :members: - -Fleur tools/utility -------------------- - -.. automodule:: aiida_fleur.tools.StructureData_util.py - -.. automodule:: aiida_fleur.tool.xml_util.py - -.. automodule:: aiida_fleur.tools.merge_parameter.py diff --git a/docs/code.rst b/docs/code.rst deleted file mode 100644 index be8fc404a..000000000 --- a/docs/code.rst +++ /dev/null @@ -1,64 +0,0 @@ -Plug-in Classes -=============== - - -Fleur input generator plug-in -+++++++++++++++++++++++++++++ - - -Fleurinputgen Calculation -------------------------- -.. automodule:: aiida.orm.calculation.job.fleur_inp.fleurinputgen - :members: - -Fleurinputgen Parser --------------------- -.. automodule:: aiida.parsers.plugins.fleur_inp.fleur_inputgen - :members: - - -Fleur input Data structure -++++++++++++++++++++++++++ - - -FleurinpData ------------- -.. automodule:: aiida.orm.data.fleurinp - :members: - :special-members: __init__ - -.. automodule:: aiida.orm.data.fleurinp.fleurinpmodifier - :members: - -Fleur plug-in -+++++++++++++ - -Fleur Calculation ------------------ -.. automodule:: aiida.orm.calculation.job.fleur_inp.fleur - :members: - -Fleur Parser ------------- -.. automodule:: aiida.parsers.plugins.fleur_inp.fleur - :members: - -Fleur tools/utility -=================== - -StructureData utility -+++++++++++++++++++++ - -.. automodule:: aiida.tools.codespecific.fleur.StructureData_util - :members: - -XML utility -+++++++++++ - -.. automodule:: aiida.tools.codespecific.fleur.xml_util - :members: - -ParameterData utility -+++++++++++++++++++++ -.. automodule:: aiida.tools.codespecific.fleur.merge_parameter - :members: diff --git a/docs/requirements_for_rtd.txt b/docs/requirements_for_rtd.txt index 929c09f13..59551695c 100644 --- a/docs/requirements_for_rtd.txt +++ b/docs/requirements_for_rtd.txt @@ -1,7 +1,9 @@ pip>=18.0 setuptools>34 lxml>=3.6.4 -aiida-core>=1.0.1,<2.0.0 +aiida-core>=1.0.1 masci-tools ase pymatgen +sphinx_click +docutils<0.17 diff --git a/docs/requirements_for_rtd_aiida.txt b/docs/requirements_for_rtd_aiida.txt deleted file mode 100644 index 4ba141bcf..000000000 --- a/docs/requirements_for_rtd_aiida.txt +++ /dev/null @@ -1,73 +0,0 @@ -Flask-Cache==0.13.1 -Flask-Cors==3.0.7 -Flask-HTTPAuth==3.2.4 -Flask-RESTful==0.3.7 -Flask-SQLAlchemy==2.3.2 -Flask==1.0.2 -Jinja2==2.11.3 -MarkupSafe==1.1.1 -PyCifRW==4.2.1; python_version < '3' -PyCifRW==4.4; python_version >= '3' -PyMySQL==0.9.3 -PyYAML>=4.2b1 -Pygments==2.7.4 -SQLAlchemy-Utils==0.33.11 -SQLAlchemy==1.3.3 -Sphinx==1.8.4 -aiida-export-migration-tests==0.7.0 -aldjemy==0.9.1 -alembic==1.0.7 -ase==3.17.0 -circus==0.15.0 -click-completion==0.5.1 -click-config-file==0.5.0 -click-spinner==0.1.8 -click==7.0 -codecov==2.0.15 -coverage==4.5.2 -django>=1.11.23 -docutils==0.14 -enum34==1.1.6; python_version<'3.5' -ete3==3.1.1 -flask-marshmallow==0.9.0 -futures; python_version=='2.7' -graphviz==0.10.1 -ipython>=4.0,<6.0 -itsdangerous==1.1.0 -kiwipy[rmq]==0.5.1 -marshmallow-sqlalchemy==0.16.0 -mock==2.0.0 -monty==2.0.4 -numpy==1.16.4,<1.18.0 -paramiko==2.6.0 -passlib==1.7.1 -pg8000<1.13.0 -pgtest==1.2.0 -pika==1.0.0 -plumpy==0.14.2 -psutil==5.6.6 -pyblake2==1.1.2; python_version<'3.6' -pymatgen<=2018.12.12 -pyparsing==2.3.1 -pytest-cov==2.6.1 -pytest==4.3.0 -python-dateutil==2.8.0 -python-memcached==1.59 -pytz==2018.9 -reentry>=1.3.0 -seekpath==1.8.4 -simplejson==3.16.0 -singledispatch>=3.4.0.3; python_version<'3.5' -six==1.12.0 -spglib==1.12.2.post0 -sphinx-rtd-theme==0.4.3 -sphinxcontrib-contentui==0.2.2 -sqlalchemy-diff==0.1.3 -sqlalchemy-migrate==0.12.0 -tabulate==0.8.3 -tornado<5.0 -typing==3.6.6; python_version<'3.5' -tzlocal==1.5.1 -unittest2==1.1.0; python_version<'3.5' -uritools==2.2.0 -wrapt==1.11.1 diff --git a/docs/source/b_index.rstb b/docs/source/b_index.rstb deleted file mode 100644 index 8c1bb1eca..000000000 --- a/docs/source/b_index.rstb +++ /dev/null @@ -1,203 +0,0 @@ -.. fleur_fleur documentation master file, created by - sphinx-quickstart on Wed Aug 10 10:20:55 2016. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -############################################## -Welcome to the `AiiDA-FLEUR`_'s documentation! -############################################## - - -.. figure:: images/fleur.png - :width: 25 % - :align: center -.. figure:: images/MAX-orizz.eps - :width: 50 % - :align: center -.. figure:: images/AiiDA_transparent_logo.png - :width: 50 % - :align: center - -.. _AiiDA: http://www.aiida.net -.. _FLEUR: http://www.flapw.de -.. _AIIDA-FLEUR: https://github.com/broeder-j/aiida-fleur - - - -The aiida-fleur python package enables the use of the all-electron DFT code Fleur (http://www.flapw.de) with the AiiDA framework (http://www.aiida.net). -The package contains plugins for the Fleur code, inputgenerator and a datastructure. Further it contains basic workflows and utility. -It open source under the MIT license and is available under (https://github.com/broeder-j/aiida-fleur). -It is developed within the MaX EU Center of Excellence (www.max-center.eu) at Forschungszentrum Jülich GmbH (http://www.fz-juelich.de/pgi/pgi-1/DE/Home/home_node.html), (IAS-1/PGI-1), Germany. - -.. note:: On these pages is the documentation of the aiida-fleur source code, some design description, usuage examples and tutorials of the plugin. For futher PGI-1 interal hints go to the Fleur wiki. - -If you use this package please cite: - -* for the plugin and workflows: (to come) -* for fleur: http:/www.flapw.de - - -User's Guide -++++++++++++ - -.. toctree:: - - - -Developer's Guide -+++++++++++++++++ - - - - -Requirements to use this code: ------------------------------- - -* A running AiiDA version (and postgresql database) -* Executables of the Fleur code - -Other packages (in addition to all requirements of AiiDA): - -* lxml -* ase - -Acknowledgments: ----------------- - -We acknowledge partial support from the EU Centre of Excellence “MaX – Materials Design at the Exascale” (http://www.max-centre.eu). (Horizon 2020 EINFRA-5, Grant No. 676598) -We also thank the AiiDA team for their help. - -Installation Instructions: --------------------------- - -Install from pypi the latest release:: - - $ pip install aiida-fleur - - -From the aiida-fleur folder use:: - - $ pip install . - # or which is very useful to keep track of the changes (developers) - $ pip install -e . - -To uninstall use:: - - $ pip uninstall aiida-fleur - - -To test if the installation was successful use:: - -$ verdi calculation plugins - - # example output: - - ## Pass as a further parameter one (or more) plugin names - ## to get more details on a given plugin. - * calculation - * codtools.cifcellcontents - * codtools.cifcodcheck - * codtools.cifcodnumbers - * codtools.ciffilter - * codtools.cifsplitprimitive - ... - * fleur.fleur - * fleur.inpgen - - -You should see fleur.* in the list - -Also run the basics tests under aiida_fleur/aiida_fleur/tests/:: - - $ ./run_all_cov.sh - -Workflows tests under /aiida_fleur/aiida_fleur/tests/workflow_tests/ have to be currently run manual, -after modifing run_workflow.sh with the code you want to use and workflows you want to test:: - - $ ./run_all_workflow_tests.sh - -after the workflow have finished running, either check manual, or execute:: - - $ ./generate_workflow_test_report.sh - - - -Contents: ---------- - -The Fleur plug-in -================= - -.. toctree:: - :maxdepth: 4 - - plugin/fleur_plugin -.. - examples - -Common Fleur Workflows -====================== - -.. toctree:: - :maxdepth: 4 - - workflows/index -.. - utility - tests - code - examples - -Guides/tutorials -================ -.. toctree:: - :maxdepth: 4 - - guides/guides -.. - installation - run inpgen - run fleur - change fleurinp - extract results - - -Tools and utility -================= - -.. toctree:: - :maxdepth: 4 - - tools/tools -.. - plugin/utility - -FAQ -=== - -.. toctree:: - :maxdepth: 4 - - plugin/hints_faq - -Source code documentation -========================= - -.. toctree:: - :maxdepth: 4 - - code - -.. - utility - workflows - documentation_fleur_plugin - - - -Indices and tables ------------------- - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/docs/source/conf.py b/docs/source/conf.py index ea8f7186e..7aac4a77f 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -46,13 +46,16 @@ 'sphinx.ext.coverage', 'sphinx.ext.ifconfig', 'sphinx.ext.intersphinx', - 'sphinx.ext.viewcode'] + 'sphinx.ext.viewcode', + 'sphinx_click.ext'] todo_include_todos = True intersphinx_mapping = { # 'python': ('https://docs.python.org/2.7', None), 'aiida': ('https://aiida-core.readthedocs.io/en/latest/', None), + 'masci-tools': ('https://masci-tools.readthedocs.io/en/latest/', None), + 'ase': ('https://wiki.fysik.dtu.dk/ase/', None) } nitpick_ignore = [('py:obj', 'module')] @@ -91,7 +94,7 @@ release = aiida_fleur.__version__ # The short X.Y version. -version = '.'.join(release.split('.')[:2]) +version = '.'.join(release.split('.')[:3]) author = 'The AiiDA-FLEUR team.' @@ -133,6 +136,7 @@ # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False +#suppress_warnings = [] # -- Options for HTML output ---------------------------------------------- @@ -300,6 +304,9 @@ configuration.IN_RT_DOC_MODE = True configuration.BACKEND = 'django' configuration.AIIDADB_PROFILE = 'default' + #Perform reentry scan + from reentry import manager + manager.scan() ''' @@ -423,14 +430,10 @@ def setup(app): # Warnings to ignore when using the -n (nitpicky) option # We should ignore any python built-in exception, for instance -nitpick_ignore = [] - -for line in open('nitpick-exceptions'): - if line.strip() == '' or line.startswith('#'): - continue - dtype, target = line.split(None, 1) - target = target.strip() - nitpick_ignore.append((dtype, target)) +with open('nitpick-exceptions', 'r') as handle: + nitpick_ignore = [ + tuple(line.strip().split(None, 1)) for line in handle.readlines() if line.strip() and not line.startswith('#') + ] html_static_path = ['_static'] diff --git a/docs/source/devel_guide/dg_index.rst b/docs/source/devel_guide/dg_index.rst index ff068db48..18966f70d 100644 --- a/docs/source/devel_guide/dg_index.rst +++ b/docs/source/devel_guide/dg_index.rst @@ -259,6 +259,40 @@ The package name has to be reserved/registerd in the AiiDA registry, because ent The right handside has the form 'module_path:class_name'. +Documentation ++++++++++++++ + +Since a lot of the documentation is auto generated it is important that you give every module, class and function +proper doc strings. + + +For the documentation we use `sphinx `, which is based on restructured text, `also see `. +And we build and upload the documentation to `readthedocs ` +Also in restructured text headings are marked with some underlining, while the order is arbitrary and sphinx determines it on occurrence. +To make the whole documentation consistent it is important that you stay to the conventions of underlying. + ++---------------+----------------+------------------+ +| Heading level | underline with | Comment | ++---------------+----------------+------------------+ +| 0 | # | | ++---------------+----------------+------------------+ +| 1 | ``*`` | | ++---------------+----------------+------------------+ +| 2 | = | usual start here | ++---------------+----------------+------------------+ +| 3 | ``+`` | | ++---------------+----------------+------------------+ +| 4 | ``-`` | | ++---------------+----------------+------------------+ +| 5 | ^ | | ++---------------+----------------+------------------+ +| 6 | ' | | ++---------------+----------------+------------------+ +| 7 | , | | ++---------------+----------------+------------------+ +| 8 | . | | ++---------------+----------------+------------------+ + Other information +++++++++++++++++ diff --git a/docs/source/images/Logo-JLVMD.png b/docs/source/images/Logo-JLVMD.png new file mode 100644 index 000000000..fa8106836 Binary files /dev/null and b/docs/source/images/Logo-JLVMD.png differ diff --git a/docs/source/images/MAX-orizz.eps b/docs/source/images/MAX-orizz.eps deleted file mode 100644 index 65669bf77..000000000 Binary files a/docs/source/images/MAX-orizz.eps and /dev/null differ diff --git a/docs/source/images/MAX-orizz.jpeg b/docs/source/images/MAX-orizz.jpeg deleted file mode 100644 index 0f7d6d799..000000000 Binary files a/docs/source/images/MAX-orizz.jpeg and /dev/null differ diff --git a/docs/source/images/MAX-orizz.png b/docs/source/images/MAX-orizz.png new file mode 100644 index 000000000..83ada3e5b Binary files /dev/null and b/docs/source/images/MAX-orizz.png differ diff --git a/docs/source/images/MaX.png b/docs/source/images/MaX.png deleted file mode 100644 index 44fdbfb3f..000000000 Binary files a/docs/source/images/MaX.png and /dev/null differ diff --git a/docs/source/images/convergence_all_MP_metals.png b/docs/source/images/convergence_all_MP_metals.png index 0ea8c0f5a..9f6f41a2c 100644 Binary files a/docs/source/images/convergence_all_MP_metals.png and b/docs/source/images/convergence_all_MP_metals.png differ diff --git a/docs/source/images/convergence_all_MP_metals1.png b/docs/source/images/convergence_all_MP_metals1.png new file mode 100644 index 000000000..0ea8c0f5a Binary files /dev/null and b/docs/source/images/convergence_all_MP_metals1.png differ diff --git a/docs/source/index.rst b/docs/source/index.rst index 613154cb2..23993a7d1 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -7,48 +7,70 @@ Welcome to the `AiiDA-FLEUR`_'s documentation! ############################################## - -.. figure:: images/fleur.png - :width: 25 % - :align: center -.. figure:: images/MAX-orizz.eps - :width: 50 % - :align: center -.. figure:: images/AiiDA_transparent_logo.png +.. |logo1| image:: images/AiiDA_transparent_logo.png + :width: 80 % + :align: middle +.. |logo2| image:: images/fleur.png :width: 50 % - :align: center - -.. _AiiDA: http://www.aiida.net -.. _FLEUR: http://www.flapw.de + :align: middle +.. |logo3| image:: images/MAX-orizz.png + :width: 80 % +.. |logo4| image:: images/Logo-JLVMD.png + :width: 80 % + ++---------+---------+ +| |logo1| | |logo2| | ++---------+---------+ +| |logo3| | |logo4| | ++---------+---------+ + +.. _AiiDA: https://www.aiida.net +.. _FLEUR: https://www.flapw.de .. _AIIDA-FLEUR: https://github.com/broeder-j/aiida-fleur .. _registry: https://aiidateam.github.io/aiida-registry .. _OQMD: http://oqmd.org +.. _JuDFT: https://judft.de +.. _MaX EU Center of Excellence: https://www.max-center.eu +.. _Joint Lab Virtual Materials Design (JLVMD): https://www.fz-juelich.de/pgi/pgi-1/EN/Forschung/Joint-Lab-VMD/_node.html The AiiDA-FLEUR python package enables the use of the all-electron Density Functional Theory (DFT) -code FLEUR (http://www.flapw.de) with the `AiiDA`_ framework (http://www.aiida.net). +code `FLEUR`_ with the `AiiDA`_ framework. -It is open source under the MIT license and is available under -(https://github.com/JuDFTteam/aiida-fleur). -The package is developed within the MaX EU Center of Excellence (www.max-center.eu) at -Forschungszentrum Jülich GmbH (http://www.fz-juelich.de/pgi/pgi-1/DE/Home/home_node.html), -(IAS-1/PGI-1), Germany. +It is open source under the MIT license and available on `github `_. +The package is developed mainly at the Forschungszentrum Jülich GmbH, +`(IAS-1/PGI-1) `_, Germany. Check out the AiiDA `registry`_ to find out more about what other packages for AiiDA exists, -that might be helpful for you. +that might be helpful for you and checkout `JuDFT`_ for further information on other IAS-1 made simulation software. +**************** +Acknowledgments: +**************** + +We acknowledge partial support from the EU Centre of Excellence “MaX – Materials Design at the +Exascale” (http://www.max-centre.eu). (Horizon 2020 EINFRA-5, Grant No. 676598). +We also acknowledge support by the `Joint Lab Virtual Materials Design (JLVMD)`_ of the Forschungszentrum Jülich. +We thank the AiiDA team for their help and work. Also the vial exchange with developers of AiiDA +packages for other codes was inspiring. If you use this package please cite: * The plugin and workflows: - J. Broeder, D. Wortmann, and S. Blügel, + J. Bröder, D. Wortmann, and S. Blügel, Using the AiiDA-FLEUR package for all-electron ab initio electronic structure data generation and processing in materials science, - In Extreme Data Workshop 2018 Proceedings, 2019, vol 40, p 43-48 + `In Extreme Data Workshop 2018 Proceedings, 2019, vol 40, p 43-48 `_ - * The FLEUR code: http:/www.flapw.de +************ +User support +************ +You can post any questions in the Fleur user `forum `_ + +For bugs, feature requests and further issues please use the issue tracker on github of the aiida-fleur repository. + **************************************** Features, Illustrations, Usage examples: **************************************** @@ -65,7 +87,7 @@ Features, Illustrations, Usage examples: .. topic:: Example 2, Material screening: - Fleur SCF convergence of 1362 different screened Binary systems managed by the scf workchain + Fleur SCF convergence of over 4000 different screened binary systems managed by the scf workchain .. figure:: images/convergence_all_MP_metals.png :width: 100 % @@ -161,13 +183,6 @@ The package also contains AiiDA dependent tools around the workflows and plugins All tools independent on aiida-core are moved to the masci-tools repository, to be available to other non AiiDA related projects and tools. -Acknowledgments: -================ - -We acknowledge partial support from the EU Centre of Excellence “MaX – Materials Design at the -Exascale” (http://www.max-centre.eu). (Horizon 2020 EINFRA-5, Grant No. 676598). -We thank the AiiDA team for their help and work. Also the vial exchange with developers of AiiDA -packages for other codes was inspiring. ************ User's Guide diff --git a/docs/source/module_guide/code.rst b/docs/source/module_guide/code.rst index cc1d9e841..33ea683f8 100644 --- a/docs/source/module_guide/code.rst +++ b/docs/source/module_guide/code.rst @@ -43,6 +43,8 @@ Fleurinp modifier .. automodule:: aiida_fleur.data.fleurinpmodifier :members: + :inherited-members: + :exclude-members: modify_xmlfile Workflows/Workchains ++++++++++++++++++++ @@ -124,3 +126,12 @@ DMI: Force-theorem calculation of Dzjaloshinskii-Moriya interaction energy dispe .. automodule:: aiida_fleur.workflows.dmi :members: + + +Commandline interface (CLI) ++++++++++++++++++++++++++++ +.. _aiidafleur_cmdline: + +.. click:: aiida_fleur.cmdline:cmd_root + :prog: aiida-fleur + :show-nested: diff --git a/docs/source/module_guide/tools.rst b/docs/source/module_guide/tools.rst index 849078c26..cab750444 100644 --- a/docs/source/module_guide/tools.rst +++ b/docs/source/module_guide/tools.rst @@ -1,10 +1,8 @@ +.. _aiidafleur_tools: + Fleur tools/utility +++++++++++++++++++ -Dealing with XML Schema files ------------------------------ - -.. automodule:: aiida_fleur.fleur_schema.schemafile_index Structure Data util ------------------- @@ -15,13 +13,7 @@ Structure Data util XML utility ----------- -.. automodule:: aiida_fleur.tools.xml_util - :members: - -Utility for LDA+U density matrix files --------------------------------------- - -.. automodule:: aiida_fleur.tools.set_nmmpmat +.. automodule:: aiida_fleur.tools.xml_aiida_modifiers :members: @@ -29,13 +21,13 @@ Parameter utility ----------------- General Parameter -_________________ +^^^^^^^^^^^^^^^^^ .. automodule:: aiida_fleur.tools.dict_util :members: Merge Parameter -_______________ +^^^^^^^^^^^^^^^ .. automodule:: aiida_fleur.tools.merge_parameter :members: diff --git a/docs/source/nitpick-exceptions b/docs/source/nitpick-exceptions index ecd0a432e..7c5dfdc82 100644 --- a/docs/source/nitpick-exceptions +++ b/docs/source/nitpick-exceptions @@ -133,27 +133,6 @@ py:class logging.Logger py:class paramiko.proxy.ProxyCommand -py:class plumpy.processes.Process -py:class plumpy.process_comms.ProcessLauncher -py:class plumpy.process_spec.ProcessSpec -py:class plumpy.Process -py:class plumpy.Communicator -py:class plumpy.Bundle -py:class plumpy.workchains.WorkChainSpec -py:class plumpy.persistence.Persister -py:class plumpy.ports.Port -py:class plumpy.ports.InputPort -py:class plumpy.ports.PortNamespace -py:class plumpy.utils.AttributesDict -py:class plumpy.loaders.DefaultObjectLoader -py:class plumpy.ObjectLoader -py:exc plumpy.TaskRejected -py:meth plumpy.process_spec.ProcessSpec.expose_inputs -py:meth plumpy.process_spec.ProcessSpec.expose_outputs - -py:class kiwipy.futures.Future -py:class kiwipy.communications.TimeoutError - py:class tornado.concurrent.Future py:class IPython.core.magic.Magics @@ -198,6 +177,49 @@ py:class aiida.orm.node.Node # This comes from ABCMeta py:meth aiida.orm.group.Group.get_from_string +# issues with order of object processing and type hinting +py:class aiida.engine.runners.ResultAndNode +py:class aiida.engine.runners.ResultAndPk +py:class aiida.engine.processes.workchains.workchain.WorkChainSpec +py:class aiida.manage.manager.Manager +py:class aiida.orm.nodes.node.WarnWhenNotEntered +py:class aiida.orm.utils.links.LinkQuadruple +py:class aiida.tools.importexport.dbexport.ExportReport +py:class aiida.tools.importexport.dbexport.ArchiveData +py:class aiida.tools.groups.paths.WalkNodeResult + +py:class Backend +py:class BackendEntity +py:class BackendNode +py:class AuthInfo +py:class CalcJob +py:class CalcJobNode +py:class Data +py:class ExitCode +py:class File +py:class FolderData +py:class JobInfo +py:class JobState +py:class Node +py:class Parser +py:class PersistenceError +py:class Process +py:class ProcessBuilder +py:class ProcessNode +py:class ProcessSpec +py:class Port +py:class PortNamespace +py:class Repository +py:class Runner +py:class Transport +py:class TransportQueue +py:class WorkChainSpec + +py:class kiwipy.communications.Communicator +py:class plumpy.process_states.State +py:class plumpy.workchains._If +py:class plumpy.workchains._While + py:mod click py:class click.Choice diff --git a/docs/source/user_guide/calculations/fleurcode_plugin.rst b/docs/source/user_guide/calculations/fleurcode_plugin.rst index bf5daa4e6..bf1381000 100644 --- a/docs/source/user_guide/calculations/fleurcode_plugin.rst +++ b/docs/source/user_guide/calculations/fleurcode_plugin.rst @@ -93,7 +93,7 @@ All the outputs can be found in ``calculation.outputs``. .. The 'simple' output node will evolve. A draft of a second complex output node which .. contains informations of all iterations and atomtypes exists, but a dictionary is not .. the optimal structure for this. For now this is postponed. In any case if you want to -.. parse something from the out.xml checkout the methods in xml_util. +.. parse something from the out.xml checkout the methods in the `masci-tools` libary. Errors '''''' diff --git a/docs/source/user_guide/cmd/cmd_index.rst b/docs/source/user_guide/cmd/cmd_index.rst index 2d8f94622..30b0656ab 100644 --- a/docs/source/user_guide/cmd/cmd_index.rst +++ b/docs/source/user_guide/cmd/cmd_index.rst @@ -1,7 +1,347 @@ -Verdi command line extentions -============================= +The command line interface (CLI) +================================ -.. _GitHub: https://github.com/JuDFTteam/aiida-fleur +Or how-to manually work or script from the terminal. -Currently there are no specific verdi commands implemented for AiiDA-FLEUR. -If you have any suggestions on some, please post an issue on `GitHub`_. +Besides the python API, aiida-fleur comes with a builtin command line interface (CLI) `aiida-fleur`, +which exposes functionalities of aiida-fleur on the command line, similar to the `verdi` commands of `aiida-core`. This interface is built using the `click` library and supports tab-completion. Of course everything you can do through the CLI and much more you can also do through the python API. + +Here you will learn how to use this CLI. Everything in a code block with a "`$`" in front can be executed in a shell, if not otherwise indicated. Expected output is displayed below the command. +If a code block if a "`$`" contains "`<>`" it means that you have to replace it with what stands inside. For example `` means you have to type in the "`pk/id`" of the SCF workflow which was run. + +General information ++++++++++++++++++++ + +To enable tab-completion, add the following to your shell loading script, e.g. the .bashrc or virtual environment activate script, or execute: + +.. code-block:: bash + + eval "$(_AIIDA_FLEUR_COMPLETE=source aiida-fleur)" + +In general, to learn about a command you can execute every command with the `-h/++help` option to see its help string. This will show you what the command does and what arguments, options and defaults it has. If it is a command group it will show you all sub-commands. + +Example command group: + +.. code-block:: bash + + $ aiida-fleur -h + Usage: aiida-fleur [OPTIONS] COMMAND [ARGS]... + + CLI for the `aiida-fleur` plugin. + + Options: + -p, ++profile PROFILE Execute the command for this profile instead of the + default profile. + + -h, ++help Show this message and exit. + + Commands: + data Commands to create and inspect data nodes. + launch Commands to launch workflows and calcjobs of aiida-fleur. + plot Invoke the plot_fleur command on given nodes + workflow Commands to inspect aiida-fleur workchains. + +Example for a command: + +.. code-block:: bash + + $ aiida-fleur launch scf -h + Usage: aiida-fleur launch scf [OPTIONS] + + Launch a scf workchain + + Options: + -s, ++structure STRUCTUREFILE StructureData node, given by pk or uuid or + file in any for mat which will be converted. + [default: (dynamic)] + + -i, ++inpgen CODE A code node or label for an inpgen + executable. [default: (dynamic)] + + -calc_p, ++calc-parameters DATA + Dict with calculation (FLAPW) parameters to + build, which will be given to inpgen. + + -set, ++settings DATA Settings node for the calcjob. + -inp, ++fleurinp DATA FleurinpData node for the fleur calculation. + -f, ++fleur CODE A code node or label for a fleur executable. + [default: (dynamic)] + + -wf, ++wf-parameters DATA Dict containing parameters given to the + workchain. + + -P, ++parent-folder DATA The PK of a parent remote folder (for + restarts). + + -d, ++daemon Submit the process to the daemon instead of + running it locally. [default: False] + + -set, ++settings DATA Settings node for the calcjob. + -opt, ++option-node DATA Dict, an option node for the workchain. + -h, ++help Show this message and exit. + +For the full automatic documentation of all commands checkout the :ref:`Commandline Interface (CLI) section ` in the module guide. + +Overview of the main commands ++++++++++++++++++++++++++++++ + +The main commands groups of `aiida-fleur` are `data`, `launch`, `plot` and `workflow`. + +The `data` group contains commands to create and inspect data nodes, for utility which is more specific to `aiida-fleur`and not covered by the `verdi data` commands of `aiida-core`. +Sub-commands of `aiida-fleur data` include: + +.. code-block:: bash + + fleurinp Commands to handle `FleurinpData` nodes. + parameter Commands to create and inspect `Dict` nodes containing FLAPW parameters + structure Commands to create and inspect `StructureData` nodes. + +The `launch` group contains commands to launch workflows/workchains and calcjobs of `aiida-fleur` from the shell. +Sub-commands of `aiida-fleur launch` include: + +.. code-block:: bash + + banddos Launch a banddos workchain + corehole Launch a corehole workchain + create_magnetic Launch a create_magnetic workchain + dmi Launch a dmi workchain + eos Launch a eos workchain + fleur Launch a base_fleur workchain. + init_cls Launch an init_cls workchain + inpgen Launch an inpgen calcjob on given input If no code is... + mae Launch a mae workchain + relax Launch a base relax workchain # TODO final scf input + scf Launch a scf workchain + ssdisp Launch a ssdisp workchain + +Important options out most launch commands include: +The `-S` option to provide a crystal structure. This can be either a `pk` or `uuid` from a `StructureData` node in the database or any file on disk in a format `ase` can read a structure from. This includes: + + +The `plot` command invokes the `plot_fleur` command of aiida fleur on given nodes. The `plot_fleur` command can visualize the output of a lot of aiida-fleur workchains. + +The `workflow` command group has sub commands to inspect `aiida-fleur` workchains and prepare inputs. + +.. code-block:: bash + + inputdict Print data from Dict nodes input into any fleur process. + res Print data from Dict nodes returned or created by any fleur process + + + +for example to launch an scf workchain on a given structure execute: + +.. code-block:: bash + + $ aiida-fleur launch scf -i -f -s + +the command can also process structures in any format `ase` can handle, this includes `Cif`, `xsf` and `poscar` files. In such a case simply parse the path to the file: + +.. code-block:: bash + + $ aiida-fleur launch scf -i -f -s ./structure/Cu.cif + +Confirm proper setup +++++++++++++++++++++ + +Quickly confirm that you have a computer and a code setup within your database. + +.. code-block:: bash + + $ verdi computer list -a + $ verdi code list -a + +should display some configured computer and codes like this (notice the "`*`"s):: + + Info: List of configured computers + Info: Use 'verdi computer show COMPUTERNAME' to display more detailed information + * localhost + * iffslurm + + # (use 'verdi code show CODEID' to see the details) + # List of configured codes: + * pk 149 - fleur_MPI_MaXR5_AMD@iffslurm + * pk 150 - inpgen_MaXR5_AMD@iffslurm + * pk 151 - inpgen_MaXR5_th1@iffslurm + * pk 148 - fleur_MPI_MaXR5_th1@iffslurm + +Prepare options nodes ++++++++++++++++++++++ + +Usually, when submitting calculations or workchains to a computer you have to provide an `options` node +in which you specify the queue to submit to and what computational resources the scheduler should allocate. +If the default option node is enough, or if the options for the default queue stored in the 'extras' of a code node, you do not need to provide this node. + +To submit simulations to the `th1` queue with one node and run with two mpi processes execute. + +.. code-block:: bash + + aiida-fleur data options create -q 'th1' -N 1 -M 2 + +To submit simulations to the `th1-2020-32` queue with one node and run with two mpi processes execute. + +.. code-block:: bash + + aiida-fleur data options create -q 'th1-2020-32' -N 1 -M 2 + +You should see some output this:: + + Success: Created and stored Options node <290> <99f79d2e-04aa-4aaf-9b5f-9eabad8142d8> + { + "max_wallclock_seconds": 1800, + "queue_name": "th1-2020-32", + "resources": { + "num_machines": 1, + "num_mpiprocs_per_machine": 2 + } + } + +Remember these pks (further named `opt_th1_pk` and `opt_amd_pk`) we need them further for launching workchains. +To display the contents of any `aiida.orm.Dict` node you can execute `verdi data dict show `. + +Launching Calculations and workchains ++++++++++++++++++++++++++++++++++++++ + +Executing inpgen +++++++++++++++++ + +First we run a simple inpgen calculation from the command line on a Si structure provided by some cif file. + +.. code-block:: bash + + $ aiida-fleur launch inpgen -i inpgen_MaXR5_th1 -s Si.cif -q th1 + +The structure is provided via the `-s` option, which can either be an identifier of a `StructureData` node or any supported format by `ase.io` (see https://wiki.fysik.dtu.dk/ase/ase/io/io.html?highlight=formats) +Among many others this includes: + +``` +cif, poscar, xsf, xyz, concar, outcar, xtd, xsd +``` +One should be cautious when dealing with film and magnetic structures, because one has to make sure that the setup is as fleur needs it, and that all the magnetic information is preserved. One could use this command to convert most formats to fleur input, or with `++dry-run` one can get an input file for the input generator without storing anything in the database. +Also the execution above will block the interpreter until the job is finished and you see the logged output. +If the job is finished look at output of the process with + +.. code-block:: bash + + $ verdi calcjob show + $ verdi process report + +.. code-block:: bash + + $ verdi outputls + +will show you all files retrieved and stored in the aiida_repository by aiida. + +.. code-block:: bash + + $ verdi calcjob gotocomputer + +you can go to the remote computer to the directory where the job was executed (execute there `exit` or `logout` to logout from the remote computer.). +To see print the inp.xml file or any other retrieved output file execute + +.. code-block:: bash + + $ verdi cajcjob outputcat + +to see the input file for the inpgen calculation execute: + +.. code-block:: bash + + $ verdi calcjob inputcat + +Executing Fleur ++++++++++++++++ + +Launch fleur calculation works in the same way, per default the `base_fleur` workchain is launched, which has some basic error handlers for fleur calculations. On the resulting `FleurinpData` from the inpgen calculation above we now launch a fleur calculation. + +.. code-block:: bash + + $ aiida-fleur launch fleur ++fleur fleur_MaXR5_th1 -inp + +Executing higher workflows +++++++++++++++++++++++++++ + +The interface to launch other workflows is very similar to the interface and options of the base calculations. +This time for each command we execute we add the `-d` option to submit the workflow to the daemon, executing them in the background instead of blocking the interpreter. +You can launch directly workflows like this + +.. code-block:: bash + + $ aiida-fleur launch scf -d -s Si.cif -i inpgen_MaXR5_th1 ++fleur fleur_MaXR5_th1 -opt + $ aiida-fleur launch relax -d -s Si.cif -i inpgen_MaXR5_th1 ++fleur fleur_MaXR5_th1 -opt + +launch an equation of states in the background to a different queue as for the other workflows + +.. code-block:: bash + + $ aiida-fleur launch eos -s Si.cif -i inpgen_MaXR5_th1 ++fleur fleur_MaXR5_AMD -opt + + +Check with + +.. code-block:: bash + + verdi process list -p1 + +what the status of the workflows is while they execute. +When they are finished we can visualize the results using the aiida-fleur plot command, which visualizes workchain results statically with matplotlib or interactive with bokeh. + +.. code-block:: bash + + $ aiida-fleur plot + $ aiida-fleur plot + +To easily display inputs and result dictionaries of aiida-fleur workchains you can utilize the workflow sub-commands. + +.. code-block:: bash + + $ aiida-fleur workflow inputcat + $ aiida-fleur workflow res ++info + + +Congratulation, you finished the aiida-fleur command line tutorial! +Thanks you! If you have any feedback, suggestions, feature requests, contact a developer or write an issue in the aiida-fleur git repository: https://github.com/JuDFTteam/aiida-fleur . + +# Further comments, where to go from here: + +(DFT) code inter operability +++++++++++++++++++++++++++++ + +You can now run a kkr scf with this relaxed structure as inputs over the similar `aiida-kkr` CLI. +For example: +For this first look at the output from the fleur relax workflow above and identify the pk of the optimized output structure + +.. code-block:: bash + + $ verdi node show + +.. code-block:: bash + + $ aiida-kkr launch scf -S ++kkr ++voro + +For more on this checkout the aiida-kkr tutorials. + +Common workflows +++++++++++++++++ + +There is also work going on for common workflow interfaces between DFT codes. +For this checkout the aiida-common-workflow repository (https://github.com/aiidateam/aiida-common-workflows). +This is per default installed with all codes on quantum mobile, not here on iffaiida. +These common workflows use protocols ('moderate', 'fast', 'precise'), which are code specific, but which allow to execute the same type of workflow on otherwise the same input for example to following lines would execute an equation of states workflow with different codes on quantum mobile (otherwise needs more inputs): + +.. code-block:: bash + + aiida-common-workflows launch eos -S Fe -p moderate fleur + aiida-common-workflows launch eos -S Fe -p fast quantum_espresso + aiida-common-workflows launch eos -S Fe -p precise siesta + aiida-common-workflows launch eos -S Fe cp2k + +Other useful commandline interfaces: + - ASE: (https://wiki.fysik.dtu.dk/ase/cmdline.html) + +Commandline versus python work +++++++++++++++++++++++++++++++ + +Work on the commandline is rather interactive, if you do not write a bash script to execute the commands you may loose information on the execution and maybe how to find things, if you have not logged something. The same if true for working with ipython. +For testing and small projects the command line interface is really useful and fast. +For large projects we still suggest strongly to use the python interface, because there you have the full functionality of `aiida-fleur` making it easier to execute a sequence of workflows which depend on each other. diff --git a/docs/source/user_guide/data/fleurinp_data.rst b/docs/source/user_guide/data/fleurinp_data.rst index 5a17e4271..73b469c03 100644 --- a/docs/source/user_guide/data/fleurinp_data.rst +++ b/docs/source/user_guide/data/fleurinp_data.rst @@ -79,16 +79,12 @@ Properties which were added to FleurinpData. Note that all of these files will be copied to the folder where FLEUR will be run. - .. * :py:exc:`~aiida_fleur.data.fleurinp.FleurinpData._schema_file_path`: Returns the absolute - .. path of the xml schema file used for the current inp.xml file. + * :py:exc:`~aiida_fleur.data.fleurinp.FleurinpData.inp_version`: Returns the version of the stored ``inp.xml`` + + * :py:exc:`~aiida_fleur.data.fleurinp.FleurinpData.parser_info`: Returns errors, warnings and information encountered while constructing the :py:exc:`~aiida_fleur.data.fleurinp.FleurinpData.inp_dict` from the ``inp.xml`` .. note:: - :py:class:`~aiida_fleur.data.fleurinp.FleurinpData` will first look in the ``aiida_fleur/fleur_schema/input/`` for matching Fleur - xml schema files to the ``inp.xml`` files. - If it does not find a match there, it will recursively search in your PYTHONPATH - and the current directory. - If you installed the package with pip there should be no problem, as long the package versions - is new enough for the version of the Fleur code you are deploying. + :py:class:`~aiida_fleur.data.fleurinp.FleurinpData` will use the ``masci-tools`` library to parse the ``inp.xml``. This library contains the schema files for the fleur input and output XML files for many of the fleur releases starting from version ``0.27``. If a version is encountered that is not yet stored in the installed version of the ``masci-tools`` library, the latest available version is used. User Methods ------------ @@ -99,22 +95,18 @@ User Methods to :py:class:`~aiida_fleur.data.fleurinp.FleurinpData` instance. * :py:func:`~aiida_fleur.data.fleurinp.FleurinpData.set_files()` - Adds several files from a folder node to :py:class:`~aiida_fleur.data.fleurinp.FleurinpData` instance. - * :py:func:`~aiida_fleur.data.fleurinp.FleurinpData.get_fleur_modes()` - Analyses inp.xml and - get a corresponding calculation mode. + * :py:func:`~aiida_fleur.data.fleurinp.FleurinpData.get_fleur_modes()` - Analyses the inp.xml and + get a dictionary with the corresponding calculation modes (Noco, SOC, ...) * :py:func:`~aiida_fleur.data.fleurinp.FleurinpData.get_structuredata()` - A CalcFunction which returns an AiiDA :py:class:`~aiida.orm.StructureData` type extracted from the inp.xml file. * :py:func:`~aiida_fleur.data.fleurinp.FleurinpData.get_kpointsdata()` - A CalcFunction which returns an AiiDA :py:class:`~aiida.orm.KpointsData` - type produced from the inp.xml + type produced from the inp.xml. If multiple k-point sets are defined (Fleur release MaX5 or later) a dictionary of :py:class:`~aiida.orm.KpointsData` types is returned file. This only works if the kpoints are listed in the in inp.xml. * :py:func:`~aiida_fleur.data.fleurinp.FleurinpData.get_parameterdata()` - A CalcFunction that extracts a :py:class:`~aiida.orm.Dict` node containing FLAPW parameters. This node can be used as an input for inpgen. - * :py:func:`~aiida_fleur.data.fleurinpmodifier.set_kpointsdata_f()` - - A Function of fleurmodifier used to writes kpoints - of a :py:class:`~aiida.orm.KpointsData` node to the - inp.xml file. It replaces old kpoints. .. _setting_labels: @@ -173,4 +165,4 @@ line to ``inpxml_changes`` of workchain parameters: # in this example the atomgroup, to which the atom with label '222' belongs, # will be modified fm = FleurinpModifier(SomeFleurinp) - fm.set_atomgr_att_label(attributedict={'force': [('relaxXYZ', 'FFF')]}, atom_label=' 222') + fm.set_atomgr_att_label(attributedict={'force': {'relaxXYZ': 'FFF'}, atom_label=' 222') diff --git a/docs/source/user_guide/data/fleurinp_modifier.rst b/docs/source/user_guide/data/fleurinp_modifier.rst index 81398ea84..ccbc5810e 100644 --- a/docs/source/user_guide/data/fleurinp_modifier.rst +++ b/docs/source/user_guide/data/fleurinp_modifier.rst @@ -68,6 +68,7 @@ _______________ returns a new :py:class:`~aiida_fleur.data.fleurinp.FleurinpData` object. * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.changes()`: Displays the current list of changes. + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.undo()`: Remove the last registered change or all registered changes * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.show()`: Applies the modifications and displays/prints the resulting ``inp.xml`` file. Does not generate a new :py:class:`~aiida_fleur.data.fleurinp.FleurinpData` object. @@ -76,42 +77,32 @@ _______________ Modification registration methods _________________________________ -The registration methods can be separated into two groups. First of all, +The registration methods can be separated into three groups. First of all, there are XML methods that require deeper knowledge about the structure of an ``inp.xml`` file. All of them require an xpath input: - * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.xml_set_attribv_occ()`: Set an - attribute of a specific occurrence of xml elements - * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.xml_set_first_attribv()`: Set - an attribute of first occurrence of xml element - * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.xml_set_all_attribv()`: Set - attributes of all occurrences of the xml element - * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.xml_set_text()`: Set the text - of first occurrence of xml element - * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.xml_set_text_occ()`: Set - an attribute of a specific occurrence of xml elements - * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.xml_set_all_text()`: Set - the text of all occurrences of the xml element - * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.create_tag()`: Insert + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.xml_set_attrib_value_no_create()`: Set an + attribute on the specified xml elements to the specified value(s). The ``occurrences`` argument can be used to select, which occurences to modify + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.xml_set_text_no_create()`: Set the + text on the specified xml elements to the specified value(s). The ``occurrences`` argument can be used to select, which occurences to modify + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.xml_create_tag()`: Insert an xml element in the xml tree. - * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.delete_att()`: Delete - an attribute for xml elements from the xpath evaluation. - * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.delete_tag()`: Delete - an xml element. - * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.replace_tag()`: Replace - an xml element. - * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.add_num_to_att()`: Adds - a value or multiplies on it given attribute. + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.xml_replace_tag()`: Replace + an xml element in the xml tree. + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.xml_delete_tag()`: Delete + an xml element in the xml tree. + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.xml_delete_att()`: Delete an attribute on + a xml element in the xml tree. On the other hand, there are shortcut methods that already know some paths: * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.set_species()`: Specific user-friendly method to change species parameters. - * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.set_atomgr_att()`: Specific + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.set_atomgroup()`: Specific method to change atom group parameters. * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.set_species_label()`: Specific user-friendly method to change a specie of an atom with a certain label. - * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.set_atomgr_att_label()`: Specific + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.set_atomgroup_label()`: Specific method to change atom group parameters of an atom with a certain label. * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.set_inpchanges()`: Specific user-friendly method for easy changes of attribute key value type. @@ -120,9 +111,38 @@ On the other hand, there are shortcut methods that already know some paths: * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.shift_value_species_label()`: Specific user-friendly method to shift value of an attribute of an atom with a certain label. * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.set_nkpts()`: Specific - method to set the number of kpoints. + method to set the number of kpoints. **(Only for Max4 and earlier)** + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.set_kpath()`: Specific + method to set a kpoint path for bandstructures **(Only for Max4 and earlier)** + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.set_kpointlist()`: Specific + method to set the used kpoints via a array of coordinates and weights + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.set_kpointsdata()` - + User-friendly method used to writes kpoints + of a :py:class:`~aiida.orm.KpointsData` node to the + inp.xml file. It replaces old kpoints for MaX4 versions and older. for MaX5 and later the kpoints are entered as a new kpoint list + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.switch_kpointset()`: Specific + method to switch the used kpoint set. **(Only for Max5 and later)** + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.set_attrib_value()`: user-friendly method for setting attributes in the xml file by specifying their name + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.set_first_attrib_value()`: user-friendly method for setting the first occurrence of an attribute in the xml file by specifying its name + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.add_number_to_attrib()`: user-friendly method for adding to or multiplying values of attributes in the xml file by specifying their name + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.add_number_to_first_attrib()`: user-friendly method for adding to or multiplying values of the first occurrence of the attribute in the xml file by specifying their name + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.set_text()`: user-friendly method for setting text on xml elements in the xml file by specifying their name + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.set_first_text()`: user-friendly method for setting the text on the first occurrence of an xml element in the xml file by specifying its name + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.set_simple_tag()`: user-friendly method for creating and setting attributes on simple xml elements (only attributes) in the xml file by specifying its name + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.set_complex_tag()`: user-friendly method for creating complex tags in the xml file by specifying its name + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.create_tag()`: User-friendly method for inserting a tag in the right place by specifying it's name + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.delete_tag()`: User-friendly method for delete a tag by specifying it's name + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.delete_att()`: User-friendly method for deleting an attribute from a tag by specifying it's name + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.replace_tag()`: User-friendly method for replacing a tag by another by specifying its name * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.set_nmmpmat()`: Specific method for initializing or modifying the density matrix file for a LDA+U calculation (details see below) + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.rotate_nmmpmat()`: Specific + method for rotating a block of the density matrix file for a LDA+U calculation (details see below) in real space + +In addition there are methods for manipulating the stored files on the :py:class:`~aiida_fleur.data.fleurinp.FleurinpData` instance directly: + + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.set_file()`: Set a file on the Fleurinpdata instance + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.del_file()`: Delete a file on the Fleurinpdata instance The figure below shows a comparison between the use of XML and shortcut methods. @@ -130,6 +150,29 @@ The figure below shows a comparison between the use of XML and shortcut methods. :width: 100% :align: center +.. warning:: Deprecated XML modification methods + + After the `aiida-fleur` release ``1.1.4`` the FleurinpModifier was restructured to enhance it's capabilities and to make it more robust. During this process several modification functions were renamed or deprecated. Even though all the old usage is still supported it is encouraged to switch to the new method names and behaviours: + + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.xml_set_attribv_occ()`, :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.xml_set_all_attribv()` and :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.xml_set_first_attribv()` are unified in the method :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.xml_set_attrib_value_no_create()`. However, these functions **can no longer create missing subtags** + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.xml_set_text_occ()`, :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.xml_set_all_text()` and :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.xml_set_text()` are unified in the method :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.xml_set_text_no_create()`. However, these functions **can no longer create missing subtags** + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.create_tag()` is now a higher-level function. The old function requiring an xpath is now called :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.xml_create_tag()` + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.replace_tag()` is now a higher-level function. The old function requiring an xpath is now called :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.xml_replace_tag()` + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.delete_tag()` is now a higher-level function. The old function requiring an xpath is now called :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.xml_delete_tag()` + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.delete_att()` is now a higher-level function. The old function requiring an xpath is now called :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.xml_delete_att()` + an xml element in the xml tree. + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.add_num_to_att()` was renamed to :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.add_number_to_attrib()` or :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.add_number_to_first_attrib()`. However, these are also higher-level functions now longer requiring a concrete xpath + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.set_atomgr_att()` and :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.set_atomgr_att_label()` were renamed to :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.set_atomgroup()` and :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.set_atomgroup_label()`. These functions now also take the changes in the form ``attributedict={'nocoParams':{'beta': val}}`` instead of ``attributedict={'nocoParams':[('beta': val)]}`` + +.. warning:: Passing XML Elements to modification functions + + Some of the low-level implementations of the XML modification functions accept explicit XML elements as arguments for replacing/inserting. However, these can only be used within limits in the :py:class:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier` at the moment. The reason for this is that there is currently no support for serializing these objects for the input of the Aiida calcfunction. The following functions are affected by this: + + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.xml_replace_tag()` (Only usable with show and validate) + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.replace_tag()` (Only usable with show and validate) + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.xml_create_tag()` (Can only be used with string names of tags) + * :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.create_tag()` (Can only be used with string names of tags) + Modifying the density matrix for LDA+U calculations --------------------------------------------------- diff --git a/docs/source/user_guide/data/images/registration_methods.png b/docs/source/user_guide/data/images/registration_methods.png index b9d12e9a1..ffc3a82c0 100644 Binary files a/docs/source/user_guide/data/images/registration_methods.png and b/docs/source/user_guide/data/images/registration_methods.png differ diff --git a/docs/source/user_guide/getting_started/getting_started.rst b/docs/source/user_guide/getting_started/getting_started.rst index 5385626fb..0aa4826dd 100644 --- a/docs/source/user_guide/getting_started/getting_started.rst +++ b/docs/source/user_guide/getting_started/getting_started.rst @@ -2,7 +2,8 @@ Getting started =============== Installation of AiiDA-FLEUR ---------------------------- ++++++++++++++++++++++++++++ + .. _downloading: https://github.com/JuDFTteam/aiida-fleur .. _AiiDA: https://aiida.readthedocs.io/projects/aiida-core/en/latest/ .. _tutorial: https://aiida.readthedocs.io/projects/aiida-core/en/latest/install/installation.html#aiida-profile-setup @@ -15,7 +16,8 @@ description of all required steps can be found in the `AiiDA`_ documentation. However, a small guide presented below shows an example of installation of AiiDA-FLEUR. Installation of python packages -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +------------------------------- + First of all, make sure that you have all required libraries that are `needed`_ for AiiDA. .. note:: @@ -74,12 +76,14 @@ from GitHub without package reinstallation. AiiDA-FLEUR can be installed the sam AiiDA setup ----------------- ++++++++++++ + Once AiiDA-FLEUR is installed, it it necessary to setup a profile, computers and codes. Profile setup -^^^^^^^^^^^^^ +------------- + First, to set up a profile with a database, use: .. code-block:: bash @@ -112,7 +116,8 @@ something like your AiiDA is set up properly and you can continue with next section. Computers setup -^^^^^^^^^^^^^^^^^ +--------------- + AiiDA needs to know how to access the computer that you want to use for FLEUR calculations. Therefore you need to set up a computer - this procedure will create a representation (node) of computational computer in the database which will be used later. It can be done by: @@ -188,12 +193,14 @@ If you are using aiida-fleur inside FZ Jülich, you can find additional helpful setting up the connection to JURECA (or other machine) on `iffwiki`_. FLEUR and inpgen setup -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +---------------------- + AiiDA-FLEUR uses two codes: FLEUR itself and an input generator called inpgen. Thus, two codes have to be set up independently. -input generator -~~~~~~~~~~~~~~~ +Input generator +^^^^^^^^^^^^^^^ + I recommend running input generator on your local machine because it runs fast and one usually spends more time waiting for the input to be uploaded to the remote machine. You need to install inpgen @@ -220,6 +227,11 @@ after that, a vim editor pops out and you need to specify prepend and append tex add required imports and commands for you system. Particularly in my case, I need to set proper library paths. Hence my prepend text looks like: +.. note:: + The default expected Input generator version is >=32 i.e >=MaX5.1. + If you want to install older version of the input generator you *must* specify a 'version' key + under the 'extras' of the code node i.e for example code.set_extra('version', 31). + .. code-block:: bash export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/intel/mkl/lib:/usr/local/intel/compilers_and_libraries_2019.3.199/mac/compiler/lib/ @@ -227,7 +239,7 @@ set proper library paths. Hence my prepend text looks like: Now inpgen code is ready to be used. FLEUR code -~~~~~~~~~~ +^^^^^^^^^^ FLEUR code can be set up the same way as the input generator. However, there is an important note that has to be mentioned. @@ -248,7 +260,7 @@ that has to be mentioned. Remote absolute path: /scratch/user/codes/fleur_MPI Installation test -^^^^^^^^^^^^^^^^^ +----------------- To test if the aiida-fleur installation was successful use: diff --git a/docs/source/user_guide/hints/exit_codes.rst b/docs/source/user_guide/hints/exit_codes.rst index f091e8b64..5451ba0b2 100644 --- a/docs/source/user_guide/hints/exit_codes.rst +++ b/docs/source/user_guide/hints/exit_codes.rst @@ -1,7 +1,7 @@ .. _exit_codes: -Exit codes -********** +Reference of Exit codes +======================= .. _documentation: https://aiida.readthedocs.io/projects/aiida-core/en/latest/working/processes.html#exit-codes @@ -48,6 +48,8 @@ The list of all exit codes implemented in AiiDA-FLEUR: +-----------+---------------------------------------------------------+------------------------+ | 230 | Invalid workchain parameters | SSDisp Conv | +-----------+---------------------------------------------------------+------------------------+ +| 230 | Invalid workchain parameters | BandDos | ++-----------+---------------------------------------------------------+------------------------+ | 231 | Invalid input configuration | CreateMagnetic | +-----------+---------------------------------------------------------+------------------------+ | 231 | Invalid input configuration | DMI | @@ -58,6 +60,8 @@ The list of all exit codes implemented in AiiDA-FLEUR: +-----------+---------------------------------------------------------+------------------------+ | 231 | Invalid input configuration | SSDisp | +-----------+---------------------------------------------------------+------------------------+ +| 231 | Invalid input configuration | BandDos | ++-----------+---------------------------------------------------------+------------------------+ | 233 | Input codes do not correspond to | DMI | | | fleur or inpgen codes respectively. | | +-----------+---------------------------------------------------------+------------------------+ @@ -67,6 +71,9 @@ The list of all exit codes implemented in AiiDA-FLEUR: | 233 | Input codes do not correspond to | SSDisp | | | fleur or inpgen codes respectively. | | +-----------+---------------------------------------------------------+------------------------+ +| 233 | Input codes do not correspond to | BandDos | +| | fleur or inpgen codes respectively. | | ++-----------+---------------------------------------------------------+------------------------+ | 235 | Input file modification failed. | DMI | +-----------+---------------------------------------------------------+------------------------+ | 235 | Input file modification failed. | MAE | @@ -75,6 +82,8 @@ The list of all exit codes implemented in AiiDA-FLEUR: +-----------+---------------------------------------------------------+------------------------+ | 235 | Input file modification failed. | SSDisp | +-----------+---------------------------------------------------------+------------------------+ +| 235 | Input file modification failed. | BandDos | ++-----------+---------------------------------------------------------+------------------------+ | 236 | Input file was corrupted after modifications | DMI | +-----------+---------------------------------------------------------+------------------------+ | 236 | Input file was corrupted after modifications | MAE | @@ -83,6 +92,8 @@ The list of all exit codes implemented in AiiDA-FLEUR: +-----------+---------------------------------------------------------+------------------------+ | 236 | Input file was corrupted after modifications | SSDisp | +-----------+---------------------------------------------------------+------------------------+ +| 236 | Input file was corrupted after modifications | BandDos | ++-----------+---------------------------------------------------------+------------------------+ | 300 | No retrieved folder found | FleurCalculation | +-----------+---------------------------------------------------------+------------------------+ | 300 | No retrieved folder found | FleurCalculation | @@ -138,12 +149,16 @@ The list of all exit codes implemented in AiiDA-FLEUR: +-----------+---------------------------------------------------------+------------------------+ | 334 | Reference calculation failed. | SSDisp | +-----------+---------------------------------------------------------+------------------------+ +| 334 | SCF calculation failed. | BandDos | ++-----------+---------------------------------------------------------+------------------------+ | 335 | Found no reference calculation remote repository. | DMI | +-----------+---------------------------------------------------------+------------------------+ | 335 | Found no reference calculation remote repository. | MAE | +-----------+---------------------------------------------------------+------------------------+ | 335 | Found no reference calculation remote repository. | SSDisp | +-----------+---------------------------------------------------------+------------------------+ +| 335 | Found no SCF calculation remote repository. | BandDos | ++-----------+---------------------------------------------------------+------------------------+ | 336 | Force theorem calculation failed. | DMI | +-----------+---------------------------------------------------------+------------------------+ | 336 | Force theorem calculation failed. | MAE | diff --git a/docs/source/user_guide/hints/hints_faq.rst b/docs/source/user_guide/hints/hints_faq.rst index f3bb6481e..dc9a4df84 100644 --- a/docs/source/user_guide/hints/hints_faq.rst +++ b/docs/source/user_guide/hints/hints_faq.rst @@ -1,9 +1,9 @@ -Hints -===== +Hints/FAQ +========= For Users ---------- ++++++++++ .. topic:: Common Errors, Traps: @@ -23,6 +23,6 @@ For Users FAQ ---- ++++ to come diff --git a/docs/source/user_guide/tools/tools_index.rst b/docs/source/user_guide/tools/tools_index.rst index 44d21a379..012a381ca 100644 --- a/docs/source/user_guide/tools/tools_index.rst +++ b/docs/source/user_guide/tools/tools_index.rst @@ -1,18 +1,24 @@ Tools ===== -here some more information about the tools contained in this package. and how to use them +here some more information about the tools contained in this package and how to use them. -Manipulation parameterdata: +In general if you are looking for something which does not depend on AiiDA and may not be FLEUR specific, +the place to look for is the `masci-tools repository `_ and +its `documentation `_. +This includes: -merger +- parsers for files +- utility for xml +- plot methods +Tools and utility which does depend on AiiDA but is not FLEUR specific you find in the +`aiida-jutools repository `_ +This includes: -Getting structure data: +- StructureData curation +- Meta AiiDA database analysis -From cif files (ICSD) -From COD/TCOD -From OQMD -From ALFOWLIB -From Materials project +Since so far we lack a in detail documentation of tools and utility within aiida-fleur you are referenced to the +:ref:`automatic documenation of tools and utility `. diff --git a/docs/source/user_guide/tutorials/tutorials.rst b/docs/source/user_guide/tutorials/tutorials.rst index 3b11e666b..04c92faf1 100644 --- a/docs/source/user_guide/tutorials/tutorials.rst +++ b/docs/source/user_guide/tutorials/tutorials.rst @@ -1,49 +1,33 @@ Tutorials ========= +Here we link you to some tutorial resources for aiida-fleur and related topics. +In general you find hands-on tutorial material under https://github.com/JuDFTteam/judft_tutorials +and in the examples folder of the aiida-fleur package (the examples lack sometimes behind). -sda - -Basic AiiDA tutorials: -^^^^^^^^^^^^^^^^^^^^^^ +AiiDA tutorials +--------------- If you are not familiar with the basics of AiiDA yet, you might want to checkout the `AiiDA youtube tutorials. `_ -The jupyter notebooks from the tutorials you will find `here `__ on github, +The jupyter notebooks from the tutorials you will find `here on github `_, where you can also try them out in binder. -Virtual machines for tutorials and tutorial manuals you find `here `__. - - -How calculation plugins work: -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Run inpgen calculation tutorial -""""""""""""""""""""""""""""""" - -sorry, not uploaded yet - -Run fleur calculation tutorial -"""""""""""""""""""""""""""""" -sorry, not uploaded yet - -Running workflows: -^^^^^^^^^^^^^^^^^^ - -Run fleur SCF tutorial -"""""""""""""""""""""" -sorry, not uploaded yet +Virtual machines for tutorials and tutorial manuals you `find here `_. -Run fleur eos tutorial -"""""""""""""""""""""" -sorry, not uploaded yet +An introduction video into AiiDA you find `here `_ +or other videos under the `Materials cloud channel `_. -Run fleur bandstructure/dos tutorial -"""""""""""""""""""""""""""""""""""" -sorry, not uploaded yet +AiiDA-FLEUR tutorials +--------------------- +Lectures: +- Introduction into `AiiDA-fleur `_ +- Introduction into `AiiDA-fleur workflows `_ -Data extraction and evaluation: -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Hands on: +- The Hands-on session from a `tutorial in 04.2021 `_. -General calculation, workflow -""""""""""""""""""""""""""""" +FLEUR & FLAPW tutorials +----------------------- +In general for new, documentation and tutorials for the FLEUR program checkout +www.flapw.de. -Total database -"""""""""""""" +Videos and pdfs from a tutorial in 2021: +https://www.flapw.de/MaX-5.1/video/ diff --git a/docs/source/user_guide/workflows/code/banddos_parameters.py b/docs/source/user_guide/workflows/code/banddos_parameters.py new file mode 100644 index 000000000..f6f15bf53 --- /dev/null +++ b/docs/source/user_guide/workflows/code/banddos_parameters.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +'mode': 'band', +'kpath': 'auto', #seek (aiida), fleur (only Max4) or string to pass to ase +'klistname': 'path-3', +'kpoints_number': None, +'kpoints_distance': None, +'kpoints_explicit': None, #dictionary containing a list of kpoints, weights +#and additional arguments to pass to set_kpointlist +'sigma': 0.005, +'emin': -0.50, +'emax': 0.90, +'add_comp_para': { + 'serial': False, + 'only_even_MPI': False, + 'max_queue_nodes': 20, + 'max_queue_wallclock_sec': 86400 +}, +'inpxml_changes': [], diff --git a/docs/source/user_guide/workflows/code/banddos_submission.py b/docs/source/user_guide/workflows/code/banddos_submission.py new file mode 100644 index 000000000..62e1924eb --- /dev/null +++ b/docs/source/user_guide/workflows/code/banddos_submission.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +from aiida_fleur.workflows.banddos import FleurBandDosWorkChain +from aiida.orm import Dict, load_node +from aiida.engine import submit + +fleur_code = load_node(FLEUR_PK) +inpgen_code = load_node(INPGEN_PK) +structure = load_node(STRUCTURE_PK) + +wf_para = Dict( + dict={ + 'mode': 'band', + 'kpath': 'auto', #seek (aiida), fleur (only Max4) or string to pass to ase + 'klistname': 'path-3', + 'kpoints_number': None, + 'kpoints_distance': None, + 'kpoints_explicit': None, #dictionary containing a list of kpoints, weights + #and additional arguments to pass to set_kpointlist + 'sigma': 0.005, + 'emin': -0.50, + 'emax': 0.90, + 'add_comp_para': { + 'serial': False, + 'only_even_MPI': False, + 'max_queue_nodes': 20, + 'max_queue_wallclock_sec': 86400 + }, + 'inpxml_changes': [], + }) + +wf_para_scf = Dict( + dict={ + 'fleur_runmax': 3, + 'density_converged': 0.001, + 'mode': 'density', + 'itmax_per_run': 30, + 'add_comp_para': { + 'serial': False, + 'only_even_MPI': False, + 'max_queue_nodes': 20, + 'max_queue_wallclock_sec': 86400 + } + }) + +options = Dict(dict={ + 'resources': { + 'num_machines': 1, + 'num_mpiprocs_per_machine': 2 + }, + 'withmpi': True, + 'max_wallclock_seconds': 600 +}) + +options_scf = Dict(dict={ + 'resources': { + 'num_machines': 1, + 'num_mpiprocs_per_machine': 2 + }, + 'withmpi': True, + 'max_wallclock_seconds': 600 +}) + +calc_parameters = Dict(dict={'kpt': {'nkpts': 500, 'path': 'default'}}) + +inputs = { + 'scf': { + 'wf_parameters': wf_para_scf, + 'structure': structure, + 'calc_parameters': calc_parameters, + 'options': options_scf, + 'inpgen': inpgen_code, + 'fleur': fleur_code + }, + 'wf_parameters': wf_para, + 'fleur': fleur_code, + 'options': options +} + +banddos_workchain = submit(FleurBandDosWorkChain, **inputs) diff --git a/docs/source/user_guide/workflows/code/dmi_parameters.py b/docs/source/user_guide/workflows/code/dmi_parameters.py index 154dc22e3..02aaf6050 100644 --- a/docs/source/user_guide/workflows/code/dmi_parameters.py +++ b/docs/source/user_guide/workflows/code/dmi_parameters.py @@ -5,7 +5,11 @@ 'soc_off': [], # a list of atom labels to switch off SOC term 'q_vectors': [[0.0, 0.0, 0.0], # set a set of q-vectors to calculate DMI dispersion [0.1, 0.1, 0.0]] -'serial': False, # False if use MPI version for the FT calc -'only_even_MPI': False, # True if suppress parallelisation having odd number of MPI +'add_comp_para': { + 'serial': False, # False if use MPI version for the FT calc + 'only_even_MPI': False, # True if suppress parallelisation having odd number of MPI + 'max_queue_nodes': 20, # Max number of nodes allowed (used by automatic error fix) + 'max_queue_wallclock_sec': 86400 # Max number of walltime allowed (used by automatic error fix) + }, 'ref_qss': [0.0, 0.0, 0.0], # sets a q-vector for the reference calculation 'inpxml_changes': [], # additional changes before the FT step diff --git a/docs/source/user_guide/workflows/code/dmi_wc_submission.py b/docs/source/user_guide/workflows/code/dmi_wc_submission.py index 2a4b58b1a..346d7b721 100644 --- a/docs/source/user_guide/workflows/code/dmi_wc_submission.py +++ b/docs/source/user_guide/workflows/code/dmi_wc_submission.py @@ -8,8 +8,12 @@ fleur_code = load_node(FLEUR_PK) inpgen_code = load_node(INPGEN_PK) -wf_para = Dict(dict={'serial': False, - 'only_even_MPI': False, +wf_para = Dict(dict={'add_comp_para': { + 'serial': False, + 'only_even_MPI': False, + 'max_queue_nodes': 20, + 'max_queue_wallclock_sec': 86400 + }, 'beta': {'all': 1.57079}, 'sqas_theta': [0.0, 1.57079, 1.57079], 'sqas_phi': [0.0, 0.0, 1.57079], diff --git a/docs/source/user_guide/workflows/code/mae_parameters.py b/docs/source/user_guide/workflows/code/mae_parameters.py index 0f3fc55dc..7cd5a95fa 100644 --- a/docs/source/user_guide/workflows/code/mae_parameters.py +++ b/docs/source/user_guide/workflows/code/mae_parameters.py @@ -3,7 +3,11 @@ 'use_soc_ref': False, # True if reference calc should use SOC terms 'sqas_theta': [0.0, 1.57079, 1.57079], # a list of theta values for the FT 'sqas_phi': [0.0, 0.0, 1.57079], # a list of phi values for the FT -'serial': False, # False if use MPI version for the FT calc -'only_even_MPI': False, # True if suppress parallelisation having odd number of MPI +'add_comp_para': { + 'serial': False, # False if use MPI version for the FT calc + 'only_even_MPI': False, # True if suppress parallelisation having odd number of MPI + 'max_queue_nodes': 20, # Max number of nodes allowed (used by automatic error fix) + 'max_queue_wallclock_sec': 86400 # Max number of walltime allowed (used by automatic error fix) + }, 'soc_off': [], # a list of atom labels to switch off SOC term 'inpxml_changes': [] # additional changes before the FT step diff --git a/docs/source/user_guide/workflows/code/mae_wc_submission.py b/docs/source/user_guide/workflows/code/mae_wc_submission.py index d37a35eeb..fcddc4e83 100644 --- a/docs/source/user_guide/workflows/code/mae_wc_submission.py +++ b/docs/source/user_guide/workflows/code/mae_wc_submission.py @@ -12,8 +12,12 @@ 'use_soc_ref': False, 'sqas_theta': [0.0, 1.57079, 1.57079], 'sqas_phi': [0.0, 0.0, 1.57079], - 'serial': False, - 'only_even_MPI': False, + 'add_comp_para': { + 'serial': False, + 'only_even_MPI': False, + 'max_queue_nodes': 20, + 'max_queue_wallclock_sec': 86400 + }, 'soc_off': [], 'inpxml_changes': [], }) diff --git a/docs/source/user_guide/workflows/code/scf_parameters.py b/docs/source/user_guide/workflows/code/scf_parameters.py index 6725ecc09..9eee93954 100644 --- a/docs/source/user_guide/workflows/code/scf_parameters.py +++ b/docs/source/user_guide/workflows/code/scf_parameters.py @@ -4,8 +4,12 @@ 'energy_converged': 0.002, # Total energy convergence criterion 'force_converged': 0.002, # Largest force convergence criterion 'mode': 'density', # Parameter to converge: 'density', 'force' or 'energy' -'serial': False, # Execute fleur with mpi or without -'only_even_MPI': False, # True if suppress parallelisation having odd number of MPI +'add_comp_para': { + 'serial': False, # False if use MPI version for the FT calc + 'only_even_MPI': False, # True if suppress parallelisation having odd number of MPI + 'max_queue_nodes': 20, # Max number of nodes allowed (used by automatic error fix) + 'max_queue_wallclock_sec': 86400 # Max number of walltime allowed (used by automatic error fix) + }, 'itmax_per_run': 30, # Maximum iterations run for one FleurCalculation 'force_dict': {'qfix': 2, # parameters required for the 'force' mode 'forcealpha': 0.5, diff --git a/docs/source/user_guide/workflows/code/scf_wc_submission.py b/docs/source/user_guide/workflows/code/scf_wc_submission.py index cb06ee017..1ec401f10 100644 --- a/docs/source/user_guide/workflows/code/scf_wc_submission.py +++ b/docs/source/user_guide/workflows/code/scf_wc_submission.py @@ -10,8 +10,12 @@ 'density_converged': 0.001, 'mode': 'density', 'itmax_per_run': 30, - 'serial': False, - 'only_even_MPI': False}) + 'add_comp_para': { + 'serial': False, + 'only_even_MPI': False, + 'max_queue_nodes': 20, + 'max_queue_wallclock_sec': 86400 + }}) options = Dict(dict={'resources': {'num_machines': 1, 'num_mpiprocs_per_machine': 2}, 'withmpi': True, diff --git a/docs/source/user_guide/workflows/code/ssdisp_parameters.py b/docs/source/user_guide/workflows/code/ssdisp_parameters.py index 1ff43952e..dfcd4b83c 100644 --- a/docs/source/user_guide/workflows/code/ssdisp_parameters.py +++ b/docs/source/user_guide/workflows/code/ssdisp_parameters.py @@ -1,11 +1,15 @@ # -*- coding: utf-8 -*- -'beta': {'all': 1.57079}, # see description below -'prop_dir': [1.0, 0.0, 0.0], # sets a propagation direction of a q-vector -'q_vectors': [[0.0, 0.0, 0.0], # set a set of q-vectors to calculate SSDispersion +'beta': {'all': 1.57079}, # see description below +'prop_dir': [1.0, 0.0, 0.0], # sets a propagation direction of a q-vector +'q_vectors': [[0.0, 0.0, 0.0], # set a set of q-vectors to calculate SSDispersion [0.125, 0.0, 0.0], [0.250, 0.0, 0.0], [0.375, 0.0, 0.0]], -'ref_qss': [0.0, 0.0, 0.0], # sets a q-vector for the reference calculation -'inpxml_changes': [] # additional changes before the FT step -'serial': False # False if use MPI version for the FT calc -'only_even_MPI': False, # True if suppress parallelisation having odd number of MPI +'ref_qss': [0.0, 0.0, 0.0], # sets a q-vector for the reference calculation +'inpxml_changes': [] # additional changes before the FT step +'add_comp_para': { + 'serial': False, # False if use MPI version for the FT calc + 'only_even_MPI': False, # True if suppress parallelisation having odd number of MPI + 'max_queue_nodes': 20, # Max number of nodes allowed (used by automatic error fix) + 'max_queue_wallclock_sec': 86400 # Max number of walltime allowed (used by automatic error fix) + }, diff --git a/docs/source/user_guide/workflows/code/ssdisp_wc_submission.py b/docs/source/user_guide/workflows/code/ssdisp_wc_submission.py index c663d6399..f57c0bbba 100644 --- a/docs/source/user_guide/workflows/code/ssdisp_wc_submission.py +++ b/docs/source/user_guide/workflows/code/ssdisp_wc_submission.py @@ -8,63 +8,82 @@ fleur_code = load_node(FLEUR_PK) inpgen_code = load_node(INPGEN_PK) -wf_para = Dict(dict={'beta': {'all': 1.57079}, - 'prop_dir': [0.125, 0.125, 0.0], - 'q_vectors': [[0.0, 0.0, 0.0], - [0.125, 0.125, 0.0], - [0.250, 0.250, 0.0], - [0.375, 0.375, 0.0], - [0.500, 0.500, 0.0]], - 'ref_qss': [0.0, 0.0, 0.0], - 'inpxml_changes': [], - 'serial': False, - 'only_even_MPI': False - }) +wf_para = Dict( + dict={ + 'beta': { + 'all': 1.57079 + }, + 'prop_dir': [0.125, 0.125, 0.0], + 'q_vectors': [[0.0, 0.0, 0.0], [0.125, 0.125, 0.0], [0.250, 0.250, 0.0], [0.375, 0.375, 0.0], + [0.500, 0.500, 0.0]], + 'ref_qss': [0.0, 0.0, 0.0], + 'inpxml_changes': [], + 'add_comp_para': { + 'serial': False, + 'only_even_MPI': False, + 'max_queue_nodes': 20, + 'max_queue_wallclock_sec': 86400 + } + }) -options = Dict(dict={'resources': {'num_machines': 1, 'num_mpiprocs_per_machine': 24}, - 'queue_name': 'devel', - 'custom_scheduler_commands': '', - 'max_wallclock_seconds': 60*60}) +options = Dict( + dict={ + 'resources': { + 'num_machines': 1, + 'num_mpiprocs_per_machine': 24 + }, + 'queue_name': 'devel', + 'custom_scheduler_commands': '', + 'max_wallclock_seconds': 60 * 60 + }) +parameters = Dict( + dict={ + 'atom': { + 'element': 'Pt', + 'lmax': 8 + }, + 'atom2': { + 'element': 'Fe', + 'lmax': 8, + }, + 'comp': { + 'kmax': 3.8, + }, + 'kpt': { + 'div1': 20, + 'div2': 24, + 'div3': 1 + } + }) -parameters = Dict(dict={'atom': {'element': 'Pt', - 'lmax': 8 - }, - 'atom2': {'element': 'Fe', - 'lmax': 8, - }, - 'comp': {'kmax': 3.8, - }, - 'kpt': {'div1': 20, - 'div2': 24, - 'div3': 1 - }}) - -wf_para_scf = {'fleur_runmax': 2, - 'itmax_per_run': 120, - 'density_converged': 0.2, - 'serial': False, - 'mode': 'density' - } +wf_para_scf = {'fleur_runmax': 2, 'itmax_per_run': 120, 'density_converged': 0.2, 'serial': False, 'mode': 'density'} wf_para_scf = Dict(dict=wf_para_scf) -options_scf = Dict(dict={'resources': {'num_machines': 2, 'num_mpiprocs_per_machine': 24}, - 'queue_name': 'devel', - 'custom_scheduler_commands': '', - 'max_wallclock_seconds': 60*60}) - -inputs = {'scf': {'wf_parameters': wf_para_scf, - 'structure': structure, - 'calc_parameters': parameters, - 'options': options_scf, - 'inpgen': inpgen_code, - 'fleur': fleur_code - }, - 'wf_parameters': wf_para, - 'fleur': fleur_code, - 'options': options - } +options_scf = Dict( + dict={ + 'resources': { + 'num_machines': 2, + 'num_mpiprocs_per_machine': 24 + }, + 'queue_name': 'devel', + 'custom_scheduler_commands': '', + 'max_wallclock_seconds': 60 * 60 + }) +inputs = { + 'scf': { + 'wf_parameters': wf_para_scf, + 'structure': structure, + 'calc_parameters': parameters, + 'options': options_scf, + 'inpgen': inpgen_code, + 'fleur': fleur_code + }, + 'wf_parameters': wf_para, + 'fleur': fleur_code, + 'options': options +} res = submit(FleurSSDispWorkChain, **inputs) diff --git a/docs/source/user_guide/workflows/corehole_wc.rst b/docs/source/user_guide/workflows/corehole_wc.rst index a1506d8a2..a3ad697de 100644 --- a/docs/source/user_guide/workflows/corehole_wc.rst +++ b/docs/source/user_guide/workflows/corehole_wc.rst @@ -6,7 +6,7 @@ Fleur core-hole workflow Class name, import from: :: - from aiida_fleur.workflows.corehole import fleur_corehole_wc + from aiida_fleur.workflows.corehole import FleurCoreholeWorkChain #or WorkflowFactory('fleur.corehole') diff --git a/docs/source/user_guide/workflows/dos_band_wc.rst b/docs/source/user_guide/workflows/dos_band_wc.rst index 9aee76c80..e1384baec 100644 --- a/docs/source/user_guide/workflows/dos_band_wc.rst +++ b/docs/source/user_guide/workflows/dos_band_wc.rst @@ -1,22 +1,17 @@ .. _dos_band_wc: -Fleur dos/band workflows +Fleur dos/band workflow ------------------------ -.. warning:: - - These workchains do not work with AiiDA >1.0 version yet. They need to be updated. - These are two seperate workflows which are pretty similar so we treat them here together -* **Class**: :py:class:`~aiida_fleur.workflows.dos.fleur_dos_wc` and :py:class:`~aiida_fleur.workflows.banddos.FleurBandDosWorkChain` -* **String to pass to the** :py:func:`~aiida.plugins.WorkflowFactory`: ``fleur.dos``, ``fleur.banddos`` -* **Workflow type**: Workflow (lv 1) -* **Aim**: Calculate a density of states. Calculate a Band structure. -* **Compuational demand**: 1 ``Fleur Job calculation`` -* **Database footprint**: Outputnode with information, full provenance, ``~ 10`` nodes +* **Class**: :py:class:`~aiida_fleur.workflows.banddos.FleurBandDosWorkChain` +* **String to pass to the** :py:func:`~aiida.plugins.WorkflowFactory`: ``fleur.banddos`` +* **Workflow type**: Workflow (lvl 1) +* **Aim**: Calculate a density of states. Calculate a band structure. +* **Computational demand**: 1 ``Fleur Job calculation`` + 1 (optional) ``Fleur SCF workflow`` +* **Database footprint**: Outputnode with information, full provenance, ``~ 10`` nodes (more if SCF workflow is included) * **File repository footprint**: The ``JobCalculation`` run, plus the DOS or Bandstructure files -* **Additional Info**: Use alone. .. contents:: @@ -24,52 +19,150 @@ Import Example: .. code-block:: python - from aiida_fleur.workflows.dos import fleur_dos_wc - #or - WorkflowFactory('fleur.dos') - from aiida_fleur.workflows.banddos import FleurBandDosWorkChain #or WorkflowFactory('fleur.banddos') Description/Purpose ^^^^^^^^^^^^^^^^^^^ - DOS: - Calculates an Density of states (DOS) ontop of a given Fleur calculation (converged or not). + Calculates an electronic band structure on top of a given Fleur calculation (converged or not). It can be started from the crystal structure utilizing the FleurSCFWorkchain as a subworkchain - BandDos: + This workflow prepares/changes the Fleur input with respect to the kpoint set and bandstructure/DOS related parameters and manages one Fleur calculation. - Calculates an electronic band structure ontop of a given Fleur calculation (converged or not). +Input nodes: +^^^^^^^^^^^^ +.. _exposed: https://aiida.readthedocs.io/projects/aiida-core/en/latest/working/workflows.html#working-workchains-expose-inputs-outputs + +The FleurBandDosWorkChain employs +`exposed`_ feature of the AiiDA, thus inputs for the nested +:ref:`SCF` workchain should be passed in the namespace +``scf``. + ++-----------------+----------------------------------------------------+-----------------------------------------+----------+ +| name | type | description | required | ++=================+====================================================+=========================================+==========+ +| scf | namespace | inputs for nested SCF WorkChain | no | ++-----------------+----------------------------------------------------+-----------------------------------------+----------+ +| fleur | :py:class:`~aiida.orm.Code` | Fleur code | yes | ++-----------------+----------------------------------------------------+-----------------------------------------+----------+ +| wf_parameters | :py:class:`~aiida.orm.Dict` | Settings of the workchain | no | ++-----------------+----------------------------------------------------+-----------------------------------------+----------+ +| fleurinp | :py:class:`~aiida_fleur.data.fleurinp.FleurinpData`| :ref:`FLEUR input` | no | ++-----------------+----------------------------------------------------+-----------------------------------------+----------+ +| remote | :py:class:`~aiida.orm.RemoteData` | Remote folder of another calculation | no | ++-----------------+----------------------------------------------------+-----------------------------------------+----------+ +| kpoints | :py:class:`~aiida.orm.KpointsData` | Kpoint-set to use | no | ++-----------------+----------------------------------------------------+-----------------------------------------+----------+ +| options | :py:class:`~aiida.orm.Dict` | AiiDA options (computational resources) | no | ++-----------------+----------------------------------------------------+-----------------------------------------+----------+ + +Only the **fleur** input is required. However, it does not mean that it is enough to specify **fleur** +only. One *must* keep one of the supported input configurations described in the +:ref:`layout_banddos` section. - In the future we plan to add the possibility to converge a calculation before, and choose the kpaths automatic. - This version should be able start simply from a crystal structure. +Returns nodes +^^^^^^^^^^^^^ - Each of these workflows prepares/chances the Fleur input and manages one Fleur calculation. +The table below shows all the possible output nodes of the BandDos workchain. ++-------------------------+------------------------------------------------------+------------------------------------------------------+ +| name |type |comment | ++=========================+======================================================+======================================================+ +| output_banddos_wc_para |:py:class:`~aiida.orm.Dict` |results of the workchain | ++-------------------------+------------------------------------------------------+------------------------------------------------------+ +| last_calc_retrieved |:py:class:`~aiida.orm.FolderData` |Link to last FleurCalculation retrieved files | ++-------------------------+------------------------------------------------------+------------------------------------------------------+ +Workchain parameters and its defaults +..................................... -Input nodes: -^^^^^^^^^^^^ - * ``fleur`` (:py:class:`~aiida.orm.Code`): Fleur code using the ``fleur.fleur`` plugin - * ``wf_parameters`` (:py:class:`~aiida.orm.Dict`, optional): Some settings of the workflow behavior (e.g. number of kpoints, path, energy sampling and smearing, ...) - * ``fleurinp`` (:py:class:`~aiida_fleur.data.fleurinp.FleurinpData`, path 2): Fleur input data object representing the fleur input files. - * ``remote_data`` (:py:class:`~aiida.orm.RemoteData`, optional): The remote folder of the (converged) calculation whose output density is used as input for the DOS, or band structure run. +``wf_parameters`` +,,,,,,,,,,,,,,,,, - * ``options`` (:py:class:`~aiida.orm.Dict`, optional): All options available in AiiDA, i.e resource specification, queue name, extras scheduler commands, ... - * ``settings`` (:py:class:`~aiida.orm.Dict`, optional): special settings for Fleur calculations, will be given like it is through to calculationss. +``wf_parameters``: :py:class:`~aiida.orm.Dict` - Settings of the workflow behavior. All possible +keys and their defaults are listed below: + +.. literalinclude:: code/banddos_parameters.py + +**mode** is a string (either ``band``(default) or ``dos``). Determines, whether a bandstructure or density of states calculation is performed. This sets the ``band`` and ``dos`` switches in the ``output`` section of the input file accordingly. + +**kpath** is only used if ``mode='band'`` to determine the kpath to use. There are 5 different options here: + +* ``auto`` Will use the default bandpath in fleur for both Max4 or Max5. If ``klistname`` is given the corresponding kpoint path is used for Max5 version or later +* A `dictionary` specifying the special points and their coordinates. Only available for versions before Max5. Will generate a kpath with ``kpoints_number`` points +* ``seek`` will use :py:func:`~aiida.tools.data.array.kpoints.get_explicit_kpoints_path()` to generate a kpath with the given ``kpoints_distance``. + + .. warning:: + This functionality only works for standardized primitive unit cells. +* ``skip`` nothing is done +* all **other strings** are used to generate a k-path using :py:func:`~ase.dft.kpoints.bandpath()` for example ``GMKGALHA``. This option supports both ``kpoints_number`` and ``kpoints_distance`` for specifying the number of points + + +**kpoints_number** integer specifying the number of kpoints in the k-path (depending on the ``kpath`` option) + +**kpoints_distance** float specifying the distance between kpoints in the k-path (depending on the ``kpath`` option) + +**kpoints_explicit** dictionary, which is used to create a new kpointlist in the input. The dictionary is unpacked and used as the argument for the :py:func:`~aiida_fleur.data.fleurinpmodifier.FleurinpModifier.set_kpointlist()` function + +**klistname** str, will be used to switch the used `kPointList` for fleur versions after Max5 (if ``kpath='auto'`` or ``mode='dos'``) + +**sigma**, **emin**, **emax** floats specifying the energy grid for DOS calculations + +``options`` +,,,,,,,,,,, + +``options``: :py:class:`~aiida.orm.Dict` - AiiDA options (computational resources). +Example: + +.. code-block:: python + + 'resources': {"num_machines": 1, "num_mpiprocs_per_machine": 1}, + 'max_wallclock_seconds': 6*60*60, + 'queue_name': '', + 'custom_scheduler_commands': '', + 'import_sys_environment': False, + 'environment_variables': {} + +.. _layout_banddos: + +Supported input configurations +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The FleurBandDosWorkChain workchain has several +input combinations that implicitly define the workchain layout. Only **scf**, **fleurinp** and +**remote** nodes control the behaviour, other input nodes are truly optional. +Depending on the setup of the given inputs, one of three supported scenarios will happen: + +1. **scf**: + + SCF workchain will be submitted to converge the charge density which will + be followed by the bandsturcture or DOS calculation. Depending on the inputs given in the SCF + namespace, SCF will start from the structure or FleurinpData or will continue + converging from the given remote_data (see details in :ref:`SCF WorkChain`). + +2. **remote**: + + Files which belong to the **remote** will be used for the direct submission of the band/DOS + calculation. ``inp.xml`` file will be converted to FleurinpData and the charge density + will be used as the charge density used in this calculation. + +3. **remote** + **fleurinp**: + + Charge density which belongs to **remote** will be used as the charge density used in the + band/DOS calculation, however the ``inp.xml`` from the **remote** will be ignored. Instead, the given **fleurinp** will be used. + The aforementioned input files will be used for direct submission of the band/DOS + calculation. + +Other combinations of the input nodes **scf**, **fleurinp** and **remote** are forbidden. + +.. warning:: + + One *must* follow one of the supported input configurations. To protect a user from the + workchain misbehaviour, an error will be thrown if one specifies e.g. both **scf** and **remote** + inputs because in this case the intention of the user is not clear either he/she wants to + converge a new charge density or use the given one. -Returns nodes -^^^^^^^^^^^^^ - * ``output_dos_wc_para`` (:py:class:`~aiida.orm.Dict`): Information of the dos workflow results like success, last result node, list with convergence behavior - * ``output_band_wc_para`` (:py:class:`~aiida.orm.Dict`): Information node from the band workflow - * ``last_fleur_calc_output`` (:py:class:`~aiida.orm.Dict`) Output node of last Fleur calculation is returned. - -Layout -^^^^^^ - .. figure:: /images/Workchain_charts_dos_wc.png - :width: 50 % - :align: center Database Node graph ^^^^^^^^^^^^^^^^^^^ @@ -143,7 +236,7 @@ Plot_fleur visualization Example usage ^^^^^^^^^^^^^ - .. include:: ../../../../examples/tutorial/workflows/tutorial_submit_dos.py + .. include:: code/banddos_submission.py :literal: @@ -157,6 +250,28 @@ Output node example Error handling ^^^^^^^^^^^^^^ - Still has to be documented - - Warning if parent calculation was not converged. +In case of failure the Banddos WorkChain should throw one of the :ref:`exit codes`: + ++-----------+---------------------------------------------+ +| Exit code | Reason | ++===========+=============================================+ +| 230 | Invalid workchain parameters ,please | +| | check input configuration | ++-----------+---------------------------------------------+ +| 231 | Invalid input configuration | +| | and fleur code nodes | ++-----------+---------------------------------------------+ +| 233 | Invalid code node specified, check inpgen | +| | and fleur code nodes | ++-----------+---------------------------------------------+ +| 235 | Input file modification failed | ++-----------+---------------------------------------------+ +| 236 |Input file was corrupted after modifications| ++-----------+---------------------------------------------+ +| 334 | SCF calculation failed | ++-----------+---------------------------------------------+ +| 335 | Found no SCF remote repository. | ++-----------+---------------------------------------------+ + +If your workchain crashes and stops in *Excepted* state, please open a new issue on the Github page +and describe the details of the failure. diff --git a/docs/source/user_guide/workflows/initial_cls_wc.rst b/docs/source/user_guide/workflows/initial_cls_wc.rst index 1132464fc..6a0e406a9 100644 --- a/docs/source/user_guide/workflows/initial_cls_wc.rst +++ b/docs/source/user_guide/workflows/initial_cls_wc.rst @@ -9,7 +9,7 @@ Fleur initial core-level shifts workflow Class name, import from: :: - from aiida_fleur.workflows.initial_cls import fleur_initial_cls_wc + from aiida_fleur.workflows.initial_cls import FleurInitialCLSWorkChain #or WorkflowFactory('fleur.init_cls') diff --git a/docs/source/user_guide/workflows/wc_index.rst b/docs/source/user_guide/workflows/wc_index.rst index 54f136214..cb7f173ec 100644 --- a/docs/source/user_guide/workflows/wc_index.rst +++ b/docs/source/user_guide/workflows/wc_index.rst @@ -2,12 +2,12 @@ AiiDA-FLEUR WorkChains ====================== General design --------------- +++++++++++++++ All of the WorkChains have a similar interface and they share several common input nodes. Inputs -'''''' +------ There is always a ``wf_parameters``: :py:class:`~aiida.orm.Dict` node for controlling the workflow behavior. @@ -47,7 +47,7 @@ Input for the nested workchains has to be specified via a corresponding namespac refer to the documentation of a particular workchain to see the details. Outputs -''''''' +------- Most of the workchains return a workflow specific *ParameterData* (:py:class:`~aiida.orm.Dict`) node named ``output_name_wc_para`` or simple ``out`` which contains the main results and some information about the workchain. @@ -57,7 +57,7 @@ documentation of a particular workchain that you are interested in. Workchain classification ------------------------- +++++++++++++++++++++++++ .. image:: images/workchains2.png :width: 100% @@ -77,7 +77,7 @@ and their self-consistent analogs in the scientific workchains group. Inputs are *uuid*, *pk*, *workchain* nodes or *ParameterData* (workchain output) nodes. Basic (Technical) Workchains -'''''''''''''''''''''''''''' +++++++++++++++++++++++++++++ .. toctree:: :maxdepth: 2 @@ -90,32 +90,47 @@ Basic (Technical) Workchains ./dos_band_wc More advanced (Scientific) Workchains -''''''''''''''''''''''''''''''''''''' ++++++++++++++++++++++++++++++++++++++ + +Advanced workchains can be divided into categories according to subject. + +XPS workchains: .. toctree:: :maxdepth: 2 ./initial_cls_wc ./corehole_wc - ./create_magnetic_wc -Magnetic workchains -................... -Force-theorem subgroup -,,,,,,,,,,,,,,,,,,,,,, +Magnetic workchains: + +Magnetic workchains can be divided into two subgroups the Force-theorem subgroup and the self-consistent sub-group. + +The Force-theorem subgroup contains: + +- ssdisp_wc +- dmi_wc +- mae_wc + +The self-consistent sub-group contains: + +- ssdisp_conv_wc +- mae_conv_wc + .. toctree:: :maxdepth: 2 ./ssdisp_wc ./dmi_wc ./mae_wc + ./ssdisp_conv_wc + ./mae_conv_wc + +And other workflows like the create_magnetic_wc. -Self-consistent sub-group -,,,,,,,,,,,,,,,,,,,,,,,,, .. toctree:: :maxdepth: 2 - ./ssdisp_conv_wc - ./mae_conv_wc + ./create_magnetic_wc diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 000000000..0033cc6c9 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,19 @@ +# AiiDA-Fleur tutorials and examples + +# Tutorials + +For tutorials see: https://github.com/JuDFTteam/judft_tutorials + +# Examples: + +The files under /simple/ are probably outdated. +Under /submission/ you find some examples on how to submit workchains. These might also be outdated. +Better checkout the aiida-fleur documentation: https://aiida-fleur.readthedocs.io/en/develop/?badge=develop + +# Benchmarks: + +This was a MaX activity. Files are old. The whole thing is probably not used anymore + + +Note: The tutorial subfolder here while outdated is used throughout the documentation. +they should be updated diff --git a/examples/old_workflowtests/__init__.py b/examples/old_workflowtests/__init__.py deleted file mode 100644 index 8b4505153..000000000 --- a/examples/old_workflowtests/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# -*- coding: utf-8 -*- -''' -FLEUR base workflows -''' - -__copyright__ = u'Copyright (c), 2016, Forschungszentrum Juelich, Germany. All rights reserved.' -__license__ = 'MIT license, see LICENSE.txt file.' -__version__ = '0.1.0' -__contributors__ = 'Jens Broeder' -__paper__ = '' -__paper_short__ = '' diff --git a/examples/old_workflowtests/old/__init__.py b/examples/old_workflowtests/old/__init__.py deleted file mode 100644 index 8b4505153..000000000 --- a/examples/old_workflowtests/old/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# -*- coding: utf-8 -*- -''' -FLEUR base workflows -''' - -__copyright__ = u'Copyright (c), 2016, Forschungszentrum Juelich, Germany. All rights reserved.' -__license__ = 'MIT license, see LICENSE.txt file.' -__version__ = '0.1.0' -__contributors__ = 'Jens Broeder' -__paper__ = '' -__paper_short__ = '' diff --git a/examples/old_workflowtests/old/test_band.py b/examples/old_workflowtests/old/test_band.py deleted file mode 100644 index c6753705c..000000000 --- a/examples/old_workflowtests/old/test_band.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -This test runs the Fleur band workflow -""" -from __future__ import absolute_import -from aiida import load_dbenv, is_dbenv_loaded -if not is_dbenv_loaded(): - load_dbenv(profile='aiida_test') -from aiida.plugins import Code, DataFactory -from aiida.orm import load_node -#from aiida.work.run import run -from aiida_fleur.workflows.band import FleurBandWorkChain - -StructureData = DataFactory('structure') -ParameterData = DataFactory('parameter') -KpointsData = DataFactory('array.kpoints') -FleurinpData = DataFactory('fleur.fleurinp') - -############################### -# Set your values here -codename2 = 'fleur_iff@local_iff' #'fleur_iff003_v0_27@iff003' -codename2 = 'fleur_iff003_v0_27@iff003' -############################### - -code2 = Code.get_from_string(codename2) - -fleurinp = load_node(1684) -fleur_calc = load_node(1693) -remote = fleur_calc.out.remote_folder -wf_para = Dict(dict={'queue': 'th123_node'}) - -#res = band.run(fleurinp=fleurinp, remote=remote, fleur=code2) -res = FleurBandWorkChain.run(wf_parameters=wf_para, fleurinp=fleurinp, remote=remote, fleur=code2) diff --git a/examples/old_workflowtests/old/test_dos.py b/examples/old_workflowtests/old/test_dos.py deleted file mode 100644 index 7578bad4c..000000000 --- a/examples/old_workflowtests/old/test_dos.py +++ /dev/null @@ -1,35 +0,0 @@ -# -*- coding: utf-8 -*- -# /usr/bin/env python -""" -This test runs the Fleur dos workflow -""" -from __future__ import absolute_import -from aiidai_fleur.workflows.dos import fleur_dos_wc -from aiida.orm import load_node -from aiida.plugins import Code, DataFactory -from aiida import load_dbenv, is_dbenv_loaded -if not is_dbenv_loaded(): - load_dbenv(profile='aiida_test') -#from aiida.work.run import run - -StructureData = DataFactory('structure') -ParameterData = DataFactory('parameter') -KpointsData = DataFactory('array.kpoints') -FleurinpData = DataFactory('fleur.fleurinp') - -############################### -# Set your values here -codename = 'inpgen_iff@local_iff' # 'fleur_inpgen_mac' -codename2 = 'fleur_iff@local_iff' # 'fleur_iff003_v0_27@iff003' -############################### - -code = Code.get_from_string(codename) -code2 = Code.get_from_string(codename2) - -fleurinp = load_node(1684) -fleur_calc = load_node(1693) -remote = fleur_calc.out.remote_folder -#wf_para = ParameterData(dict={}) - -res = fleur_dos_wc.run(fleurinp=fleurinp, remote=remote, fleur=code2) -#res = dos.run(wf_parameters=wf_para, fleurinp=fleurinp, fleur_calc=remote, fleur=code2) diff --git a/examples/old_workflowtests/old/test_eos.py b/examples/old_workflowtests/old/test_eos.py deleted file mode 100644 index 07bb4bce5..000000000 --- a/examples/old_workflowtests/old/test_eos.py +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from __future__ import absolute_import -from __future__ import print_function -from aiida import load_dbenv, is_dbenv_loaded -if not is_dbenv_loaded(): - load_dbenv(profile='aiida_test') -from aiida.plugins import Code, DataFactory -from aiida.orm import load_node -from aiida_fleur.workflows.eos import FleurEosWorkChain -StructureData = DataFactory('structure') -ParameterData = DataFactory('parameter') -KpointsData = DataFactory('array.kpoints') -FleurinpData = DataFactory('fleur.fleurinp') - -############################### -# Set your values here -codename = 'inpgen_iff@local_iff' #'inpgen_mac_30_11_2016@local_mac' -codename2 = 'fleur_MPI_iff003_v0_27@iff003' -############################### - -code = Code.get_from_string(codename) -code2 = Code.get_from_string(codename2) - -from pprint import pprint -s = load_node(5937) #Ti -s = load_node(5955) #W -#s = load_node(5898) #Be2W -s = load_node(120) # Si - -f = s.get_formula() -#print s.get_formula() -parameters = load_node(139) -parameters = load_node(121) # Si -wf_para = Dict( - dict={ - 'fleur_runmax': 4, - 'points': 7, - 'step': 0.002, - 'guess': 1.00, - 'resources': { - 'num_machines': 1, - 'num_mpiprocs_per_machine': 12 - }, - 'walltime_sec': 60 * 60, - 'queue_name': 'th123_node' - }) - -print(('structure = {}'.format(f))) -print('wf-para =') #.format(wf_para.get_dict())) -pprint(wf_para.get_dict()) -print('parameterdata = ') #{}".format(parameters.get_dict())) -pprint(parameters.get_dict()) - -res = FleurEosWorkChain.run(wf_parameters=wf_para, structure=s, calc_parameters=parameters, inpgen=code, - fleur=code2) #, settings=settings)# diff --git a/examples/old_workflowtests/old/test_eos_old.py b/examples/old_workflowtests/old/test_eos_old.py deleted file mode 100644 index 36653b332..000000000 --- a/examples/old_workflowtests/old/test_eos_old.py +++ /dev/null @@ -1,166 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import print_function -import numpy as np -from matplotlib.backends import _macosx -import matplotlib.pyplot as pp - -from matplotlib.font_manager import FontProperties -from scipy.optimize import curve_fit -from pprint import pprint -from aiida import load_dbenv, is_dbenv_loaded -if not is_dbenv_loaded(): - load_dbenv() -from aiida.orm.querybuilder import QueryBuilder as QB -from aiida.plugins import Node, User, DataFactory, Calculation, Computer, Code -from aiida.orm import load_node - -from aiida.tools.codespecific.fleur.StructureData_util import eos_structures -from aiida.tools.codespecific.fleur.convergence import fleur_convergence -from aiida.tools.codespecific.fleur.calculate_lattice_con import lattice_constant -from aiida.tools.codespecific.fleur.convergence import fleur_convergence - -from aiida.workflows2.run import async, run - -ParameterData = DataFactory('parameter') - -W_bcc_id = 24513 #24423 -W_bcc_id2 = 24422 -W_fcc_id = 24 - -W_bcc = load_node(W_bcc_id) -print('StructureData used:\n{}'.format(W_bcc)) -print('cell: {}\n'.format(W_bcc.cell)) -print('sites: {}\n'.format(W_bcc.sites)) - -# create a Parameternode or load one from the DB -W_para_id = 24507 #without soc soc:24424 -W_para = load_node(W_para_id) - -print('ParamterNode used:') -pprint(W_para.get_dict()) - -############################### -# Set your values here -codename = 'inpgen_mac_25_10_2016' -computer_name = 'local_mac' -#computer_name = 'iff003' -codename2 = 'fleur_mac_v0_27' -#codename2 = 'fleur_iff003_v0_27@iff003' -#codename2 = 'fleur_MPI_iff003_v0_27@iff003' -points = 3 #9 -step = 0.002 -guess = 1.01 -wf_dict = {'fleur_runmax': 2, 'density_criterion': 0.0000001, 'points': points, 'step': step, 'guess': guess} -############################### - -code = Code.get_from_string(codename) -code2 = Code.get_from_string(codename2) -computer = Computer.objects.get(computer_name) -wf_para = Dict(dict=wf_dict) - -#res = run(lattice_constant, wf_parameters=wf_para, structure=W_bcc, -# calc_parameters=W_para, inpgen = code, fleur=code2) - -#print res -''' -# other tests: -yfit_all = [] -def func(x, a, b, c): - return a*x**2 + b*x + c - -def fit_latticeconstant(scale, eT): - """ - """ - import numpy as np - # call fitt pol2 # or something else - #def func(x, a, b, c): - # return a*x**2 + b*x + c - f1 = np.polyfit(scale,eT,2) - a0 = f1[0] - a1 = f1[1] - a2 = f1[2] - la = -0.5*a1/a0 - c = a2 - a1**2/a2 - return a0,la,c, f1 - -scale = [-1, 0, 1, 2, 3] -eT = [1.02, 0.03, 1.03, 4.02, 9.02] - -a0,la,c, f1 = fit_latticeconstant(scale, eT) - -print a0, la, c, f1 -scaleAll.append(scale) -eT1_all.append(eT) -yfit = func(scale,a0,a1,a2) -yfit_all.append(yfit) - -#### plot - -nfiles = 1 -save = False -labela = ['bla', 'blub'] -# plot 1 -pl = [] -a = 1 - -fig = pp.figure() -ax = fig.add_subplot(111) -#pp.set_grid() -ax.set_title('Total Energy vs lattice constant', fontsize=16, alpha=a, ha='center') -ax.set_xlabel('Lattice Constant [a/3.16]', fontsize=15) -ax.set_ylabel('Total engery [htr]', fontsize=15) -ax.yaxis.set_tick_params(size = 4.0, width = 1.0, labelsize =14, length = 5) -ax.xaxis.set_tick_params(size = 4.0, width = 1.0, labelsize =14, length = 5) -#ax.ticklabel_format(style='sci', axis='x', scilimits=(4,4)) -#ax.ticklabel_format(style='sci', axis='y', scilimits=(4,4)) -ax.yaxis.get_major_formatter().set_powerlimits((0, 3)) -ax.yaxis.get_major_formatter().set_useOffset(False) - -for i in range(0,nfiles): - p1 = pp.plot(scaleAll[i],eT1_all[i], 's-', label = labela[i], linewidth = 2.0, markersize = 4.0) - pl.append(p1) - -pp.legend(bbox_to_anchor=(0.85, 1), loc=2, borderaxespad=0., fancybox=True)#loc='best', fancybox=True) #, framealpha=0.5) #loc='upper right') - -if save: - pp.savefig('Et_la_kpts.pdf', format='pdf') -''' -''' -# plot 2 - -fig1 = pp.figure() -ax1 = fig1.add_subplot(111) -#pp.set_grid() -ax1.set_title('Lattice Constant vs Nkpts', fontsize=16, alpha=a, ha='center') -ax1.set_xlabel('Nkpts', fontsize=15) -ax1.set_ylabel('Lattice Constant [a/3.16]', fontsize=15) -ax1.yaxis.set_tick_params(size = 4.0, width = 1.0, labelsize =14, length = 5) -ax1.xaxis.set_tick_params(size = 4.0, width = 1.0, labelsize =14, length = 5) -ax1.yaxis.get_major_formatter().set_powerlimits((0, 3)) -ax1.yaxis.get_major_formatter().set_useOffset(False) -pl1 = pp.plot(xnkpts, a1_all, 's', label = '', linewidth = 2.0, markersize = 4.0) - -if save: - pp.savefig('La_kpts.pdf', format='pdf') - -# plot 3 - -fig2 = pp.figure() -ax2 = fig2.add_subplot(111) -#pp.set_grid() -ax2.set_title('Total Energy vs Nkpts', fontsize=16, alpha=a, ha='center') -ax2.set_xlabel('Nkpts', fontsize=15) -ax2.set_ylabel('Minimum Total Engery [htr]', fontsize=15) -ax2.yaxis.set_tick_params(size = 4.0, width = 1.0, labelsize =14, length = 5) -ax2.xaxis.set_tick_params(size = 4.0, width = 1.0, labelsize =14, length = 5) -ax2.yaxis.get_major_formatter().set_powerlimits((0, 3)) -ax2.yaxis.get_major_formatter().set_useOffset(False) -pl2 = pp.plot(xnkpts, a2_allc, 's', label = '', linewidth = 2.0, markersize = 4.0) - - -if save: - pp.savefig('Et_kpts.pdf', format='pdf') -''' -#pp.show() diff --git a/examples/old_workflowtests/old/test_relaxation.py b/examples/old_workflowtests/old/test_relaxation.py deleted file mode 100644 index 2cdfb3570..000000000 --- a/examples/old_workflowtests/old/test_relaxation.py +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import print_function -from aiida import load_dbenv, is_dbenv_loaded -if not is_dbenv_loaded(): - load_dbenv() -import sys, os -from aiida.plugins import Code, DataFactory -from aiida.orm import Computer -from aiida.orm import load_node -from aiida.engine import async, run -from aiida_fleur.calculation.fleurinputgen import FleurinputgenCalculation -from aiida_fleur.calculation.fleur import FleurCalculation -from aiida_fleur.workflows.relax import fleur_relax_wc - -StructureData = DataFactory('structure') -ParameterData = DataFactory('parameter') -KpointsData = DataFactory('array.kpoints') -FleurinpData = DataFactory('fleur.fleurinp') - -############################### -# Set your values here -codename = 'fleur_inpgen_mac' -computer_name = 'local_mac' -#computer_name = 'iff003' -#codename2 = 'fleur_v0.27@iff003' -codename2 = 'fleur_iff003_v0_27@iff003' - -#codename2 = 'fleur_mac' - -############################### - -code = Code.get_from_string(codename) -code2 = Code.get_from_string(codename2) -#JobCalc = FleurinputgenCalculation.process() -computer = Computer.objects.get(computer_name) - -s = load_node(14204) #13586)#137)# Be13586, W 137 -print(s.sites) -#print s.cell -parameters = load_node(13496) # Be 13496, W 13161 -wf_para = Dict(dict={'relax_runmax': 5, 'density_criterion': 0.0000001, 'max_force_cycle': 9}) - -res = fleur_relax_wc.run(wf_parameters=wf_para, structure=s, calc_parameters=parameters, inpgen=code, - fleur=code2) #, computer=computer) diff --git a/examples/old_workflowtests/old/test_scf.py b/examples/old_workflowtests/old/test_scf.py deleted file mode 100644 index 580ea778d..000000000 --- a/examples/old_workflowtests/old/test_scf.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -This test runs the fleur_convergence workflow for path 1 -""" -#TODO: overall tests, should create the nodes they use in the db. -from __future__ import absolute_import -from aiida import load_dbenv, is_dbenv_loaded -if not is_dbenv_loaded(): - load_dbenv(profile='aiida_test') -from aiida.plugins import Code, DataFactory -from aiida.orm import load_node -from aiida_fleur.workflows.scf import FleurScfWorkChain - -StructureData = DataFactory('structure') -ParameterData = DataFactory('parameter') -KpointsData = DataFactory('array.kpoints') -#FleurinpData = DataFactory('fleurinp.fleurinp') -FleurinpData = DataFactory('fleur.fleurinp') - -############################### -# Set your values here -codename = 'inpgen_iff_0.28@local_iff' #'inpgen_iff@local_iff'#'inpgen_mac_30_11_2016@local_mac' -#codename2 = 'fleur_iff@local_iff'#'fleur_mac_v0_27@local_mac' -#codename = 'fleur_inpgen_iff003@iff003'#'inpgen_mac_30_11_2016@local_mac' -#codename2 = 'fleur_iff003_v0_27@iff003'#fleur_iff@iff003'#'fleur_mac_v0_27@local_mac' -codename2 = 'fleur_iff_0.28@local_iff' #'fleur_MPI_iff003_v0_27@iff003' -############################### - -code = Code.get_from_string(codename) -code2 = Code.get_from_string(codename2) - -s = load_node(138) - -parameters = Dict(dict={}) - -settings = Dict( - dict={ - 'files_to_retrieve': [], - 'files_not_to_retrieve': [], - 'files_copy_remotely': [], - 'files_not_copy_remotely': [], - 'commandline_options': ['-wtime', '30'], - 'blaha': ['bla'] - }) - -wf_para = Dict( - dict={ - 'fleur_runmax': 4, - 'density_criterion': 0.000001, #}) - 'queue_name': 'th123_node', - 'resources': { - 'num_machines': 1, - 'num_mpiprocs_per_machine': 12 - }, - 'walltime_sec': 10 * 60 - }) - -res = FleurScfWorkChain.run( - wf_parameters=wf_para, - structure=s, - #calc_parameters=parameters, - inpgen=code, - fleur=code2) #, settings=settings)# -''' -code = Code.get_from_string('inpgen_mac_25_10_2016') -code2 = Code.get_from_string('fleur_mac_v0_27') -computer = Computer.get('local_mac') -wf_para = ParameterData(dict={'fleur_runmax' : 4, 'density_criterion' : 0.0000001})#, 'queue' : 'th1'}) -res = run(fleur_convergence, wf_parameters=wf_para, structure=load_node(24422), - calc_parameters=load_node(24507), inpgen = code, fleur=code2) -''' diff --git a/examples/old_workflowtests/old/test_scf2.py b/examples/old_workflowtests/old/test_scf2.py deleted file mode 100644 index dbd729a45..000000000 --- a/examples/old_workflowtests/old/test_scf2.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -This test runs the fleur_convergence workflow for path 2 -""" -#TODO: overall tests, should create the nodes they use in the db. - -from __future__ import absolute_import -from aiida import load_dbenv, is_dbenv_loaded -if not is_dbenv_loaded(): - load_dbenv() -from aiida.plugins import Code, DataFactory -from aiida.orm import load_node -#from aiida.work.run import async, run -from aiida_fleur.workflows.scf import FleurScfWorkChain - -StructureData = DataFactory('structure') -ParameterData = DataFactory('parameter') -KpointsData = DataFactory('array.kpoints') -#FleurinpData = DataFactory('fleurinp.fleurinp') -FleurinpData = DataFactory('fleur.fleurinp') - -############################### -# Set your values here -codename = 'inpgen_iff@local_iff' #'inpgen_mac_30_11_2016@local_mac' -codename2 = 'fleur_iff@local_iff' #'fleur_mac_v0_27@local_mac' -############################### - -code = Code.get_from_string(codename) -code2 = Code.get_from_string(codename2) - -wf_para = Dict( - dict={ - 'relax_runmax': 4, - 'density_criterion': 0.0000002, - 'energy_criterion': 0.0005, - 'converge_energy': True, - 'converge_density': True - }) -fleurinp = load_node(1339) -remote = load_node(1353) -wf_para = load_node(1333) - -res = FleurScfWorkChain.run(wf_parameters=wf_para, fleurinp=fleurinp, remote_data=remote, fleur=code2) diff --git a/examples/old_workflowtests/test_inital_state.py b/examples/old_workflowtests/test_inital_state.py deleted file mode 100644 index 2bcc632f5..000000000 --- a/examples/old_workflowtests/test_inital_state.py +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -This test runs the initial stae CLS workflow -""" -#TODO: overall tests, should create the nodes they use in the db. -from __future__ import absolute_import -from __future__ import print_function -from aiida import load_dbenv, is_dbenv_loaded -if not is_dbenv_loaded(): - load_dbenv() - #load_dbenv(profile='aiida_test') -from aiida.plugins import Code, DataFactory -from aiida.orm import load_node -from aiida_fleur.workflows.initial_cls import fleur_initial_cls_wc - -StructureData = DataFactory('structure') -ParameterData = DataFactory('parameter') -KpointsData = DataFactory('array.kpoints') -#FleurinpData = DataFactory('fleurinp.fleurinp') -FleurinpData = DataFactory('fleur.fleurinp') - -############################### -# Set your values here -codename = 'inpgen_iff@local_iff' #'inpgen_mac_30_11_2016@local_mac' -#codename2 = 'fleur_iff@local_iff'#'fleur_mac_v0_27@local_mac' -#codename = 'fleur_inpgen_iff003@iff003'#'inpgen_mac_30_11_2016@local_mac' -#codename2 = 'fleur_iff003_v0_27@iff003'#fleur_iff@iff003'#'fleur_mac_v0_27@local_mac' -codename2 = 'fleur_MPI_iff003_v0_27@iff003' -############################### - -code = Code.get_from_string(codename) -code2 = Code.get_from_string(codename2) - -resources = {'num_machines': 1, 'num_mpiprocs_per_machine': 12} - -s = load_node(5898) # Be2W - -s1 = load_node(5955) #W -s2 = load_node(3100) #Be - -references = {'use': {'Be': s2.uuid, 'W': s1.uuid}} -print(references) -parameters = Dict(dict={}) -parameters = Dict(dict={}) - -wf_para = Dict( - dict={ - 'fleur_runmax': 4, - 'density_criterion': 0.000002, #}) - 'queue_name': 'th123_node', - 'resources': resources, - 'walltime_sec': 10 * 60, - 'serial': False, - 'references': references - }) - -res = fleur_initial_cls_wc.run(structure=s, wf_parameters=wf_para, inpgen=code, fleur=code2) # -#wf_parameters=wf_para, -''' - _default_wf_para = {'references' : {'calculate' : 'all'}, - 'calculate_doses' : False, - 'relax' : True, - 'relax_mode': 'QE Fleur', - 'relax_para' : 'default', - 'scf_para' : 'default', - 'dos_para' : 'default', - 'same_para' : True, - 'resources' : {"num_machines": 1}, - 'walltime_sec' : 10*30, - 'queue' : None, - 'serial' : False} - - spec.input("wf_parameters", valid_type=ParameterData, required=False, - default=ParameterData(dict=self._default_wf_para))#get_defaut_wf_para()))# - spec.input("fleurinp", valid_type=FleurinpData, required=False) - spec.input("fleur", valid_type=Code, required=True) - spec.input("inpgen", valid_type=Code, required=False) - spec.input("structure", valid_type=StructureData, required=False) - spec.input("calc_parameters", valid_type=ParameterData, required=False) -''' diff --git a/examples/old_workflowtests/test_run_scf2.py b/examples/old_workflowtests/test_run_scf2.py deleted file mode 100644 index 9f305e9d8..000000000 --- a/examples/old_workflowtests/test_run_scf2.py +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env runaiida -# -*- coding: utf-8 -*- - -from __future__ import absolute_import -from __future__ import print_function -__copyright__ = (u'Copyright (c), 2016, Forschungszentrum Jülich GmbH, ' 'IAS-1/PGI-1, Germany. All rights reserved.') -__license__ = 'MIT license, see LICENSE.txt file' -__version__ = '0.27' -__contributors__ = 'Jens Broeder' - -from aiida import load_dbenv, is_dbenv_loaded -if not is_dbenv_loaded(): - load_dbenv() - -import sys -import os - -from aiida.common.example_helpers import test_and_get_code -from aiida.plugins import DataFactory -from aiida_fleur.workflows.scf import FleurScfWorkChain - -# If set to True, will ask AiiDA to run in serial mode (i.e., AiiDA will not -# invoke the mpirun command in the submission script) -run_in_serial_mode = True #False -queue = None - -################################################################ -ParameterData = DataFactory('parameter') -FleurinpData = DataFactory('fleur.fleurinp') - -try: - dontsend = sys.argv[1] - if dontsend == '--dont-send': - submit_test = True - elif dontsend == '--send': - submit_test = False - else: - raise IndexError -except IndexError: - print(('The first parameter can only be either ' '--send or --dont-send'), file=sys.stderr) - sys.exit(1) - -try: - codename = sys.argv[2] -except IndexError: - codename = None - -try: - queue = sys.argv[3] -except IndexError: - queue = None - -##### - -code = test_and_get_code(codename, expected_code_type='fleur.fleur') - -# get where tests folder is, then relative path -inpxmlfile = '/usr/users/iff_th1/broeder/aiida/github/aiida-fleur/tests/inp_xml_files/W/inp.xml' -fleurinp = FleurinpData(files=[inpxmlfile]) - -wf_para = Dict( - dict={ - 'fleur_runmax': 4, - 'density_criterion': 0.000001, #}) - 'queue_name': 'th123_node', - 'resources': { - 'num_machines': 1, - 'num_mpiprocs_per_machine': 12 - }, - 'walltime_sec': 10 * 60, - 'serial': run_in_serial_mode - }) - -if submit_test: - print('workchain do not have so far a submit_test function') -else: - print('Running fleur_scf_wc') - res = FleurScfWorkChain.run(wf_parameters=wf_para, fleurinp=fleurinp, fleur=code) - #remote_data= remote, fleur=code) diff --git a/examples/old_workflowtests/test_submit_inpgen.py b/examples/old_workflowtests/test_submit_inpgen.py deleted file mode 100644 index 1e6bf77b9..000000000 --- a/examples/old_workflowtests/test_submit_inpgen.py +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/env runaiida -# -*- coding: utf-8 -*- - -from __future__ import absolute_import -from __future__ import print_function -__copyright__ = (u'Copyright (c), 2016, Forschungszentrum Jülich GmbH, ' 'IAS-1/PGI-1, Germany. All rights reserved.') -__license__ = 'MIT license, see LICENSE.txt file' -__version__ = '0.27' -__contributors__ = 'Jens Broeder' - -from aiida import load_dbenv, is_dbenv_loaded -if not is_dbenv_loaded(): - load_dbenv() - -import sys -import os - -from aiida.common.example_helpers import test_and_get_code -from aiida.plugins import DataFactory, CalculationFactory -from aiida_fleur.calculation.fleurinputgen import FleurinputgenCalculation as calc - -################################################################ - -ParameterData = DataFactory('parameter') -StructureData = DataFactory('structure') -try: - dontsend = sys.argv[1] - if dontsend == '--dont-send': - submit_test = True - elif dontsend == '--send': - submit_test = False - else: - raise IndexError -except IndexError: - print(('The first parameter can only be either ' '--send or --dont-send'), file=sys.stderr) - sys.exit(1) - -try: - codename = sys.argv[2] -except IndexError: - codename = None - -queue = None -# queue = "th1_small" -settings = None -##### - -code = test_and_get_code(codename, expected_code_type='fleur.inpgen') - -# W bcc structure -bohr_a_0 = 0.52917721092 # A -a = 3.013812049196 * bohr_a_0 -cell = [[-a, a, a], [a, -a, a], [a, a, -a]] -s = StructureData(cell=cell) -s.append_atom(position=(0., 0., 0.), symbols='W') -parameters = Dict( - dict={ - 'atom': { - 'element': 'W', - 'jri': 833, - 'rmt': 2.3, - 'dx': 0.015, - 'lmax': 8, - 'lo': '5p', - 'econfig': '[Kr] 5s2 4d10 4f14| 5p6 5d4 6s2', - }, - 'comp': { - 'kmax': 3.5, - 'gmax': 2.9, - }, - 'kpt': { - 'nkpt': 200, - } - }) - -#elements = list(s.get_symbols_set()) - -## For remote codes, it is not necessary to manually set the computer, -## since it is set automatically by new_calc -#computer = code.get_remote_computer() -#calc = code.new_calc(computer=computer) - -calc = code.new_calc() -#calc = CalculationFactory('fleur.inpgen') -print(calc, type(calc)) -calc.label = 'Test Fleur inpgen' -calc.description = 'Test calculation of the Fleur input generator' -calc.set_max_wallclock_seconds(300) # 5 min -# Valid only for Slurm and PBS (using default values for the -# number_cpus_per_machine), change for SGE-like schedulers -calc.set_resources({'num_machines': 1}) -calc.set_withmpi(False) -calc.use_code(code) -## Otherwise, to specify a given # of cpus per machine, uncomment the following: -# calc.set_resources({"num_machines": 1, "num_mpiprocs_per_machine": 8}) -calc.set_resources({'tot_num_mpiprocs': 1}) - -#calc.set_custom_scheduler_commands("#SBATCH --account=ch3") -calc.set_custom_scheduler_commands('#BSUB -P jara0043 \n#BSUB -x') - -if queue is not None: - calc.set_queue_name(queue) - -calc.use_structure(s) -calc.use_parameters(parameters) - -if settings is not None: - calc.use_settings(settings) - -if submit_test: - subfolder, script_filename = calc.submit_test() - print("Test_submit for calculation (uuid='{}')".format(calc.uuid)) - print('Submit file in {}'.format(os.path.join(os.path.relpath(subfolder.abspath), script_filename))) -else: - calc.store_all() - print("created calculation; calc=Calculation(uuid='{}') # ID={}".format(calc.uuid, calc.dbnode.pk)) - calc.submit() - print("submitted calculation; calc=Calculation(uuid='{}') # ID={}".format(calc.uuid, calc.dbnode.pk)) diff --git a/examples/submission/workflow_tests/test_submit_scf.py b/examples/submission/workflow_tests/test_submit_scf.py index 597443afa..e73dd9a98 100644 --- a/examples/submission/workflow_tests/test_submit_scf.py +++ b/examples/submission/workflow_tests/test_submit_scf.py @@ -10,7 +10,7 @@ # http://aiida-fleur.readthedocs.io/en/develop/ # ############################################################################### """ -Here we run the FleurScfWorkChain for Si or some other material +Here we run the FleurScfWorkChain """ # pylint: disable=invalid-name from __future__ import absolute_import diff --git a/setup.json b/setup.json index 6c18a9dfb..54b941a34 100644 --- a/setup.json +++ b/setup.json @@ -1,5 +1,5 @@ { - "version": "1.1.4", + "version": "1.2.0", "name": "aiida-fleur", "url": "https://github.com/JuDFTteam/aiida-fleur", "license": "MIT License, see LICENSE.txt file.", @@ -33,7 +33,7 @@ "lxml>=3.6.4", "numpy~=1.16,>=1.16.4", "sympy", - "masci-tools>=0.3.12-dev4,<0.4.0", + "masci-tools >= 0.4.8", "future", "pyhull", "sqlalchemy<1.4" @@ -45,7 +45,8 @@ "docs": [ "Sphinx", "docutils", - "sphinx_rtd_theme" + "sphinx_rtd_theme", + "sphinx-click" ], "pre-commit": [ "pre-commit>=2.6.0", @@ -77,9 +78,10 @@ "fleur.scf = aiida_fleur.workflows.scf:FleurScfWorkChain", "fleur.dos = aiida_fleur.workflows.dos:fleur_dos_wc", "fleur.banddos = aiida_fleur.workflows.banddos:FleurBandDosWorkChain", - "fleur.eos = aiida_fleur.workflows.eos:FleurEosWorkChain", - "fleur.init_cls = aiida_fleur.workflows.initial_cls:fleur_initial_cls_wc", - "fleur.corehole = aiida_fleur.workflows.corehole:fleur_corehole_wc", + "fleur.strain = aiida_fleur.workflows.strain:FleurStrainWorkChain", + "fleur.eos = aiida_fleur.workflows.eos:FleurEosWorkChain", + "fleur.init_cls = aiida_fleur.workflows.initial_cls:FleurInitialCLSWorkChain", + "fleur.corehole = aiida_fleur.workflows.corehole:FleurCoreholeWorkChain", "fleur.mae = aiida_fleur.workflows.mae:FleurMaeWorkChain", "fleur.mae_conv = aiida_fleur.workflows.mae_conv:FleurMaeConvWorkChain", "fleur.ssdisp = aiida_fleur.workflows.ssdisp:FleurSSDispWorkChain", diff --git a/tests/calculation/test_inpgen.py b/tests/calculation/test_inpgen.py index e7a27fc40..6f31e6ac7 100644 --- a/tests/calculation/test_inpgen.py +++ b/tests/calculation/test_inpgen.py @@ -197,3 +197,72 @@ def test_FleurinpgenJobCalc_full_mock(aiida_profile, mock_code_factory, generate print((res['remote_folder'].list_objects())) print((res['retrieved'].list_objects())) assert bool(node.is_finished_ok) + + +def test_x_and_bunchatom_input( + aiida_profile, + fixture_sandbox, + generate_calc_job, + fixture_code, +): + """Test that plugin can deal (ignores) with other StructureData features + + Currently we assume atoms, deal with vacancies, i.e we leave them out + ignore the x element. This is important for interoperability with kkr + + # TODO often we do natoms= len(n.sites), which would be false in the case of vacancies. + """ + from aiida.orm import StructureData + + struc_Fe7Nb = StructureData() + struc_Fe7Nb.cell = [[3.3168796764431, 0.0, 0.0], [1.6584398382215, 2.3453881115923, 0.0], + [0.0, 0.0, 13.349076054836]] + struc_Fe7Nb.pbc = (True, True, False) + elements = ['X', 'X', 'X', 'Fe', 'Nb', 'Nb'] + positions = [[0.0, 0.0, 1.1726940557829], [1.6584398382215, 0.0, 3.5180821673487], [0.0, 0.0, 5.8634702789145], + [1.6584398382215, 0.0, 8.2088583904803], [0.0, 0.0, 10.096376717551], + [1.6584398382215, 0.0, 12.46832205832]] + for el, pos in zip(elements, positions): + struc_Fe7Nb.append_atom(symbols=[el], position=pos) + + entry_point_name = 'fleur.inpgen' + + parameters = {} + + inputs = { + 'code': fixture_code(entry_point_name), + 'structure': struc_Fe7Nb, + 'metadata': { + 'options': { + 'resources': { + 'num_machines': 1 + }, + 'max_wallclock_seconds': int(100), + 'withmpi': False + } + } + } + + calc_info = generate_calc_job(fixture_sandbox, entry_point_name, inputs) + codes_info = calc_info.codes_info + cmdline_params = ['+all', '-explicit', '-f', 'aiida.in'] + local_copy_list = [] + retrieve_list = ['inp.xml', 'out', 'shell.out', 'out.error', 'struct.xsf', 'aiida.in'] + retrieve_temporary_list = [] + + # Check the attributes of the returned `CalcInfo` + assert isinstance(calc_info, datastructures.CalcInfo) + assert sorted(calc_info.retrieve_list) == sorted(retrieve_list) + + with fixture_sandbox.open('aiida.in') as handle: + input_written = handle.read() + + print(input_written) + assert ' 3\n' in input_written # test for natoms + + # Test not none of the vacany elements was written into the input file + for line in input_written.split('\n'): + assert 'X' not in line + assert ' 0 ' not in line + + # todo weights, an molecules on site diff --git a/tests/conftest.py b/tests/conftest.py index 74ec02f82..d49adcf9f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -246,8 +246,10 @@ def create_fleurinp(): from aiida.plugins import DataFactory fleurinp = DataFactory('fleur.fleurinp') - def _make_fleurinp(inpxmlfilepath): - return fleurinp(files=[inpxmlfilepath]) + def _make_fleurinp(inpxmlfilepath, additional_files=None): + if additional_files is None: + additional_files = [] + return fleurinp(files=[inpxmlfilepath] + additional_files) return _make_fleurinp @@ -256,15 +258,33 @@ def _make_fleurinp(inpxmlfilepath): def inpxml_etree(): """Returns the etree generator""" - def _get_etree(path): + def _get_etree(path, return_schema=False): from lxml import etree + from masci_tools.io.parsers.fleur.fleur_schema import InputSchemaDict with open(path, 'r') as inpxmlfile: tree = etree.parse(inpxmlfile) - return tree + version = tree.getroot().attrib['fleurInputVersion'] + schema_dict = InputSchemaDict.fromVersion(version) + if return_schema: + return tree, schema_dict + else: + return tree return _get_etree +@pytest.fixture +def eval_xpath(): + """Return the eval_xpath function""" + + def _eval_xpath(node, xpath, list_return=False): + from masci_tools.util.xml.common_functions import eval_xpath + + return eval_xpath(node, xpath, list_return=list_return) + + return _eval_xpath + + @pytest.fixture def generate_work_chain_node(): """Fixture to generate a mock `WorkChainNode` for testing parsers.""" diff --git a/tests/data/test_fleurinp.py b/tests/data/test_fleurinp.py index 0f6ec26bb..9e50ee27e 100644 --- a/tests/data/test_fleurinp.py +++ b/tests/data/test_fleurinp.py @@ -36,6 +36,12 @@ if file.endswith('.xml'): inpxmlfilelist2.append(os.path.join(subdir, file)) +INPXML_LATNAM_DEFINITION = [ + 'Fe_bct_SOCXML', 'CuBulkXML', 'CuBandXML', 'Bi2Te3XML', 'PTO-SOCXML', 'Fe_bctXML', 'Fe_1lXML', 'Fe_1l_SOCXML', + 'Fe_bct_LOXML', 'NiO_ldauXML', 'CuDOSXML', 'PTOXML' +] +INPXML_NO_KPOINTLISTS = ['GaAsMultiForceXML'] + @pytest.mark.parametrize('inpxmlfilepath', inpxmlfilelist) def test_fleurinp_valid_inpxml(create_fleurinp, inpxmlfilepath): @@ -44,9 +50,8 @@ def test_fleurinp_valid_inpxml(create_fleurinp, inpxmlfilepath): """ fleurinp_tmp = create_fleurinp(inpxmlfilepath) - assert fleurinp_tmp._has_schema - assert fleurinp_tmp._schema_file_path is not None assert fleurinp_tmp.inp_dict != {} + assert fleurinp_tmp.parser_info['parser_warnings'] == [] assert fleurinp_tmp._validate() is None # if fails, _validate throws an error @@ -76,14 +81,20 @@ def test_fleurinp_kpointsdata_extraction(create_fleurinp, inpxmlfilepath): from aiida.orm import KpointsData fleurinp_tmp = create_fleurinp(inpxmlfilepath) - kptsd = fleurinp_tmp.get_kpointsdata_ncf() - if kptsd is not None: - assert isinstance(kptsd, KpointsData) + if any(folder in inpxmlfilepath for folder in INPXML_LATNAM_DEFINITION): + with pytest.raises(ValueError, match='Could not extract Bravais matrix out of inp.xml.'): + fleurinp_tmp.get_kpointsdata_ncf() + elif any(folder in inpxmlfilepath for folder in INPXML_NO_KPOINTLISTS): + with pytest.raises(ValueError, match='No Kpoint lists found in the given inp.xml'): + fleurinp_tmp.get_kpointsdata_ncf() else: - pass - # What todo here, may test inpxml are with latnam definded, which does not work here. - # or without a kpoint list. Therefore this test might let two much through + kptsd = fleurinp_tmp.get_kpointsdata_ncf() + + assert isinstance(kptsd, (KpointsData, dict)) + + if isinstance(kptsd, dict): + assert all(isinstance(val, KpointsData) for val in kptsd.values()) @pytest.mark.parametrize('inpxmlfilepath', inpxmlfilelist) @@ -111,12 +122,14 @@ def test_fleurinp_structuredata_extraction(create_fleurinp, inpxmlfilepath): from aiida.orm import StructureData fleurinp_tmp = create_fleurinp(inpxmlfilepath) - struc = fleurinp_tmp.get_structuredata_ncf() + if any(folder in inpxmlfilepath for folder in INPXML_LATNAM_DEFINITION): + with pytest.raises(ValueError, match='Could not extract Bravais matrix out of inp.xml.'): + fleurinp_tmp.get_structuredata_ncf() + else: + struc = fleurinp_tmp.get_structuredata_ncf() - if struc is not None: assert isinstance(struc, StructureData) - else: - pass + # # What todo here, may test inpxml are with latnam definded, # # which does not work here. # # But if something else fails also None return. T @@ -148,6 +161,63 @@ def test_fleurinp_single_value_modification(create_fleurinp, inpxmlfilepath): def test_get_tag(create_fleurinp, inpxmlfilepath): fleurinp_tmp = create_fleurinp(inpxmlfilepath) - tag = fleurinp_tmp.get_tag('/fleurInput/atomSpecies/species') + with pytest.deprecated_call(): + tag = fleurinp_tmp.get_tag('/fleurInput/atomSpecies/species') assert tag != [] + + +folderlist = [ + os.path.abspath(os.path.join(inpxmlfilefolder, '../files/inpxml/Fe_1l_SOCXML/files')), + os.path.abspath(os.path.join(inpxmlfilefolder, '../files/inpxml/FePt')), + os.path.abspath(os.path.join(inpxmlfilefolder, '../files/included_xml_files')), + os.path.abspath(os.path.join(inpxmlfilefolder, '../parsers/fixtures/fleur/default')), + os.path.abspath(os.path.join(inpxmlfilefolder, '../parsers/fixtures/fleur/relax')) +] + +expected_files_list = [{'inp.xml'}, {'inp.xml'}, {'inp.xml', 'kpts.xml', 'sym.xml'}, {'inp.xml'}, + {'inp.xml', 'relax.xml'}] + + +@pytest.mark.parametrize('folderpath,expected_files', zip(folderlist, expected_files_list)) +def test_get_fleurinp_from_folder_data(folderpath, expected_files): + from aiida import orm + from aiida_fleur.data.fleurinp import get_fleurinp_from_folder_data + + folder = orm.FolderData() + folder.put_object_from_tree(folderpath) + + fleurinp = get_fleurinp_from_folder_data(folder) + + assert set(fleurinp.files) == expected_files + + +test_names = ['default', 'relax'] +fleur_expected_files = [{'inp.xml'}, {'inp.xml', 'relax.xml'}] + + +@pytest.mark.parametrize('parser_test_name,expected_files', zip(test_names, fleur_expected_files)) +def test_get_fleurinp_from_remote_data_fleur(fixture_localhost, generate_calc_job_node, parser_test_name, + expected_files): + from aiida_fleur.data.fleurinp import get_fleurinp_from_remote_data + + entry_point_calc_job = 'fleur.fleur' + + node = generate_calc_job_node(entry_point_calc_job, fixture_localhost, parser_test_name, store=True) + + fleurinp = get_fleurinp_from_remote_data(node.outputs.remote_folder) + + assert set(fleurinp.files) == expected_files + + +def test_get_fleurinp_from_remote_data_inpgen(fixture_localhost, generate_calc_job_node): + from aiida_fleur.data.fleurinp import get_fleurinp_from_remote_data + + name = 'default' + entry_point_calc_job = 'fleur.inpgen' + + node = generate_calc_job_node(entry_point_calc_job, fixture_localhost, name, store=True) + + fleurinp = get_fleurinp_from_remote_data(node.outputs.remote_folder) + + assert set(fleurinp.files) == {'inp.xml'} diff --git a/tests/data/test_fleurinpmodifier.py b/tests/data/test_fleurinpmodifier.py index 270884d7b..07363f5db 100644 --- a/tests/data/test_fleurinpmodifier.py +++ b/tests/data/test_fleurinpmodifier.py @@ -25,6 +25,7 @@ def test_fleurinp_modifier1(create_fleurinp): """Tests if fleurinp_modifier with various modifations on species""" + from masci_tools.io.fleurxmlmodifier import ModifierTask fleurinp_tmp = create_fleurinp(inpxmlfilefolder) fm = FleurinpModifier(fleurinp_tmp) @@ -34,12 +35,19 @@ def test_fleurinp_modifier1(create_fleurinp): fm.set_species('all', {'mtSphere': {'radius': 3.333}}) fm.undo() changes = fm.changes() - assert changes == [('set_inpchanges', { - 'Kmax': 3.9, - 'dos': True - }), ('shift_value', { - 'Kmax': 0.1 - }, 'rel'), ('shift_value_species_label', ' 222', 'radius', 3, 'abs')] + + assert changes == [ + ModifierTask(name='set_inpchanges', args=({ + 'dos': True, + 'Kmax': 3.9 + },), kwargs={}), + ModifierTask(name='shift_value', args=({ + 'Kmax': 0.1 + }, 'rel'), kwargs={}), + ModifierTask(name='shift_value_species_label', + args=(' 222', 'radius', 3), + kwargs={'mode': 'abs'}) + ] fm.show(validate=True) fm.freeze() @@ -51,10 +59,9 @@ def test_fleurinp_modifier1(create_fleurinp): assert changes == [] -def test_fleurinp_modifier2(create_fleurinp, inpxml_etree): +def test_fleurinp_modifier2(create_fleurinp, inpxml_etree, eval_xpath): """Tests if fleurinp_modifier with various other modifations methods, the detailed tests for method functionality is tested elsewhere.""" - from aiida_fleur.tools.xml_util import eval_xpath fleurinp_tmp = create_fleurinp(inpxmlfilefolder) etree = inpxml_etree(inpxmlfilefolder) @@ -64,26 +71,104 @@ def test_fleurinp_modifier2(create_fleurinp, inpxml_etree): assert isinstance(actions, dict) new_tag = eval_xpath(etree, '/fleurInput/calculationSetup/scfLoop') - fm.delete_tag('/fleurInput/calculationSetup/scfLoop') - fm.replace_tag('/fleurInput/calculationSetup/cutoffs', new_tag) - fm.delete_att('/fleurInput/calculationSetup/soc', 'theta') - fm.create_tag('/fleurInput/calculationSetup/soc', 'theta') - fm.xml_set_all_text('/fleurInput/cell/symmetryOperations/symOp/row-1', 'test text') - fm.xml_set_text_occ('/fleurInput/cell/symmetryOperations/symOp/row-1', 'test text') - fm.xml_set_text('/fleurInput/cell/symmetryOperations/symOp/row-1', 'test text') - fm.xml_set_all_attribv('/fleurInput/calculationSetup/soc', 'theta', 12) - fm.xml_set_first_attribv('/fleurInput/calculationSetup/soc', 'theta', 12) - fm.xml_set_attribv_occ('/fleurInput/calculationSetup/soc', 'theta', 12) + with pytest.deprecated_call(): + fm.delete_tag('/fleurInput/calculationSetup/scfLoop') + with pytest.deprecated_call(): + fm.replace_tag('/fleurInput/calculationSetup/cutoffs', new_tag) + with pytest.deprecated_call(): + fm.delete_att('/fleurInput/calculationSetup/soc', 'theta') + with pytest.deprecated_call(): + fm.create_tag('/fleurInput/calculationSetup/soc', 'theta') + with pytest.deprecated_call(): + fm.xml_set_all_text('/fleurInput/cell/symmetryOperations/symOp/row-1', 'test text') + with pytest.deprecated_call(): + fm.xml_set_text_occ('/fleurInput/cell/symmetryOperations/symOp/row-1', 'test text') + with pytest.deprecated_call(): + fm.xml_set_text('/fleurInput/cell/symmetryOperations/symOp/row-1', 'test text') + with pytest.deprecated_call(): + fm.xml_set_all_attribv('/fleurInput/calculationSetup/soc', 'theta', 12) + with pytest.deprecated_call(): + fm.xml_set_first_attribv('/fleurInput/calculationSetup/soc', 'theta', 12) + with pytest.deprecated_call(): + fm.xml_set_attribv_occ('/fleurInput/calculationSetup/soc', 'theta', 12) fm.set_species_label(' 222', {'mtSphere': {'radius': 3.333}}) - fm.set_atomgr_att_label(attributedict={'force': [('relaxXYZ', 'FFF')]}, atom_label=' 222') - fm.set_atomgr_att(attributedict={'force': [('relaxXYZ', 'TFF')]}, species='Fe-1') + with pytest.deprecated_call(): + fm.set_atomgr_att_label(attributedict={'force': {'relaxXYZ': 'FFF'}}, atom_label=' 222') + with pytest.deprecated_call(): + fm.set_atomgr_att(attributedict={'force': {'relaxXYZ': 'TFF'}}, species='Fe-1') + + #fm.set_nkpts(500, gamma='T') + #fm.set_kpath({'gamma': (0, 0, 0), 'L': (0.1, 0.1, 0.1)}, 300) + with pytest.deprecated_call(): + fm.add_num_to_att('/fleurInput/calculationSetup/scfLoop', 'minDistance', 4) + #fm.set_species1 + fm.show() + + +def test_fleurinp_modifier_regression(create_fleurinp, inpxml_etree, file_regression): + """Tests if fleurinp_modifier with various other modifations methods, + the detailed tests for method functionality is tested elsewhere.""" + fleurinp_tmp = create_fleurinp(inpxmlfilefolder) - fm.set_nkpts(500, gamma='T') - fm.set_kpath({'gamma': (0, 0, 0), 'L': (0.1, 0.1, 0.1)}, 300) - fm.add_num_to_att('/fleurInput/calculationSetup/soc', 'theta', 4) + fm = FleurinpModifier(fleurinp_tmp) + fm.set_inpchanges({'dos': True, 'Kmax': 3.9}) + fm.shift_value({'Kmax': 0.1}, 'rel') + fm.shift_value_species_label(' 222', 'radius', 3, mode='abs') + fm.set_species('all', {'mtSphere': {'radius': 3.333}}) + + #fm.set_nkpts(500, gamma='T') + #fm.set_kpath({'gamma': (0, 0, 0), 'L': (0.1, 0.1, 0.1)}, 300) + with pytest.deprecated_call(): + fm.add_num_to_att('/fleurInput/calculationSetup/scfLoop', 'minDistance', 4) #fm.set_species1 fm.show() + new_fleurinp = fm.freeze() + + file_regression.check(new_fleurinp.get_content('inp.xml'), extension='.xml') + + +def test_fleurinp_modifier_included_files(create_fleurinp, inpxml_etree, file_regression): + """Tests if fleurinp_modifier with various other modifations methods, + the detailed tests for method functionality is tested elsewhere.""" + + TEST_FOLDER = os.path.dirname(os.path.abspath(__file__)) + TEST_FOLDER = os.path.abspath(os.path.join(TEST_FOLDER, '../files/included_xml_files')) + + INPXML_FILE = os.path.join(TEST_FOLDER, 'inp.xml') + KPTSXML_FILE = os.path.join(TEST_FOLDER, 'kpts.xml') + SYMXML_FILE = os.path.join(TEST_FOLDER, 'sym.xml') + + fleurinp_tmp = create_fleurinp(INPXML_FILE, additional_files=[KPTSXML_FILE, SYMXML_FILE]) + + fm = FleurinpModifier(fleurinp_tmp) + #Modify main inp.xml file + fm.set_inpchanges({'dos': True, 'Kmax': 3.9}) + fm.shift_value({'Kmax': 0.1}, 'rel') + + #Modify included xml files + fm.delete_tag('symmetryOperations') + fm.create_tag('symmetryOperations') + fm.create_tag('kPointList') + fm.create_tag('kPoint', occurrences=0) + fm.set_attrib_value('name', 'TEST', contains='kPointList', occurrences=0) + fm.set_text('kPoint', [0.0, 0.0, 0.0], + complex_xpath="/fleurInput/cell/bzIntegration/kPointLists/kPointList[@name='TEST']/kPoint") + + fm.show() + + new_fleurinp = fm.freeze() + + assert new_fleurinp.files == ['kpts.xml', 'sym.xml', 'inp.xml'] + + file_content = [ + new_fleurinp.get_content('inp.xml'), + new_fleurinp.get_content('kpts.xml'), + new_fleurinp.get_content('sym.xml') + ] + + file_regression.check('\n'.join(file_content), extension='.xml') + #For this test we need a input file with defined LDA+U procedures file_path2 = '../files/inpxml/GaAsMultiForceXML/inp.xml' @@ -97,7 +182,7 @@ def test_fleurinp_modifier_set_nmmpmat(create_fleurinp): fleurinp_tmp = create_fleurinp(inpxmlfilefolder2) fm = FleurinpModifier(fleurinp_tmp) - fm.set_nmmpmat('Ga-1', orbital=2, spin=1, occStates=[1, 2, 3, 4, 5]) + fm.set_nmmpmat('Ga-1', orbital=2, spin=1, state_occupations=[1, 2, 3, 4, 5]) fm.set_nmmpmat('As-2', orbital=1, spin=1, denmat=[[1, -2, 3], [4, -5, 6], [7, -8, 9]]) # Does not validate @@ -108,6 +193,48 @@ def test_fleurinp_modifier_set_nmmpmat(create_fleurinp): assert 'n_mmp_mat' in new_fleurinp.files +def test_fleurinp_modifier_instance_modifications(create_fleurinp): + """Tests if set_nmmpmat works on fleurinp modifier works, with right interface""" + fleurinp_tmp = create_fleurinp(inpxmlfilefolder2) + + n_mmp_mat_file = os.path.dirname(os.path.abspath(__file__)) + n_mmp_mat_file = os.path.abspath(os.path.join(n_mmp_mat_file, '../files/n_mmp_mat/n_mmp_mat_GaAsMultiForceXML')) + + fm = FleurinpModifier(fleurinp_tmp) + fm.set_file(n_mmp_mat_file, dst_filename='n_mmp_mat') + + new_fleurinp = fm.freeze() + assert 'n_mmp_mat' in new_fleurinp.files + + fm = FleurinpModifier(new_fleurinp) + fm.del_file('n_mmp_mat') + new_fleurinp = fm.freeze() + assert 'n_mmp_mat' not in new_fleurinp.files + + +def test_fleurinp_modifier_instance_modifications_node(create_fleurinp): + """Tests if set_nmmpmat works on fleurinp modifier works, with right interface""" + from aiida.orm import FolderData + fleurinp_tmp = create_fleurinp(inpxmlfilefolder2) + + n_mmp_mat_folder = os.path.dirname(os.path.abspath(__file__)) + n_mmp_mat_folder = os.path.abspath(os.path.join(n_mmp_mat_folder, '../files/n_mmp_mat')) + + n_mmp_mat_folder = FolderData(tree=n_mmp_mat_folder) + n_mmp_mat_folder.store() + + fm = FleurinpModifier(fleurinp_tmp) + fm.set_file('n_mmp_mat_GaAsMultiForceXML', dst_filename='n_mmp_mat', node=n_mmp_mat_folder) + + new_fleurinp = fm.freeze() + assert 'n_mmp_mat' in new_fleurinp.files + + fm = FleurinpModifier(new_fleurinp) + fm.del_file('n_mmp_mat') + new_fleurinp = fm.freeze() + assert 'n_mmp_mat' not in new_fleurinp.files + + def test_fleurinp_modifier_set_kpointsdata(create_fleurinp): """Test if setting a kpoints list to a fleurinp data node works""" from aiida.orm import KpointsData @@ -144,7 +271,7 @@ def test_fleurinpmodifier_error_messages(create_fleurinp): fleurinp_tmp = create_fleurinp(inpxmlfilefolder) fm = FleurinpModifier(fleurinp_tmp) - fm._tasks.append(('not_existent', 1, 2, 3)) # task does not exists. + fm._tasks.append(('not_existent', [1, 2, 3], {'Random_arg': 'Does not make sense'})) # task does not exists. with pytest.raises(ValueError): fm.freeze() diff --git a/tests/data/test_fleurinpmodifier/test_fleurinp_modifier_included_files.xml b/tests/data/test_fleurinpmodifier/test_fleurinp_modifier_included_files.xml new file mode 100644 index 000000000..5c1e1e74f --- /dev/null +++ b/tests/data/test_fleurinpmodifier/test_fleurinp_modifier_included_files.xml @@ -0,0 +1,119 @@ + + + bcc Fe + + + + + + + + + + + + + + + + + + + + + + + + + + + 5.4159572954 .0000000000 .0000000000 + .0000000000 5.4159572954 .0000000000 + .0000000000 .0000000000 5.4159572954 + + + + + + + + + (1s1/2) (2s1/2) (2p1/2) (2p3/2) (3s1/2) (3p1/2) (3p3/2) + (4s1/2) (3d3/2) (3d5/2) + + + + + + + + + + .0000000000 .0000000000 .0000000000 + + + + + 1.000/2.000 1.000/2.000 1.000/2.000 + + + + + + + + + + + + + + + + + + + 0.0000000000000 0.0000000000000 0.0000000000000 + + + 0.450000 0.450000 0.450000 + 0.450000 0.450000 0.350000 + 0.450000 0.450000 0.250000 + 0.450000 0.450000 0.150000 + 0.450000 0.450000 0.050000 + 0.450000 0.350000 0.350000 + 0.450000 0.350000 0.250000 + 0.450000 0.350000 0.150000 + 0.450000 0.350000 0.050000 + 0.450000 0.250000 0.250000 + 0.450000 0.250000 0.150000 + 0.450000 0.250000 0.050000 + 0.450000 0.150000 0.150000 + 0.450000 0.150000 0.050000 + 0.450000 0.050000 0.050000 + 0.350000 0.350000 0.350000 + 0.350000 0.350000 0.250000 + 0.350000 0.350000 0.150000 + 0.350000 0.350000 0.050000 + 0.350000 0.250000 0.250000 + 0.350000 0.250000 0.150000 + 0.350000 0.250000 0.050000 + 0.350000 0.150000 0.150000 + 0.350000 0.150000 0.050000 + 0.350000 0.050000 0.050000 + 0.250000 0.250000 0.250000 + 0.250000 0.250000 0.150000 + 0.250000 0.250000 0.050000 + 0.250000 0.150000 0.150000 + 0.250000 0.150000 0.050000 + 0.250000 0.050000 0.050000 + 0.150000 0.150000 0.150000 + 0.150000 0.150000 0.050000 + 0.150000 0.050000 0.050000 + 0.050000 0.050000 0.050000 + + + + + + diff --git a/tests/data/test_fleurinpmodifier/test_fleurinp_modifier_regression.xml b/tests/data/test_fleurinpmodifier/test_fleurinp_modifier_regression.xml new file mode 100644 index 000000000..7815fd416 --- /dev/null +++ b/tests/data/test_fleurinpmodifier/test_fleurinp_modifier_regression.xml @@ -0,0 +1,385 @@ + + + A Fleur + + + + + + + + .0000000000 .0000000000 .0000000000 + + + + + + + + + + + + + + + + 11.00/24.00 7.00/16.00 0.00/2.00 + 9.00/24.00 7.00/16.00 0.00/2.00 + 7.00/24.00 7.00/16.00 0.00/2.00 + 5.00/24.00 7.00/16.00 0.00/2.00 + 3.00/24.00 7.00/16.00 0.00/2.00 + 1.00/24.00 7.00/16.00 0.00/2.00 + 11.00/24.00 5.00/16.00 0.00/2.00 + 9.00/24.00 5.00/16.00 0.00/2.00 + 7.00/24.00 5.00/16.00 0.00/2.00 + 5.00/24.00 5.00/16.00 0.00/2.00 + 3.00/24.00 5.00/16.00 0.00/2.00 + 1.00/24.00 5.00/16.00 0.00/2.00 + 11.00/24.00 3.00/16.00 0.00/2.00 + 9.00/24.00 3.00/16.00 0.00/2.00 + 7.00/24.00 3.00/16.00 0.00/2.00 + 5.00/24.00 3.00/16.00 0.00/2.00 + 3.00/24.00 3.00/16.00 0.00/2.00 + 1.00/24.00 3.00/16.00 0.00/2.00 + 11.00/24.00 1.00/16.00 0.00/2.00 + 9.00/24.00 1.00/16.00 0.00/2.00 + 7.00/24.00 1.00/16.00 0.00/2.00 + 5.00/24.00 1.00/16.00 0.00/2.00 + 3.00/24.00 1.00/16.00 0.00/2.00 + 1.00/24.00 1.00/16.00 0.00/2.00 + + + 0.0000000000000 0.0000000000000 0.0000000000000 + 0.0071428571429 0.0000000000000 0.0000000000000 + 0.0142857142857 0.0000000000000 0.0000000000000 + 0.0214285714286 0.0000000000000 0.0000000000000 + 0.0285714285714 0.0000000000000 0.0000000000000 + 0.0357142857143 0.0000000000000 0.0000000000000 + 0.0428571428571 0.0000000000000 0.0000000000000 + 0.0500000000000 0.0000000000000 0.0000000000000 + 0.0571428571429 0.0000000000000 0.0000000000000 + 0.0642857142857 0.0000000000000 0.0000000000000 + 0.0714285714286 0.0000000000000 0.0000000000000 + 0.0785714285714 0.0000000000000 0.0000000000000 + 0.0857142857143 0.0000000000000 0.0000000000000 + 0.0928571428571 0.0000000000000 0.0000000000000 + 0.1000000000000 0.0000000000000 0.0000000000000 + 0.1071428571429 0.0000000000000 0.0000000000000 + 0.1142857142857 0.0000000000000 0.0000000000000 + 0.1214285714286 0.0000000000000 0.0000000000000 + 0.1285714285714 0.0000000000000 0.0000000000000 + 0.1357142857143 0.0000000000000 0.0000000000000 + 0.1428571428571 0.0000000000000 0.0000000000000 + 0.1500000000000 0.0000000000000 0.0000000000000 + 0.1571428571429 0.0000000000000 0.0000000000000 + 0.1642857142857 0.0000000000000 0.0000000000000 + 0.1714285714286 0.0000000000000 0.0000000000000 + 0.1785714285714 0.0000000000000 0.0000000000000 + 0.1857142857143 0.0000000000000 0.0000000000000 + 0.1928571428571 0.0000000000000 0.0000000000000 + 0.2000000000000 0.0000000000000 0.0000000000000 + 0.2071428571429 0.0000000000000 0.0000000000000 + 0.2142857142857 0.0000000000000 0.0000000000000 + 0.2214285714286 0.0000000000000 0.0000000000000 + 0.2285714285714 0.0000000000000 0.0000000000000 + 0.2357142857143 0.0000000000000 0.0000000000000 + 0.2428571428571 0.0000000000000 0.0000000000000 + 0.2500000000000 0.0000000000000 0.0000000000000 + 0.2571428571429 0.0000000000000 0.0000000000000 + 0.2642857142857 0.0000000000000 0.0000000000000 + 0.2714285714286 0.0000000000000 0.0000000000000 + 0.2785714285714 0.0000000000000 0.0000000000000 + 0.2857142857143 0.0000000000000 0.0000000000000 + 0.2928571428571 0.0000000000000 0.0000000000000 + 0.3000000000000 0.0000000000000 0.0000000000000 + 0.3071428571429 0.0000000000000 0.0000000000000 + 0.3142857142857 0.0000000000000 0.0000000000000 + 0.3214285714286 0.0000000000000 0.0000000000000 + 0.3285714285714 0.0000000000000 0.0000000000000 + 0.3357142857143 0.0000000000000 0.0000000000000 + 0.3428571428571 0.0000000000000 0.0000000000000 + 0.3500000000000 0.0000000000000 0.0000000000000 + 0.3571428571429 0.0000000000000 0.0000000000000 + 0.3642857142857 0.0000000000000 0.0000000000000 + 0.3714285714286 0.0000000000000 0.0000000000000 + 0.3785714285714 0.0000000000000 0.0000000000000 + 0.3857142857143 0.0000000000000 0.0000000000000 + 0.3928571428571 0.0000000000000 0.0000000000000 + 0.4000000000000 0.0000000000000 0.0000000000000 + 0.4071428571429 0.0000000000000 0.0000000000000 + 0.4142857142857 0.0000000000000 0.0000000000000 + 0.4214285714286 0.0000000000000 0.0000000000000 + 0.4285714285714 0.0000000000000 0.0000000000000 + 0.4357142857143 0.0000000000000 0.0000000000000 + 0.4428571428571 0.0000000000000 0.0000000000000 + 0.4500000000000 0.0000000000000 0.0000000000000 + 0.4571428571429 0.0000000000000 0.0000000000000 + 0.4642857142857 0.0000000000000 0.0000000000000 + 0.4714285714286 0.0000000000000 0.0000000000000 + 0.4785714285714 0.0000000000000 0.0000000000000 + 0.4857142857143 0.0000000000000 0.0000000000000 + 0.4928571428571 0.0000000000000 0.0000000000000 + 0.5000000000000 0.0000000000000 0.0000000000000 + 0.5000000000000 0.0102040816327 0.0000000000000 + 0.5000000000000 0.0204081632653 0.0000000000000 + 0.5000000000000 0.0306122448980 0.0000000000000 + 0.5000000000000 0.0408163265306 0.0000000000000 + 0.5000000000000 0.0510204081633 0.0000000000000 + 0.5000000000000 0.0612244897959 0.0000000000000 + 0.5000000000000 0.0714285714286 0.0000000000000 + 0.5000000000000 0.0816326530612 0.0000000000000 + 0.5000000000000 0.0918367346939 0.0000000000000 + 0.5000000000000 0.1020408163265 0.0000000000000 + 0.5000000000000 0.1122448979592 0.0000000000000 + 0.5000000000000 0.1224489795918 0.0000000000000 + 0.5000000000000 0.1326530612245 0.0000000000000 + 0.5000000000000 0.1428571428571 0.0000000000000 + 0.5000000000000 0.1530612244898 0.0000000000000 + 0.5000000000000 0.1632653061224 0.0000000000000 + 0.5000000000000 0.1734693877551 0.0000000000000 + 0.5000000000000 0.1836734693878 0.0000000000000 + 0.5000000000000 0.1938775510204 0.0000000000000 + 0.5000000000000 0.2040816326531 0.0000000000000 + 0.5000000000000 0.2142857142857 0.0000000000000 + 0.5000000000000 0.2244897959184 0.0000000000000 + 0.5000000000000 0.2346938775510 0.0000000000000 + 0.5000000000000 0.2448979591837 0.0000000000000 + 0.5000000000000 0.2551020408163 0.0000000000000 + 0.5000000000000 0.2653061224490 0.0000000000000 + 0.5000000000000 0.2755102040816 0.0000000000000 + 0.5000000000000 0.2857142857143 0.0000000000000 + 0.5000000000000 0.2959183673469 0.0000000000000 + 0.5000000000000 0.3061224489796 0.0000000000000 + 0.5000000000000 0.3163265306122 0.0000000000000 + 0.5000000000000 0.3265306122449 0.0000000000000 + 0.5000000000000 0.3367346938776 0.0000000000000 + 0.5000000000000 0.3469387755102 0.0000000000000 + 0.5000000000000 0.3571428571429 0.0000000000000 + 0.5000000000000 0.3673469387755 0.0000000000000 + 0.5000000000000 0.3775510204082 0.0000000000000 + 0.5000000000000 0.3877551020408 0.0000000000000 + 0.5000000000000 0.3979591836735 0.0000000000000 + 0.5000000000000 0.4081632653061 0.0000000000000 + 0.5000000000000 0.4183673469388 0.0000000000000 + 0.5000000000000 0.4285714285714 0.0000000000000 + 0.5000000000000 0.4387755102041 0.0000000000000 + 0.5000000000000 0.4489795918367 0.0000000000000 + 0.5000000000000 0.4591836734694 0.0000000000000 + 0.5000000000000 0.4693877551020 0.0000000000000 + 0.5000000000000 0.4795918367347 0.0000000000000 + 0.5000000000000 0.4897959183673 0.0000000000000 + 0.5000000000000 0.5000000000000 0.0000000000000 + 0.4928571428571 0.5000000000000 0.0000000000000 + 0.4857142857143 0.5000000000000 0.0000000000000 + 0.4785714285714 0.5000000000000 0.0000000000000 + 0.4714285714286 0.5000000000000 0.0000000000000 + 0.4642857142857 0.5000000000000 0.0000000000000 + 0.4571428571429 0.5000000000000 0.0000000000000 + 0.4500000000000 0.5000000000000 0.0000000000000 + 0.4428571428571 0.5000000000000 0.0000000000000 + 0.4357142857143 0.5000000000000 0.0000000000000 + 0.4285714285714 0.5000000000000 0.0000000000000 + 0.4214285714286 0.5000000000000 0.0000000000000 + 0.4142857142857 0.5000000000000 0.0000000000000 + 0.4071428571429 0.5000000000000 0.0000000000000 + 0.4000000000000 0.5000000000000 0.0000000000000 + 0.3928571428571 0.5000000000000 0.0000000000000 + 0.3857142857143 0.5000000000000 0.0000000000000 + 0.3785714285714 0.5000000000000 0.0000000000000 + 0.3714285714286 0.5000000000000 0.0000000000000 + 0.3642857142857 0.5000000000000 0.0000000000000 + 0.3571428571429 0.5000000000000 0.0000000000000 + 0.3500000000000 0.5000000000000 0.0000000000000 + 0.3428571428571 0.5000000000000 0.0000000000000 + 0.3357142857143 0.5000000000000 0.0000000000000 + 0.3285714285714 0.5000000000000 0.0000000000000 + 0.3214285714286 0.5000000000000 0.0000000000000 + 0.3142857142857 0.5000000000000 0.0000000000000 + 0.3071428571429 0.5000000000000 0.0000000000000 + 0.3000000000000 0.5000000000000 0.0000000000000 + 0.2928571428571 0.5000000000000 0.0000000000000 + 0.2857142857143 0.5000000000000 0.0000000000000 + 0.2785714285714 0.5000000000000 0.0000000000000 + 0.2714285714286 0.5000000000000 0.0000000000000 + 0.2642857142857 0.5000000000000 0.0000000000000 + 0.2571428571429 0.5000000000000 0.0000000000000 + 0.2500000000000 0.5000000000000 0.0000000000000 + 0.2428571428571 0.5000000000000 0.0000000000000 + 0.2357142857143 0.5000000000000 0.0000000000000 + 0.2285714285714 0.5000000000000 0.0000000000000 + 0.2214285714286 0.5000000000000 0.0000000000000 + 0.2142857142857 0.5000000000000 0.0000000000000 + 0.2071428571429 0.5000000000000 0.0000000000000 + 0.2000000000000 0.5000000000000 0.0000000000000 + 0.1928571428571 0.5000000000000 0.0000000000000 + 0.1857142857143 0.5000000000000 0.0000000000000 + 0.1785714285714 0.5000000000000 0.0000000000000 + 0.1714285714286 0.5000000000000 0.0000000000000 + 0.1642857142857 0.5000000000000 0.0000000000000 + 0.1571428571429 0.5000000000000 0.0000000000000 + 0.1500000000000 0.5000000000000 0.0000000000000 + 0.1428571428571 0.5000000000000 0.0000000000000 + 0.1357142857143 0.5000000000000 0.0000000000000 + 0.1285714285714 0.5000000000000 0.0000000000000 + 0.1214285714286 0.5000000000000 0.0000000000000 + 0.1142857142857 0.5000000000000 0.0000000000000 + 0.1071428571429 0.5000000000000 0.0000000000000 + 0.1000000000000 0.5000000000000 0.0000000000000 + 0.0928571428571 0.5000000000000 0.0000000000000 + 0.0857142857143 0.5000000000000 0.0000000000000 + 0.0785714285714 0.5000000000000 0.0000000000000 + 0.0714285714286 0.5000000000000 0.0000000000000 + 0.0642857142857 0.5000000000000 0.0000000000000 + 0.0571428571429 0.5000000000000 0.0000000000000 + 0.0500000000000 0.5000000000000 0.0000000000000 + 0.0428571428571 0.5000000000000 0.0000000000000 + 0.0357142857143 0.5000000000000 0.0000000000000 + 0.0285714285714 0.5000000000000 0.0000000000000 + 0.0214285714286 0.5000000000000 0.0000000000000 + 0.0142857142857 0.5000000000000 0.0000000000000 + 0.0071428571429 0.5000000000000 0.0000000000000 + 0.0000000000000 0.5000000000000 0.0000000000000 + 0.0000000000000 0.4900000000000 0.0000000000000 + 0.0000000000000 0.4800000000000 0.0000000000000 + 0.0000000000000 0.4700000000000 0.0000000000000 + 0.0000000000000 0.4600000000000 0.0000000000000 + 0.0000000000000 0.4500000000000 0.0000000000000 + 0.0000000000000 0.4400000000000 0.0000000000000 + 0.0000000000000 0.4300000000000 0.0000000000000 + 0.0000000000000 0.4200000000000 0.0000000000000 + 0.0000000000000 0.4100000000000 0.0000000000000 + 0.0000000000000 0.4000000000000 0.0000000000000 + 0.0000000000000 0.3900000000000 0.0000000000000 + 0.0000000000000 0.3800000000000 0.0000000000000 + 0.0000000000000 0.3700000000000 0.0000000000000 + 0.0000000000000 0.3600000000000 0.0000000000000 + 0.0000000000000 0.3500000000000 0.0000000000000 + 0.0000000000000 0.3400000000000 0.0000000000000 + 0.0000000000000 0.3300000000000 0.0000000000000 + 0.0000000000000 0.3200000000000 0.0000000000000 + 0.0000000000000 0.3100000000000 0.0000000000000 + 0.0000000000000 0.3000000000000 0.0000000000000 + 0.0000000000000 0.2900000000000 0.0000000000000 + 0.0000000000000 0.2800000000000 0.0000000000000 + 0.0000000000000 0.2700000000000 0.0000000000000 + 0.0000000000000 0.2600000000000 0.0000000000000 + 0.0000000000000 0.2500000000000 0.0000000000000 + 0.0000000000000 0.2400000000000 0.0000000000000 + 0.0000000000000 0.2300000000000 0.0000000000000 + 0.0000000000000 0.2200000000000 0.0000000000000 + 0.0000000000000 0.2100000000000 0.0000000000000 + 0.0000000000000 0.2000000000000 0.0000000000000 + 0.0000000000000 0.1900000000000 0.0000000000000 + 0.0000000000000 0.1800000000000 0.0000000000000 + 0.0000000000000 0.1700000000000 0.0000000000000 + 0.0000000000000 0.1600000000000 0.0000000000000 + 0.0000000000000 0.1500000000000 0.0000000000000 + 0.0000000000000 0.1400000000000 0.0000000000000 + 0.0000000000000 0.1300000000000 0.0000000000000 + 0.0000000000000 0.1200000000000 0.0000000000000 + 0.0000000000000 0.1100000000000 0.0000000000000 + 0.0000000000000 0.1000000000000 0.0000000000000 + 0.0000000000000 0.0900000000000 0.0000000000000 + 0.0000000000000 0.0800000000000 0.0000000000000 + 0.0000000000000 0.0700000000000 0.0000000000000 + 0.0000000000000 0.0600000000000 0.0000000000000 + 0.0000000000000 0.0500000000000 0.0000000000000 + 0.0000000000000 0.0400000000000 0.0000000000000 + 0.0000000000000 0.0300000000000 0.0000000000000 + 0.0000000000000 0.0200000000000 0.0000000000000 + 0.0000000000000 0.0100000000000 0.0000000000000 + 0.0000000000000 0.0000000000000 0.0000000000000 + + + + + + 1 0 0 .0000000000 + 0 1 0 .0000000000 + 0 0 1 .0000000000 + + + -1 0 0 .0000000000 + 0 1 0 .0000000000 + 0 0 1 .0000000000 + + + 1 0 0 .0000000000 + 0 -1 0 .0000000000 + 0 0 1 .0000000000 + + + -1 0 0 .0000000000 + 0 -1 0 .0000000000 + 0 0 1 .0000000000 + + + + + 5.3011797030 0.0000000000 0.0000000000 + 0.0000000000 7.4970000330 0.0000000000 + 0.0000000000 0.0000000000 9.4500000000 + + + + + + + + + + + (1s1/2) (2s1/2) (2p1/2) (2p3/2) + (3s1/2) (3p1/2) (3p3/2) (4s1/2) (3d3/2) (3d5/2) + + + + + + + + + + + + + + (1s1/2) (2s1/2) (2p1/2) (2p3/2) (3s1/2) (3p1/2) (3p3/2) (4s1/2) (3d3/2) (3d5/2) (4p1/2) (4p3/2) (5s1/2) (4d3/2) (4d5/2) (4f5/2) (4f7/2) + (5p1/2) (5p3/2) (6s1/2) (5d3/2) (5d5/2) + + + + + + + + + + + + + .0000000000 .0000000000 -.9964250044 + + + + + 1.000/2.000 1.000/2.000 .9964250044 + + + + + + + + + + + + + + + + + + diff --git a/tests/files/included_xml_files/inp.xml b/tests/files/included_xml_files/inp.xml new file mode 100644 index 000000000..6395b7d95 --- /dev/null +++ b/tests/files/included_xml_files/inp.xml @@ -0,0 +1,70 @@ + + + + bcc Fe + + + + + + + + + + + + + + + + + + + + + + + + + 5.4159572954 .0000000000 .0000000000 + .0000000000 5.4159572954 .0000000000 + .0000000000 .0000000000 5.4159572954 + + + + + + + + + (1s1/2) (2s1/2) (2p1/2) (2p3/2) (3s1/2) (3p1/2) (3p3/2) + (4s1/2) (3d3/2) (3d5/2) + + + + + + + + + + .0000000000 .0000000000 .0000000000 + + + + + 1.000/2.000 1.000/2.000 1.000/2.000 + + + + + + + + + + + + + + diff --git a/tests/files/included_xml_files/kpts.xml b/tests/files/included_xml_files/kpts.xml new file mode 100644 index 000000000..f7e8c185b --- /dev/null +++ b/tests/files/included_xml_files/kpts.xml @@ -0,0 +1,39 @@ + + + 0.450000 0.450000 0.450000 + 0.450000 0.450000 0.350000 + 0.450000 0.450000 0.250000 + 0.450000 0.450000 0.150000 + 0.450000 0.450000 0.050000 + 0.450000 0.350000 0.350000 + 0.450000 0.350000 0.250000 + 0.450000 0.350000 0.150000 + 0.450000 0.350000 0.050000 + 0.450000 0.250000 0.250000 + 0.450000 0.250000 0.150000 + 0.450000 0.250000 0.050000 + 0.450000 0.150000 0.150000 + 0.450000 0.150000 0.050000 + 0.450000 0.050000 0.050000 + 0.350000 0.350000 0.350000 + 0.350000 0.350000 0.250000 + 0.350000 0.350000 0.150000 + 0.350000 0.350000 0.050000 + 0.350000 0.250000 0.250000 + 0.350000 0.250000 0.150000 + 0.350000 0.250000 0.050000 + 0.350000 0.150000 0.150000 + 0.350000 0.150000 0.050000 + 0.350000 0.050000 0.050000 + 0.250000 0.250000 0.250000 + 0.250000 0.250000 0.150000 + 0.250000 0.250000 0.050000 + 0.250000 0.150000 0.150000 + 0.250000 0.150000 0.050000 + 0.250000 0.050000 0.050000 + 0.150000 0.150000 0.150000 + 0.150000 0.150000 0.050000 + 0.150000 0.050000 0.050000 + 0.050000 0.050000 0.050000 + + diff --git a/tests/files/included_xml_files/sym.xml b/tests/files/included_xml_files/sym.xml new file mode 100644 index 000000000..674c7c63f --- /dev/null +++ b/tests/files/included_xml_files/sym.xml @@ -0,0 +1,242 @@ + + + 1 0 0 .0000000000 + 0 1 0 .0000000000 + 0 0 1 .0000000000 + + + -1 0 0 .0000000000 + 0 1 0 .0000000000 + 0 0 1 .0000000000 + + + 1 0 0 .0000000000 + 0 -1 0 .0000000000 + 0 0 1 .0000000000 + + + -1 0 0 .0000000000 + 0 -1 0 .0000000000 + 0 0 1 .0000000000 + + + 0 -1 0 .0000000000 + -1 0 0 .0000000000 + 0 0 1 .0000000000 + + + 0 -1 0 .0000000000 + 1 0 0 .0000000000 + 0 0 1 .0000000000 + + + 0 1 0 .0000000000 + -1 0 0 .0000000000 + 0 0 1 .0000000000 + + + 0 1 0 .0000000000 + 1 0 0 .0000000000 + 0 0 1 .0000000000 + + + 1 0 0 .0000000000 + 0 1 0 .0000000000 + 0 0 -1 .0000000000 + + + -1 0 0 .0000000000 + 0 1 0 .0000000000 + 0 0 -1 .0000000000 + + + 1 0 0 .0000000000 + 0 -1 0 .0000000000 + 0 0 -1 .0000000000 + + + -1 0 0 .0000000000 + 0 -1 0 .0000000000 + 0 0 -1 .0000000000 + + + 0 -1 0 .0000000000 + -1 0 0 .0000000000 + 0 0 -1 .0000000000 + + + 0 -1 0 .0000000000 + 1 0 0 .0000000000 + 0 0 -1 .0000000000 + + + 0 1 0 .0000000000 + -1 0 0 .0000000000 + 0 0 -1 .0000000000 + + + 0 1 0 .0000000000 + 1 0 0 .0000000000 + 0 0 -1 .0000000000 + + + 1 0 0 .0000000000 + 0 0 -1 .0000000000 + 0 -1 0 .0000000000 + + + -1 0 0 .0000000000 + 0 0 -1 .0000000000 + 0 -1 0 .0000000000 + + + 0 -1 0 .0000000000 + 0 0 -1 .0000000000 + -1 0 0 .0000000000 + + + 0 -1 0 .0000000000 + 0 0 -1 .0000000000 + 1 0 0 .0000000000 + + + 0 1 0 .0000000000 + 0 0 -1 .0000000000 + -1 0 0 .0000000000 + + + 0 1 0 .0000000000 + 0 0 -1 .0000000000 + 1 0 0 .0000000000 + + + 1 0 0 .0000000000 + 0 0 -1 .0000000000 + 0 1 0 .0000000000 + + + -1 0 0 .0000000000 + 0 0 -1 .0000000000 + 0 1 0 .0000000000 + + + 0 0 -1 .0000000000 + 0 1 0 .0000000000 + -1 0 0 .0000000000 + + + 0 0 -1 .0000000000 + 0 1 0 .0000000000 + 1 0 0 .0000000000 + + + 0 0 -1 .0000000000 + -1 0 0 .0000000000 + 0 -1 0 .0000000000 + + + 0 0 -1 .0000000000 + 1 0 0 .0000000000 + 0 -1 0 .0000000000 + + + 0 0 -1 .0000000000 + 0 -1 0 .0000000000 + -1 0 0 .0000000000 + + + 0 0 -1 .0000000000 + 0 -1 0 .0000000000 + 1 0 0 .0000000000 + + + 0 0 -1 .0000000000 + -1 0 0 .0000000000 + 0 1 0 .0000000000 + + + 0 0 -1 .0000000000 + 1 0 0 .0000000000 + 0 1 0 .0000000000 + + + 0 0 1 .0000000000 + 0 1 0 .0000000000 + -1 0 0 .0000000000 + + + 0 0 1 .0000000000 + 0 1 0 .0000000000 + 1 0 0 .0000000000 + + + 0 0 1 .0000000000 + -1 0 0 .0000000000 + 0 -1 0 .0000000000 + + + 0 0 1 .0000000000 + 1 0 0 .0000000000 + 0 -1 0 .0000000000 + + + 0 0 1 .0000000000 + 0 -1 0 .0000000000 + -1 0 0 .0000000000 + + + 0 0 1 .0000000000 + 0 -1 0 .0000000000 + 1 0 0 .0000000000 + + + 0 0 1 .0000000000 + -1 0 0 .0000000000 + 0 1 0 .0000000000 + + + 0 0 1 .0000000000 + 1 0 0 .0000000000 + 0 1 0 .0000000000 + + + 1 0 0 .0000000000 + 0 0 1 .0000000000 + 0 -1 0 .0000000000 + + + -1 0 0 .0000000000 + 0 0 1 .0000000000 + 0 -1 0 .0000000000 + + + 0 -1 0 .0000000000 + 0 0 1 .0000000000 + -1 0 0 .0000000000 + + + 0 -1 0 .0000000000 + 0 0 1 .0000000000 + 1 0 0 .0000000000 + + + 0 1 0 .0000000000 + 0 0 1 .0000000000 + -1 0 0 .0000000000 + + + 0 1 0 .0000000000 + 0 0 1 .0000000000 + 1 0 0 .0000000000 + + + 1 0 0 .0000000000 + 0 0 1 .0000000000 + 0 1 0 .0000000000 + + + -1 0 0 .0000000000 + 0 0 1 .0000000000 + 0 1 0 .0000000000 + + diff --git a/tests/files/inpxml/FePt/31/inp.xml b/tests/files/inpxml/FePt/31/inp.xml new file mode 100644 index 000000000..8780fb5c9 --- /dev/null +++ b/tests/files/inpxml/FePt/31/inp.xml @@ -0,0 +1,104 @@ + + + A Fleur input generator calculation with aiida + + + + + + + + + + 0.0 0.0 0.0 + + + + + + + 0.000000 0.000000 0.000000 + + + + + + + + + + + 1 0 0 .0000000000 + 0 1 0 .0000000000 + 0 0 1 .0000000000 + + + 1 0 0 .0000000000 + 0 -1 0 .0000000000 + 0 0 1 .0000000000 + + + + + 5.301179702900000 .000000000000000 .000000000000000 + .000000000000000 7.497000033000000 .000000000000000 + .000000000000000 .000000000000000 7.992850008800000 + + + + + + + + + + + + + + [Ar] + (4s1/2) (3d3/2) (3d5/2) + + + + + + + + + + + [Xe] (4f5/2) (4f7/2) + (6s1/2) (5d3/2) (5d5/2) + + + + + + + + .0000000000 .0000000000 -.9964250044 + + + + + 1.000/2.000 1.000/2.000 .9964250044 + + + + + + + + + + + + + + + + + + + diff --git a/tests/files/inpxml/FePt/32/inp.xml b/tests/files/inpxml/FePt/32/inp.xml new file mode 100644 index 000000000..8780fb5c9 --- /dev/null +++ b/tests/files/inpxml/FePt/32/inp.xml @@ -0,0 +1,104 @@ + + + A Fleur input generator calculation with aiida + + + + + + + + + + 0.0 0.0 0.0 + + + + + + + 0.000000 0.000000 0.000000 + + + + + + + + + + + 1 0 0 .0000000000 + 0 1 0 .0000000000 + 0 0 1 .0000000000 + + + 1 0 0 .0000000000 + 0 -1 0 .0000000000 + 0 0 1 .0000000000 + + + + + 5.301179702900000 .000000000000000 .000000000000000 + .000000000000000 7.497000033000000 .000000000000000 + .000000000000000 .000000000000000 7.992850008800000 + + + + + + + + + + + + + + [Ar] + (4s1/2) (3d3/2) (3d5/2) + + + + + + + + + + + [Xe] (4f5/2) (4f7/2) + (6s1/2) (5d3/2) (5d5/2) + + + + + + + + .0000000000 .0000000000 -.9964250044 + + + + + 1.000/2.000 1.000/2.000 .9964250044 + + + + + + + + + + + + + + + + + + + diff --git a/tests/files/inpxml/FePt/generate_inp.py b/tests/files/inpxml/FePt/generate_inp.py new file mode 100644 index 000000000..71a42f0d6 --- /dev/null +++ b/tests/files/inpxml/FePt/generate_inp.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +"""Helper for regenerating inp.xml files +""" +from aiida import load_profile +load_profile() + +from aiida.orm import load_node +from aiida.engine import run +from aiida.plugins import CalculationFactory +from aiida_fleur.data.fleurinp import FleurinpData + +inpgenc = CalculationFactory('fleur.inpgen') +path = './inp.xml' + +fleurinp = FleurinpData(inpxml=path) + +struc = fleurinp.get_structuredata_ncf() +param = fleurinp.get_parameterdata_ncf() +inpgen_code = load_node() + +runa = run(inpgenc, code=inpgen_code, structure=struc, calc_parameter=param) diff --git a/tests/files/inpxml/FePt/inp.xml b/tests/files/inpxml/FePt/inp.xml index 8780fb5c9..8f380bb99 100644 --- a/tests/files/inpxml/FePt/inp.xml +++ b/tests/files/inpxml/FePt/inp.xml @@ -1,104 +1,385 @@ - - - A Fleur input generator calculation with aiida + + + + A Fleur - - - - - - - - - 0.0 0.0 0.0 - - - - - - - 0.000000 0.000000 0.000000 - - - - - - - - - - - 1 0 0 .0000000000 - 0 1 0 .0000000000 - 0 0 1 .0000000000 - - - 1 0 0 .0000000000 - 0 -1 0 .0000000000 - 0 0 1 .0000000000 - - - - - 5.301179702900000 .000000000000000 .000000000000000 - .000000000000000 7.497000033000000 .000000000000000 - .000000000000000 .000000000000000 7.992850008800000 - - - - - - - - - - - - - - [Ar] - (4s1/2) (3d3/2) (3d5/2) - - - - - - - - - - - [Xe] (4f5/2) (4f7/2) - (6s1/2) (5d3/2) (5d5/2) - - - - - - - - .0000000000 .0000000000 -.9964250044 - - - - - 1.000/2.000 1.000/2.000 .9964250044 - - - - - - - - - - - - - - + + + + + + + .0000000000 .0000000000 .0000000000 + + + + + + + + + + + + + + + + 11.00/24.00 7.00/16.00 0.00/2.00 + 9.00/24.00 7.00/16.00 0.00/2.00 + 7.00/24.00 7.00/16.00 0.00/2.00 + 5.00/24.00 7.00/16.00 0.00/2.00 + 3.00/24.00 7.00/16.00 0.00/2.00 + 1.00/24.00 7.00/16.00 0.00/2.00 + 11.00/24.00 5.00/16.00 0.00/2.00 + 9.00/24.00 5.00/16.00 0.00/2.00 + 7.00/24.00 5.00/16.00 0.00/2.00 + 5.00/24.00 5.00/16.00 0.00/2.00 + 3.00/24.00 5.00/16.00 0.00/2.00 + 1.00/24.00 5.00/16.00 0.00/2.00 + 11.00/24.00 3.00/16.00 0.00/2.00 + 9.00/24.00 3.00/16.00 0.00/2.00 + 7.00/24.00 3.00/16.00 0.00/2.00 + 5.00/24.00 3.00/16.00 0.00/2.00 + 3.00/24.00 3.00/16.00 0.00/2.00 + 1.00/24.00 3.00/16.00 0.00/2.00 + 11.00/24.00 1.00/16.00 0.00/2.00 + 9.00/24.00 1.00/16.00 0.00/2.00 + 7.00/24.00 1.00/16.00 0.00/2.00 + 5.00/24.00 1.00/16.00 0.00/2.00 + 3.00/24.00 1.00/16.00 0.00/2.00 + 1.00/24.00 1.00/16.00 0.00/2.00 + + + 0.0000000000000 0.0000000000000 0.0000000000000 + 0.0071428571429 0.0000000000000 0.0000000000000 + 0.0142857142857 0.0000000000000 0.0000000000000 + 0.0214285714286 0.0000000000000 0.0000000000000 + 0.0285714285714 0.0000000000000 0.0000000000000 + 0.0357142857143 0.0000000000000 0.0000000000000 + 0.0428571428571 0.0000000000000 0.0000000000000 + 0.0500000000000 0.0000000000000 0.0000000000000 + 0.0571428571429 0.0000000000000 0.0000000000000 + 0.0642857142857 0.0000000000000 0.0000000000000 + 0.0714285714286 0.0000000000000 0.0000000000000 + 0.0785714285714 0.0000000000000 0.0000000000000 + 0.0857142857143 0.0000000000000 0.0000000000000 + 0.0928571428571 0.0000000000000 0.0000000000000 + 0.1000000000000 0.0000000000000 0.0000000000000 + 0.1071428571429 0.0000000000000 0.0000000000000 + 0.1142857142857 0.0000000000000 0.0000000000000 + 0.1214285714286 0.0000000000000 0.0000000000000 + 0.1285714285714 0.0000000000000 0.0000000000000 + 0.1357142857143 0.0000000000000 0.0000000000000 + 0.1428571428571 0.0000000000000 0.0000000000000 + 0.1500000000000 0.0000000000000 0.0000000000000 + 0.1571428571429 0.0000000000000 0.0000000000000 + 0.1642857142857 0.0000000000000 0.0000000000000 + 0.1714285714286 0.0000000000000 0.0000000000000 + 0.1785714285714 0.0000000000000 0.0000000000000 + 0.1857142857143 0.0000000000000 0.0000000000000 + 0.1928571428571 0.0000000000000 0.0000000000000 + 0.2000000000000 0.0000000000000 0.0000000000000 + 0.2071428571429 0.0000000000000 0.0000000000000 + 0.2142857142857 0.0000000000000 0.0000000000000 + 0.2214285714286 0.0000000000000 0.0000000000000 + 0.2285714285714 0.0000000000000 0.0000000000000 + 0.2357142857143 0.0000000000000 0.0000000000000 + 0.2428571428571 0.0000000000000 0.0000000000000 + 0.2500000000000 0.0000000000000 0.0000000000000 + 0.2571428571429 0.0000000000000 0.0000000000000 + 0.2642857142857 0.0000000000000 0.0000000000000 + 0.2714285714286 0.0000000000000 0.0000000000000 + 0.2785714285714 0.0000000000000 0.0000000000000 + 0.2857142857143 0.0000000000000 0.0000000000000 + 0.2928571428571 0.0000000000000 0.0000000000000 + 0.3000000000000 0.0000000000000 0.0000000000000 + 0.3071428571429 0.0000000000000 0.0000000000000 + 0.3142857142857 0.0000000000000 0.0000000000000 + 0.3214285714286 0.0000000000000 0.0000000000000 + 0.3285714285714 0.0000000000000 0.0000000000000 + 0.3357142857143 0.0000000000000 0.0000000000000 + 0.3428571428571 0.0000000000000 0.0000000000000 + 0.3500000000000 0.0000000000000 0.0000000000000 + 0.3571428571429 0.0000000000000 0.0000000000000 + 0.3642857142857 0.0000000000000 0.0000000000000 + 0.3714285714286 0.0000000000000 0.0000000000000 + 0.3785714285714 0.0000000000000 0.0000000000000 + 0.3857142857143 0.0000000000000 0.0000000000000 + 0.3928571428571 0.0000000000000 0.0000000000000 + 0.4000000000000 0.0000000000000 0.0000000000000 + 0.4071428571429 0.0000000000000 0.0000000000000 + 0.4142857142857 0.0000000000000 0.0000000000000 + 0.4214285714286 0.0000000000000 0.0000000000000 + 0.4285714285714 0.0000000000000 0.0000000000000 + 0.4357142857143 0.0000000000000 0.0000000000000 + 0.4428571428571 0.0000000000000 0.0000000000000 + 0.4500000000000 0.0000000000000 0.0000000000000 + 0.4571428571429 0.0000000000000 0.0000000000000 + 0.4642857142857 0.0000000000000 0.0000000000000 + 0.4714285714286 0.0000000000000 0.0000000000000 + 0.4785714285714 0.0000000000000 0.0000000000000 + 0.4857142857143 0.0000000000000 0.0000000000000 + 0.4928571428571 0.0000000000000 0.0000000000000 + 0.5000000000000 0.0000000000000 0.0000000000000 + 0.5000000000000 0.0102040816327 0.0000000000000 + 0.5000000000000 0.0204081632653 0.0000000000000 + 0.5000000000000 0.0306122448980 0.0000000000000 + 0.5000000000000 0.0408163265306 0.0000000000000 + 0.5000000000000 0.0510204081633 0.0000000000000 + 0.5000000000000 0.0612244897959 0.0000000000000 + 0.5000000000000 0.0714285714286 0.0000000000000 + 0.5000000000000 0.0816326530612 0.0000000000000 + 0.5000000000000 0.0918367346939 0.0000000000000 + 0.5000000000000 0.1020408163265 0.0000000000000 + 0.5000000000000 0.1122448979592 0.0000000000000 + 0.5000000000000 0.1224489795918 0.0000000000000 + 0.5000000000000 0.1326530612245 0.0000000000000 + 0.5000000000000 0.1428571428571 0.0000000000000 + 0.5000000000000 0.1530612244898 0.0000000000000 + 0.5000000000000 0.1632653061224 0.0000000000000 + 0.5000000000000 0.1734693877551 0.0000000000000 + 0.5000000000000 0.1836734693878 0.0000000000000 + 0.5000000000000 0.1938775510204 0.0000000000000 + 0.5000000000000 0.2040816326531 0.0000000000000 + 0.5000000000000 0.2142857142857 0.0000000000000 + 0.5000000000000 0.2244897959184 0.0000000000000 + 0.5000000000000 0.2346938775510 0.0000000000000 + 0.5000000000000 0.2448979591837 0.0000000000000 + 0.5000000000000 0.2551020408163 0.0000000000000 + 0.5000000000000 0.2653061224490 0.0000000000000 + 0.5000000000000 0.2755102040816 0.0000000000000 + 0.5000000000000 0.2857142857143 0.0000000000000 + 0.5000000000000 0.2959183673469 0.0000000000000 + 0.5000000000000 0.3061224489796 0.0000000000000 + 0.5000000000000 0.3163265306122 0.0000000000000 + 0.5000000000000 0.3265306122449 0.0000000000000 + 0.5000000000000 0.3367346938776 0.0000000000000 + 0.5000000000000 0.3469387755102 0.0000000000000 + 0.5000000000000 0.3571428571429 0.0000000000000 + 0.5000000000000 0.3673469387755 0.0000000000000 + 0.5000000000000 0.3775510204082 0.0000000000000 + 0.5000000000000 0.3877551020408 0.0000000000000 + 0.5000000000000 0.3979591836735 0.0000000000000 + 0.5000000000000 0.4081632653061 0.0000000000000 + 0.5000000000000 0.4183673469388 0.0000000000000 + 0.5000000000000 0.4285714285714 0.0000000000000 + 0.5000000000000 0.4387755102041 0.0000000000000 + 0.5000000000000 0.4489795918367 0.0000000000000 + 0.5000000000000 0.4591836734694 0.0000000000000 + 0.5000000000000 0.4693877551020 0.0000000000000 + 0.5000000000000 0.4795918367347 0.0000000000000 + 0.5000000000000 0.4897959183673 0.0000000000000 + 0.5000000000000 0.5000000000000 0.0000000000000 + 0.4928571428571 0.5000000000000 0.0000000000000 + 0.4857142857143 0.5000000000000 0.0000000000000 + 0.4785714285714 0.5000000000000 0.0000000000000 + 0.4714285714286 0.5000000000000 0.0000000000000 + 0.4642857142857 0.5000000000000 0.0000000000000 + 0.4571428571429 0.5000000000000 0.0000000000000 + 0.4500000000000 0.5000000000000 0.0000000000000 + 0.4428571428571 0.5000000000000 0.0000000000000 + 0.4357142857143 0.5000000000000 0.0000000000000 + 0.4285714285714 0.5000000000000 0.0000000000000 + 0.4214285714286 0.5000000000000 0.0000000000000 + 0.4142857142857 0.5000000000000 0.0000000000000 + 0.4071428571429 0.5000000000000 0.0000000000000 + 0.4000000000000 0.5000000000000 0.0000000000000 + 0.3928571428571 0.5000000000000 0.0000000000000 + 0.3857142857143 0.5000000000000 0.0000000000000 + 0.3785714285714 0.5000000000000 0.0000000000000 + 0.3714285714286 0.5000000000000 0.0000000000000 + 0.3642857142857 0.5000000000000 0.0000000000000 + 0.3571428571429 0.5000000000000 0.0000000000000 + 0.3500000000000 0.5000000000000 0.0000000000000 + 0.3428571428571 0.5000000000000 0.0000000000000 + 0.3357142857143 0.5000000000000 0.0000000000000 + 0.3285714285714 0.5000000000000 0.0000000000000 + 0.3214285714286 0.5000000000000 0.0000000000000 + 0.3142857142857 0.5000000000000 0.0000000000000 + 0.3071428571429 0.5000000000000 0.0000000000000 + 0.3000000000000 0.5000000000000 0.0000000000000 + 0.2928571428571 0.5000000000000 0.0000000000000 + 0.2857142857143 0.5000000000000 0.0000000000000 + 0.2785714285714 0.5000000000000 0.0000000000000 + 0.2714285714286 0.5000000000000 0.0000000000000 + 0.2642857142857 0.5000000000000 0.0000000000000 + 0.2571428571429 0.5000000000000 0.0000000000000 + 0.2500000000000 0.5000000000000 0.0000000000000 + 0.2428571428571 0.5000000000000 0.0000000000000 + 0.2357142857143 0.5000000000000 0.0000000000000 + 0.2285714285714 0.5000000000000 0.0000000000000 + 0.2214285714286 0.5000000000000 0.0000000000000 + 0.2142857142857 0.5000000000000 0.0000000000000 + 0.2071428571429 0.5000000000000 0.0000000000000 + 0.2000000000000 0.5000000000000 0.0000000000000 + 0.1928571428571 0.5000000000000 0.0000000000000 + 0.1857142857143 0.5000000000000 0.0000000000000 + 0.1785714285714 0.5000000000000 0.0000000000000 + 0.1714285714286 0.5000000000000 0.0000000000000 + 0.1642857142857 0.5000000000000 0.0000000000000 + 0.1571428571429 0.5000000000000 0.0000000000000 + 0.1500000000000 0.5000000000000 0.0000000000000 + 0.1428571428571 0.5000000000000 0.0000000000000 + 0.1357142857143 0.5000000000000 0.0000000000000 + 0.1285714285714 0.5000000000000 0.0000000000000 + 0.1214285714286 0.5000000000000 0.0000000000000 + 0.1142857142857 0.5000000000000 0.0000000000000 + 0.1071428571429 0.5000000000000 0.0000000000000 + 0.1000000000000 0.5000000000000 0.0000000000000 + 0.0928571428571 0.5000000000000 0.0000000000000 + 0.0857142857143 0.5000000000000 0.0000000000000 + 0.0785714285714 0.5000000000000 0.0000000000000 + 0.0714285714286 0.5000000000000 0.0000000000000 + 0.0642857142857 0.5000000000000 0.0000000000000 + 0.0571428571429 0.5000000000000 0.0000000000000 + 0.0500000000000 0.5000000000000 0.0000000000000 + 0.0428571428571 0.5000000000000 0.0000000000000 + 0.0357142857143 0.5000000000000 0.0000000000000 + 0.0285714285714 0.5000000000000 0.0000000000000 + 0.0214285714286 0.5000000000000 0.0000000000000 + 0.0142857142857 0.5000000000000 0.0000000000000 + 0.0071428571429 0.5000000000000 0.0000000000000 + 0.0000000000000 0.5000000000000 0.0000000000000 + 0.0000000000000 0.4900000000000 0.0000000000000 + 0.0000000000000 0.4800000000000 0.0000000000000 + 0.0000000000000 0.4700000000000 0.0000000000000 + 0.0000000000000 0.4600000000000 0.0000000000000 + 0.0000000000000 0.4500000000000 0.0000000000000 + 0.0000000000000 0.4400000000000 0.0000000000000 + 0.0000000000000 0.4300000000000 0.0000000000000 + 0.0000000000000 0.4200000000000 0.0000000000000 + 0.0000000000000 0.4100000000000 0.0000000000000 + 0.0000000000000 0.4000000000000 0.0000000000000 + 0.0000000000000 0.3900000000000 0.0000000000000 + 0.0000000000000 0.3800000000000 0.0000000000000 + 0.0000000000000 0.3700000000000 0.0000000000000 + 0.0000000000000 0.3600000000000 0.0000000000000 + 0.0000000000000 0.3500000000000 0.0000000000000 + 0.0000000000000 0.3400000000000 0.0000000000000 + 0.0000000000000 0.3300000000000 0.0000000000000 + 0.0000000000000 0.3200000000000 0.0000000000000 + 0.0000000000000 0.3100000000000 0.0000000000000 + 0.0000000000000 0.3000000000000 0.0000000000000 + 0.0000000000000 0.2900000000000 0.0000000000000 + 0.0000000000000 0.2800000000000 0.0000000000000 + 0.0000000000000 0.2700000000000 0.0000000000000 + 0.0000000000000 0.2600000000000 0.0000000000000 + 0.0000000000000 0.2500000000000 0.0000000000000 + 0.0000000000000 0.2400000000000 0.0000000000000 + 0.0000000000000 0.2300000000000 0.0000000000000 + 0.0000000000000 0.2200000000000 0.0000000000000 + 0.0000000000000 0.2100000000000 0.0000000000000 + 0.0000000000000 0.2000000000000 0.0000000000000 + 0.0000000000000 0.1900000000000 0.0000000000000 + 0.0000000000000 0.1800000000000 0.0000000000000 + 0.0000000000000 0.1700000000000 0.0000000000000 + 0.0000000000000 0.1600000000000 0.0000000000000 + 0.0000000000000 0.1500000000000 0.0000000000000 + 0.0000000000000 0.1400000000000 0.0000000000000 + 0.0000000000000 0.1300000000000 0.0000000000000 + 0.0000000000000 0.1200000000000 0.0000000000000 + 0.0000000000000 0.1100000000000 0.0000000000000 + 0.0000000000000 0.1000000000000 0.0000000000000 + 0.0000000000000 0.0900000000000 0.0000000000000 + 0.0000000000000 0.0800000000000 0.0000000000000 + 0.0000000000000 0.0700000000000 0.0000000000000 + 0.0000000000000 0.0600000000000 0.0000000000000 + 0.0000000000000 0.0500000000000 0.0000000000000 + 0.0000000000000 0.0400000000000 0.0000000000000 + 0.0000000000000 0.0300000000000 0.0000000000000 + 0.0000000000000 0.0200000000000 0.0000000000000 + 0.0000000000000 0.0100000000000 0.0000000000000 + 0.0000000000000 0.0000000000000 0.0000000000000 + + + + + + 1 0 0 .0000000000 + 0 1 0 .0000000000 + 0 0 1 .0000000000 + + + -1 0 0 .0000000000 + 0 1 0 .0000000000 + 0 0 1 .0000000000 + + + 1 0 0 .0000000000 + 0 -1 0 .0000000000 + 0 0 1 .0000000000 + + + -1 0 0 .0000000000 + 0 -1 0 .0000000000 + 0 0 1 .0000000000 + + + + + 5.3011797030 0.0000000000 0.0000000000 + 0.0000000000 7.4970000330 0.0000000000 + 0.0000000000 0.0000000000 9.4500000000 + + + + + + + + + + + (1s1/2) (2s1/2) (2p1/2) (2p3/2) + (3s1/2) (3p1/2) (3p3/2) (4s1/2) (3d3/2) (3d5/2) + + + + + + + + + + + + + + (1s1/2) (2s1/2) (2p1/2) (2p3/2) (3s1/2) (3p1/2) (3p3/2) (4s1/2) (3d3/2) (3d5/2) (4p1/2) (4p3/2) (5s1/2) (4d3/2) (4d5/2) (4f5/2) (4f7/2) + (5p1/2) (5p3/2) (6s1/2) (5d3/2) (5d5/2) + + + + + + + + + + + + + .0000000000 .0000000000 -.9964250044 + + + + + 1.000/2.000 1.000/2.000 .9964250044 + + + + + + + + + + + + + + - - - + diff --git a/tests/files/outxml/all_test/fleur_32_out.xml b/tests/files/outxml/all_test/fleur_32_out.xml new file mode 100644 index 000000000..2b43216b5 --- /dev/null +++ b/tests/files/outxml/all_test/fleur_32_out.xml @@ -0,0 +1,1962 @@ + + + + + + GEN + + + CPP_MPI CPP_HDF + + + + + + + + + + + A Fleur + + + + + + + + + + + + .0000000000 .0000000000 .0000000000 + + + + + + + + + + + + + + + + + + + + + + + + + 7.00/16.00 7.00/ + 16.00 7.00/16.00 + + 5.00/16.00 7.00/ + 16.00 7.00/16.00 + + 3.00/16.00 7.00/ + 16.00 7.00/16.00 + + 1.00/16.00 7.00/ + 16.00 7.00/16.00 + + 1.00/16.00 8.00/ + 16.00 8.00/16.00 + + 3.00/16.00 9.00/ + 16.00 9.00/16.00 + + 5.00/16.00 9.00/ + 16.00 9.00/16.00 + + 7.00/16.00 7.00/ + 16.00 9.00/16.00 + + 5.00/16.00 5.00/ + 16.00 7.00/16.00 + + 3.00/16.00 5.00/ + 16.00 7.00/16.00 + + 2.00/16.00 6.00/ + 16.00 7.00/16.00 + + 2.00/16.00 7.00/ + 16.00 8.00/16.00 + + 3.00/16.00 8.00/ + 16.00 10.00/16.00 + + 5.00/16.00 7.00/ + 16.00 11.00/16.00 + + 5.00/16.00 7.00/ + 16.00 9.00/16.00 + + 4.00/16.00 4.00/ + 16.00 7.00/16.00 + + 4.00/16.00 6.00/ + 16.00 7.00/16.00 + + 4.00/16.00 7.00/ + 16.00 8.00/16.00 + + 4.00/16.00 7.00/ + 16.00 10.00/16.00 + + 4.00/16.00 8.00/ + 16.00 11.00/16.00 + + 3.00/16.00 7.00/ + 16.00 9.00/16.00 + + 6.00/16.00 6.00/ + 16.00 7.00/16.00 + + 6.00/16.00 7.00/ + 16.00 8.00/16.00 + + 6.00/16.00 7.00/ + 16.00 10.00/16.00 + + 4.00/16.00 9.00/ + 16.00 10.00/16.00 + + 2.00/16.00 8.00/ + 16.00 9.00/16.00 + + 7.00/16.00 8.00/ + 16.00 8.00/16.00 + + 6.00/16.00 8.00/ + 16.00 9.00/16.00 + + 4.00/16.00 8.00/ + 16.00 9.00/16.00 + + 6.00/16.00 6.00/ + 16.00 9.00/16.00 + + 4.00/16.00 6.00/ + 16.00 9.00/16.00 + + 5.00/16.00 5.00/ + 16.00 9.00/16.00 + + 5.00/16.00 5.00/ + 16.00 5.00/16.00 + + 3.00/16.00 5.00/ + 16.00 5.00/16.00 + + 1.00/16.00 5.00/ + 16.00 5.00/16.00 + + 1.00/16.00 6.00/ + 16.00 6.00/16.00 + + 3.00/16.00 8.00/ + 16.00 8.00/16.00 + + 6.00/16.00 6.00/ + 16.00 11.00/16.00 + + 3.00/16.00 3.00/ + 16.00 5.00/16.00 + + 2.00/16.00 4.00/ + 16.00 5.00/16.00 + + 2.00/16.00 5.00/ + 16.00 6.00/16.00 + + 3.00/16.00 6.00/ + 16.00 8.00/16.00 + + 5.00/16.00 8.00/ + 16.00 10.00/16.00 + + 4.00/16.00 4.00/ + 16.00 5.00/16.00 + + 4.00/16.00 5.00/ + 16.00 6.00/16.00 + + 4.00/16.00 5.00/ + 16.00 8.00/16.00 + + 5.00/16.00 6.00/ + 16.00 10.00/16.00 + + 5.00/16.00 6.00/ + 16.00 6.00/16.00 + + 5.00/16.00 6.00/ + 16.00 8.00/16.00 + + 5.00/16.00 8.00/ + 16.00 8.00/16.00 + + 3.00/16.00 3.00/ + 16.00 3.00/16.00 + + 1.00/16.00 3.00/ + 16.00 3.00/16.00 + + 1.00/16.00 4.00/ + 16.00 4.00/16.00 + + 3.00/16.00 6.00/ + 16.00 6.00/16.00 + + 2.00/16.00 2.00/ + 16.00 3.00/16.00 + + 2.00/16.00 3.00/ + 16.00 4.00/16.00 + + 3.00/16.00 4.00/ + 16.00 6.00/16.00 + + 3.00/16.00 4.00/ + 16.00 4.00/16.00 + + 1.00/16.00 1.00/ + 16.00 1.00/16.00 + + 1.00/16.00 2.00/ + 16.00 2.00/16.00 + + + + 0.5000000000000 0.5 + 000000000000 1.0000000000000 + + 0.4934210526316 0.49 + 34210526316 0.9868421052632 + + 0.4868421052632 0.48 + 68421052632 0.9736842105263 + + 0.4802631578947 0.48 + 02631578947 0.9605263157895 + + 0.4736842105263 0.47 + 36842105263 0.9473684210526 + + 0.4671052631579 0.46 + 71052631579 0.9342105263158 + + 0.4605263157895 0.46 + 05263157895 0.9210526315789 + + 0.4539473684211 0.45 + 39473684211 0.9078947368421 + + 0.4473684210526 0.44 + 73684210526 0.8947368421053 + + 0.4407894736842 0.44 + 07894736842 0.8815789473684 + + 0.4342105263158 0.43 + 42105263158 0.8684210526316 + + 0.4276315789474 0.42 + 76315789474 0.8552631578947 + + 0.4210526315789 0.42 + 10526315789 0.8421052631579 + + 0.4144736842105 0.41 + 44736842105 0.8289473684211 + + 0.4078947368421 0.40 + 78947368421 0.8157894736842 + + 0.4013157894737 0.40 + 13157894737 0.8026315789474 + + 0.3947368421053 0.39 + 47368421053 0.7894736842105 + + 0.3881578947368 0.38 + 81578947368 0.7763157894737 + + 0.3815789473684 0.38 + 15789473684 0.7631578947368 + + 0.3750000000000 0.3 + 750000000000 0.7500000000000 + + 0.3683035714286 0.36 + 83035714286 0.7366071428571 + + 0.3616071428571 0.36 + 16071428571 0.7232142857143 + + 0.3549107142857 0.35 + 49107142857 0.7098214285714 + + 0.3482142857143 0.34 + 82142857143 0.6964285714286 + + 0.3415178571429 0.34 + 15178571429 0.6830357142857 + + 0.3348214285714 0.33 + 48214285714 0.6696428571429 + + 0.3281250000000 0.32 + 81250000000 0.6562500000000 + + 0.3214285714286 0.32 + 14285714286 0.6428571428571 + + 0.3147321428571 0.31 + 47321428571 0.6294642857143 + + 0.3080357142857 0.30 + 80357142857 0.6160714285714 + + 0.3013392857143 0.30 + 13392857143 0.6026785714286 + + 0.2946428571429 0.29 + 46428571429 0.5892857142857 + + 0.2879464285714 0.28 + 79464285714 0.5758928571429 + + 0.2812500000000 0.28 + 12500000000 0.5625000000000 + + 0.2745535714286 0.27 + 45535714286 0.5491071428571 + + 0.2678571428571 0.26 + 78571428571 0.5357142857143 + + 0.2611607142857 0.26 + 11607142857 0.5223214285714 + + 0.2544642857143 0.25 + 44642857143 0.5089285714286 + + 0.2477678571429 0.24 + 77678571429 0.4955357142857 + + 0.2410714285714 0.24 + 10714285714 0.4821428571429 + + 0.2343750000000 0.23 + 43750000000 0.4687500000000 + + 0.2276785714286 0.22 + 76785714286 0.4553571428571 + + 0.2209821428571 0.22 + 09821428571 0.4419642857143 + + 0.2142857142857 0.21 + 42857142857 0.4285714285714 + + 0.2075892857143 0.20 + 75892857143 0.4151785714286 + + 0.2008928571429 0.20 + 08928571429 0.4017857142857 + + 0.1941964285714 0.19 + 41964285714 0.3883928571429 + + 0.1875000000000 0.18 + 75000000000 0.3750000000000 + + 0.1808035714286 0.18 + 08035714286 0.3616071428571 + + 0.1741071428571 0.17 + 41071428571 0.3482142857143 + + 0.1674107142857 0.16 + 74107142857 0.3348214285714 + + 0.1607142857143 0.16 + 07142857143 0.3214285714286 + + 0.1540178571429 0.15 + 40178571429 0.3080357142857 + + 0.1473214285714 0.14 + 73214285714 0.2946428571429 + + 0.1406250000000 0.14 + 06250000000 0.2812500000000 + + 0.1339285714286 0.13 + 39285714286 0.2678571428571 + + 0.1272321428571 0.12 + 72321428571 0.2544642857143 + + 0.1205357142857 0.12 + 05357142857 0.2410714285714 + + 0.1138392857143 0.11 + 38392857143 0.2276785714286 + + 0.1071428571429 0.10 + 71428571429 0.2142857142857 + + 0.1004464285714 0.10 + 04464285714 0.2008928571429 + + 0.0937500000000 0.09 + 37500000000 0.1875000000000 + + 0.0870535714286 0.08 + 70535714286 0.1741071428571 + + 0.0803571428571 0.08 + 03571428571 0.1607142857143 + + 0.0736607142857 0.07 + 36607142857 0.1473214285714 + + 0.0669642857143 0.06 + 69642857143 0.1339285714286 + + 0.0602678571429 0.06 + 02678571429 0.1205357142857 + + 0.0535714285714 0.05 + 35714285714 0.1071428571429 + + 0.0468750000000 0.04 + 68750000000 0.0937500000000 + + 0.0401785714286 0.04 + 01785714286 0.0803571428571 + + 0.0334821428571 0.03 + 34821428571 0.0669642857143 + + 0.0267857142857 0.02 + 67857142857 0.0535714285714 + + 0.0200892857143 0.02 + 00892857143 0.0401785714286 + + 0.0133928571429 0.01 + 33928571429 0.0267857142857 + + 0.0066964285714 0.00 + 66964285714 0.0133928571429 + + 0.0000000000000 0.0 + 000000000000 0.0000000000000 + + 0.0108695652174 0.01 + 08695652174 0.0108695652174 + + 0.0217391304348 0.02 + 17391304348 0.0217391304348 + + 0.0326086956522 0.03 + 26086956522 0.0326086956522 + + 0.0434782608696 0.04 + 34782608696 0.0434782608696 + + 0.0543478260870 0.05 + 43478260870 0.0543478260870 + + 0.0652173913043 0.06 + 52173913043 0.0652173913043 + + 0.0760869565217 0.07 + 60869565217 0.0760869565217 + + 0.0869565217391 0.08 + 69565217391 0.0869565217391 + + 0.0978260869565 0.09 + 78260869565 0.0978260869565 + + 0.1086956521739 0.10 + 86956521739 0.1086956521739 + + 0.1195652173913 0.11 + 95652173913 0.1195652173913 + + 0.1304347826087 0.13 + 04347826087 0.1304347826087 + + 0.1413043478261 0.14 + 13043478261 0.1413043478261 + + 0.1521739130435 0.15 + 21739130435 0.1521739130435 + + 0.1630434782609 0.16 + 30434782609 0.1630434782609 + + 0.1739130434783 0.17 + 39130434783 0.1739130434783 + + 0.1847826086957 0.18 + 47826086957 0.1847826086957 + + 0.1956521739130 0.19 + 56521739130 0.1956521739130 + + 0.2065217391304 0.20 + 65217391304 0.2065217391304 + + 0.2173913043478 0.21 + 73913043478 0.2173913043478 + + 0.2282608695652 0.22 + 82608695652 0.2282608695652 + + 0.2391304347826 0.23 + 91304347826 0.2391304347826 + + 0.2500000000000 0.25 + 00000000000 0.2500000000000 + + 0.2608695652174 0.26 + 08695652174 0.2608695652174 + + 0.2717391304348 0.27 + 17391304348 0.2717391304348 + + 0.2826086956522 0.28 + 26086956522 0.2826086956522 + + 0.2934782608696 0.29 + 34782608696 0.2934782608696 + + 0.3043478260870 0.30 + 43478260870 0.3043478260870 + + 0.3152173913043 0.31 + 52173913043 0.3152173913043 + + 0.3260869565217 0.32 + 60869565217 0.3260869565217 + + 0.3369565217391 0.33 + 69565217391 0.3369565217391 + + 0.3478260869565 0.34 + 78260869565 0.3478260869565 + + 0.3586956521739 0.35 + 86956521739 0.3586956521739 + + 0.3695652173913 0.36 + 95652173913 0.3695652173913 + + 0.3804347826087 0.38 + 04347826087 0.3804347826087 + + 0.3913043478261 0.39 + 13043478261 0.3913043478261 + + 0.4021739130435 0.40 + 21739130435 0.4021739130435 + + 0.4130434782609 0.41 + 30434782609 0.4130434782609 + + 0.4239130434783 0.42 + 39130434783 0.4239130434783 + + 0.4347826086957 0.43 + 47826086957 0.4347826086957 + + 0.4456521739130 0.44 + 56521739130 0.4456521739130 + + 0.4565217391304 0.45 + 65217391304 0.4565217391304 + + 0.4673913043478 0.46 + 73913043478 0.4673913043478 + + 0.4782608695652 0.47 + 82608695652 0.4782608695652 + + 0.4891304347826 0.48 + 91304347826 0.4891304347826 + + 0.5000000000000 0.5 + 000000000000 0.5000000000000 + + 0.5000000000000 0.49 + 34210526316 0.5065789473684 + + 0.5000000000000 0.48 + 68421052632 0.5131578947368 + + 0.5000000000000 0.48 + 02631578947 0.5197368421053 + + 0.5000000000000 0.47 + 36842105263 0.5263157894737 + + 0.5000000000000 0.46 + 71052631579 0.5328947368421 + + 0.5000000000000 0.46 + 05263157895 0.5394736842105 + + 0.5000000000000 0.45 + 39473684211 0.5460526315789 + + 0.5000000000000 0.44 + 73684210526 0.5526315789474 + + 0.5000000000000 0.44 + 07894736842 0.5592105263158 + + 0.5000000000000 0.43 + 42105263158 0.5657894736842 + + 0.5000000000000 0.42 + 76315789474 0.5723684210526 + + 0.5000000000000 0.42 + 10526315789 0.5789473684211 + + 0.5000000000000 0.41 + 44736842105 0.5855263157895 + + 0.5000000000000 0.40 + 78947368421 0.5921052631579 + + 0.5000000000000 0.40 + 13157894737 0.5986842105263 + + 0.5000000000000 0.39 + 47368421053 0.6052631578947 + + 0.5000000000000 0.38 + 81578947368 0.6118421052632 + + 0.5000000000000 0.38 + 15789473684 0.6184210526316 + + 0.5000000000000 0.37 + 50000000000 0.6250000000000 + + 0.5000000000000 0.36 + 84210526316 0.6315789473684 + + 0.5000000000000 0.36 + 18421052632 0.6381578947368 + + 0.5000000000000 0.35 + 52631578947 0.6447368421053 + + 0.5000000000000 0.34 + 86842105263 0.6513157894737 + + 0.5000000000000 0.34 + 21052631579 0.6578947368421 + + 0.5000000000000 0.33 + 55263157895 0.6644736842105 + + 0.5000000000000 0.32 + 89473684211 0.6710526315789 + + 0.5000000000000 0.32 + 23684210526 0.6776315789474 + + 0.5000000000000 0.31 + 57894736842 0.6842105263158 + + 0.5000000000000 0.30 + 92105263158 0.6907894736842 + + 0.5000000000000 0.30 + 26315789474 0.6973684210526 + + 0.5000000000000 0.29 + 60526315789 0.7039473684211 + + 0.5000000000000 0.28 + 94736842105 0.7105263157895 + + 0.5000000000000 0.28 + 28947368421 0.7171052631579 + + 0.5000000000000 0.27 + 63157894737 0.7236842105263 + + 0.5000000000000 0.26 + 97368421053 0.7302631578947 + + 0.5000000000000 0.26 + 31578947368 0.7368421052632 + + 0.5000000000000 0.25 + 65789473684 0.7434210526316 + + 0.5000000000000 0.2 + 500000000000 0.7500000000000 + + 0.5000000000000 0.24 + 07407407407 0.7407407407407 + + 0.5000000000000 0.23 + 14814814815 0.7314814814815 + + 0.5000000000000 0.22 + 22222222222 0.7222222222222 + + 0.5000000000000 0.21 + 29629629630 0.7129629629630 + + 0.5000000000000 0.20 + 37037037037 0.7037037037037 + + 0.5000000000000 0.19 + 44444444444 0.6944444444444 + + 0.5000000000000 0.18 + 51851851852 0.6851851851852 + + 0.5000000000000 0.17 + 59259259259 0.6759259259259 + + 0.5000000000000 0.16 + 66666666667 0.6666666666667 + + 0.5000000000000 0.15 + 74074074074 0.6574074074074 + + 0.5000000000000 0.14 + 81481481481 0.6481481481481 + + 0.5000000000000 0.13 + 88888888889 0.6388888888889 + + 0.5000000000000 0.12 + 96296296296 0.6296296296296 + + 0.5000000000000 0.12 + 03703703704 0.6203703703704 + + 0.5000000000000 0.11 + 11111111111 0.6111111111111 + + 0.5000000000000 0.10 + 18518518519 0.6018518518519 + + 0.5000000000000 0.09 + 25925925926 0.5925925925926 + + 0.5000000000000 0.08 + 33333333333 0.5833333333333 + + 0.5000000000000 0.07 + 40740740741 0.5740740740741 + + 0.5000000000000 0.06 + 48148148148 0.5648148148148 + + 0.5000000000000 0.05 + 55555555556 0.5555555555556 + + 0.5000000000000 0.04 + 62962962963 0.5462962962963 + + 0.5000000000000 0.03 + 70370370370 0.5370370370370 + + 0.5000000000000 0.02 + 77777777778 0.5277777777778 + + 0.5000000000000 0.01 + 85185185185 0.5185185185185 + + 0.5000000000000 0.00 + 92592592593 0.5092592592593 + + 0.5000000000000 0.0 + 000000000000 0.5000000000000 + + 0.4905660377358 0.00 + 00000000000 0.4905660377358 + + 0.4811320754717 0.00 + 00000000000 0.4811320754717 + + 0.4716981132075 0.00 + 00000000000 0.4716981132075 + + 0.4622641509434 0.00 + 00000000000 0.4622641509434 + + 0.4528301886792 0.00 + 00000000000 0.4528301886792 + + 0.4433962264151 0.00 + 00000000000 0.4433962264151 + + 0.4339622641509 0.00 + 00000000000 0.4339622641509 + + 0.4245283018868 0.00 + 00000000000 0.4245283018868 + + 0.4150943396226 0.00 + 00000000000 0.4150943396226 + + 0.4056603773585 0.00 + 00000000000 0.4056603773585 + + 0.3962264150943 0.00 + 00000000000 0.3962264150943 + + 0.3867924528302 0.00 + 00000000000 0.3867924528302 + + 0.3773584905660 0.00 + 00000000000 0.3773584905660 + + 0.3679245283019 0.00 + 00000000000 0.3679245283019 + + 0.3584905660377 0.00 + 00000000000 0.3584905660377 + + 0.3490566037736 0.00 + 00000000000 0.3490566037736 + + 0.3396226415094 0.00 + 00000000000 0.3396226415094 + + 0.3301886792453 0.00 + 00000000000 0.3301886792453 + + 0.3207547169811 0.00 + 00000000000 0.3207547169811 + + 0.3113207547170 0.00 + 00000000000 0.3113207547170 + + 0.3018867924528 0.00 + 00000000000 0.3018867924528 + + 0.2924528301887 0.00 + 00000000000 0.2924528301887 + + 0.2830188679245 0.00 + 00000000000 0.2830188679245 + + 0.2735849056604 0.00 + 00000000000 0.2735849056604 + + 0.2641509433962 0.00 + 00000000000 0.2641509433962 + + 0.2547169811321 0.00 + 00000000000 0.2547169811321 + + 0.2452830188679 0.00 + 00000000000 0.2452830188679 + + 0.2358490566038 0.00 + 00000000000 0.2358490566038 + + 0.2264150943396 0.00 + 00000000000 0.2264150943396 + + 0.2169811320755 0.00 + 00000000000 0.2169811320755 + + 0.2075471698113 0.00 + 00000000000 0.2075471698113 + + 0.1981132075472 0.00 + 00000000000 0.1981132075472 + + 0.1886792452830 0.00 + 00000000000 0.1886792452830 + + 0.1792452830189 0.00 + 00000000000 0.1792452830189 + + 0.1698113207547 0.00 + 00000000000 0.1698113207547 + + 0.1603773584906 0.00 + 00000000000 0.1603773584906 + + 0.1509433962264 0.00 + 00000000000 0.1509433962264 + + 0.1415094339623 0.00 + 00000000000 0.1415094339623 + + 0.1320754716981 0.00 + 00000000000 0.1320754716981 + + 0.1226415094340 0.00 + 00000000000 0.1226415094340 + + 0.1132075471698 0.00 + 00000000000 0.1132075471698 + + 0.1037735849057 0.00 + 00000000000 0.1037735849057 + + 0.0943396226415 0.00 + 00000000000 0.0943396226415 + + 0.0849056603774 0.00 + 00000000000 0.0849056603774 + + 0.0754716981132 0.00 + 00000000000 0.0754716981132 + + 0.0660377358491 0.00 + 00000000000 0.0660377358491 + + 0.0566037735849 0.00 + 00000000000 0.0566037735849 + + 0.0471698113208 0.00 + 00000000000 0.0471698113208 + + 0.0377358490566 0.00 + 00000000000 0.0377358490566 + + 0.0283018867925 0.00 + 00000000000 0.0283018867925 + + 0.0188679245283 0.00 + 00000000000 0.0188679245283 + + 0.0094339622642 0.00 + 00000000000 0.0094339622642 + + 0.0000000000000 0.0 + 000000000000 0.0000000000000 + + + + + + 1 0 0 .0000000000 + 0 1 0 .0000000000 + 0 0 1 .0000000000 + + + 0 1 0 .0000000000 + 1 0 0 .0000000000 + 0 0 1 .0000000000 + + + -1 0 0 .0000000000 + 0 -1 0 .0000000000 + 1 1 1 .5000000000 + + + 0 -1 0 .0000000000 + -1 0 0 .0000000000 + 1 1 1 .5000000000 + + + 0 1 0 .0000000000 + 1 0 0 .0000000000 + -1 -1 -1 .5000000000 + + + 1 0 0 .0000000000 + 0 1 0 .0000000000 + -1 -1 -1 .5000000000 + + + -1 0 0 .0000000000 + 0 -1 0 .0000000000 + 0 0 -1 .0000000000 + + + 0 -1 0 .0000000000 + -1 0 0 .0000000000 + 0 0 -1 .0000000000 + + + 0 0 1 .0000000000 + 1 0 0 .0000000000 + -1 -1 -1 .5000000000 + + + 0 0 1 .0000000000 + 0 1 0 .0000000000 + -1 -1 -1 .5000000000 + + + 1 1 1 .5000000000 + 0 -1 0 .0000000000 + 0 0 -1 .0000000000 + + + 1 1 1 .5000000000 + -1 0 0 .0000000000 + 0 0 -1 .0000000000 + + + -1 0 0 .0000000000 + 1 1 1 .5000000000 + 0 0 -1 .0000000000 + + + 1 0 0 .0000000000 + 0 0 1 .0000000000 + -1 -1 -1 .5000000000 + + + 0 1 0 .0000000000 + 0 0 1 .0000000000 + -1 -1 -1 .5000000000 + + + 0 -1 0 .0000000000 + 1 1 1 .5000000000 + 0 0 -1 .0000000000 + + + -1 0 0 .0000000000 + 0 0 -1 .0000000000 + 0 -1 0 .0000000000 + + + 0 1 0 .0000000000 + -1 -1 -1 .5000000000 + 1 0 0 .0000000000 + + + 0 -1 0 .0000000000 + 0 0 -1 .0000000000 + -1 0 0 .0000000000 + + + 1 0 0 .0000000000 + -1 -1 -1 .5000000000 + 0 1 0 .0000000000 + + + 1 1 1 .5000000000 + 0 0 -1 .0000000000 + 0 -1 0 .0000000000 + + + 0 0 1 .0000000000 + -1 -1 -1 .5000000000 + 1 0 0 .0000000000 + + + 1 1 1 .5000000000 + 0 0 -1 .0000000000 + -1 0 0 .0000000000 + + + 0 0 1 .0000000000 + -1 -1 -1 .5000000000 + 0 1 0 .0000000000 + + + 0 0 -1 .0000000000 + -1 0 0 .0000000000 + 0 -1 0 .0000000000 + + + 0 0 -1 .0000000000 + 0 -1 0 .0000000000 + -1 0 0 .0000000000 + + + -1 -1 -1 .5000000000 + 0 1 0 .0000000000 + 1 0 0 .0000000000 + + + -1 -1 -1 .5000000000 + 1 0 0 .0000000000 + 0 1 0 .0000000000 + + + 0 0 1 .0000000000 + 0 1 0 .0000000000 + 1 0 0 .0000000000 + + + 1 1 1 .5000000000 + -1 0 0 .0000000000 + 0 -1 0 .0000000000 + + + 1 1 1 .5000000000 + 0 -1 0 .0000000000 + -1 0 0 .0000000000 + + + 0 0 1 .0000000000 + 1 0 0 .0000000000 + 0 1 0 .0000000000 + + + 0 0 -1 .0000000000 + 1 1 1 .5000000000 + -1 0 0 .0000000000 + + + 0 0 -1 .0000000000 + 1 1 1 .5000000000 + 0 -1 0 .0000000000 + + + -1 -1 -1 .5000000000 + 0 0 1 .0000000000 + 1 0 0 .0000000000 + + + -1 -1 -1 .5000000000 + 0 0 1 .0000000000 + 0 1 0 .0000000000 + + + -1 0 0 .0000000000 + 1 1 1 .5000000000 + 0 -1 0 .0000000000 + + + 0 1 0 .0000000000 + 0 0 1 .0000000000 + 1 0 0 .0000000000 + + + 0 -1 0 .0000000000 + 1 1 1 .5000000000 + -1 0 0 .0000000000 + + + 1 0 0 .0000000000 + 0 0 1 .0000000000 + 0 1 0 .0000000000 + + + 1 0 0 .0000000000 + -1 -1 -1 .5000000000 + 0 0 1 .0000000000 + + + 0 1 0 .0000000000 + -1 -1 -1 .5000000000 + 0 0 1 .0000000000 + + + 0 -1 0 .0000000000 + 0 0 -1 .0000000000 + 1 1 1 .5000000000 + + + -1 0 0 .0000000000 + 0 0 -1 .0000000000 + 1 1 1 .5000000000 + + + -1 -1 -1 .5000000000 + 1 0 0 .0000000000 + 0 0 1 .0000000000 + + + -1 -1 -1 .5000000000 + 0 1 0 .0000000000 + 0 0 1 .0000000000 + + + 0 0 -1 .0000000000 + 0 -1 0 .0000000000 + 1 1 1 .5000000000 + + + 0 0 -1 .0000000000 + -1 0 0 .0000000000 + 1 1 1 .5000000000 + + + + + 0.0000000000 5.1535387520 5.1535387520 + 5.1535387520 0.0000000000 5.1535387520 + 5.1535387520 5.1535387520 0.0000000000 + + + + + + + + + + (1s1/2) (2s1/2) (2p1/2) (2p3/2) + (3s1/2) (3p1/2) (3p3/2) + + + + + + + + + + + + + + + 1.000/8.000 1.000/8.000 + 1.000/8.000 + + -1.000/8.000 -1.000/8.00 + 0 -1.000/8.000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.437500 0.437500 0.437500 + 0.312500 0.437500 0.437500 + 0.187500 0.437500 0.437500 + 0.062500 0.437500 0.437500 + 0.062500 0.500000 0.500000 + 0.187500 0.562500 0.562500 + 0.312500 0.562500 0.562500 + 0.437500 0.437500 0.562500 + 0.312500 0.312500 0.437500 + 0.187500 0.312500 0.437500 + 0.125000 0.375000 0.437500 + 0.125000 0.437500 0.500000 + 0.187500 0.500000 0.625000 + 0.312500 0.437500 0.687500 + 0.312500 0.437500 0.562500 + 0.250000 0.250000 0.437500 + 0.250000 0.375000 0.437500 + 0.250000 0.437500 0.500000 + 0.250000 0.437500 0.625000 + 0.250000 0.500000 0.687500 + 0.187500 0.437500 0.562500 + 0.375000 0.375000 0.437500 + 0.375000 0.437500 0.500000 + 0.375000 0.437500 0.625000 + 0.250000 0.562500 0.625000 + 0.125000 0.500000 0.562500 + 0.437500 0.500000 0.500000 + 0.375000 0.500000 0.562500 + 0.250000 0.500000 0.562500 + 0.375000 0.375000 0.562500 + 0.250000 0.375000 0.562500 + 0.312500 0.312500 0.562500 + 0.312500 0.312500 0.312500 + 0.187500 0.312500 0.312500 + 0.062500 0.312500 0.312500 + 0.062500 0.375000 0.375000 + 0.187500 0.500000 0.500000 + 0.375000 0.375000 0.687500 + 0.187500 0.187500 0.312500 + 0.125000 0.250000 0.312500 + 0.125000 0.312500 0.375000 + 0.187500 0.375000 0.500000 + 0.312500 0.500000 0.625000 + 0.250000 0.250000 0.312500 + 0.250000 0.312500 0.375000 + 0.250000 0.312500 0.500000 + 0.312500 0.375000 0.625000 + 0.312500 0.375000 0.375000 + 0.312500 0.375000 0.500000 + 0.312500 0.500000 0.500000 + 0.187500 0.187500 0.187500 + 0.062500 0.187500 0.187500 + 0.062500 0.250000 0.250000 + 0.187500 0.375000 0.375000 + 0.125000 0.125000 0.187500 + 0.125000 0.187500 0.250000 + 0.187500 0.250000 0.375000 + 0.187500 0.250000 0.250000 + 0.062500 0.062500 0.062500 + 0.062500 0.125000 0.125000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/fleur_schema/__init__.py b/tests/fleur_schema/__init__.py deleted file mode 100644 index 713971d59..000000000 --- a/tests/fleur_schema/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- coding: utf-8 -*- -''' -AiiDA-FLEUR -''' - -__copyright__ = u'Copyright (c), 2015-2017, Forschungszentrum Juelich GmbH, Germany. All rights reserved.' -__license__ = 'MIT license, see LICENSE.txt file.' -__contributors__ = 'Jens Broeder' -__paper__ = '' -__paper_short__ = '' diff --git a/tests/fleur_schema/test_schema_files.py b/tests/fleur_schema/test_schema_files.py deleted file mode 100644 index b50697aae..000000000 --- a/tests/fleur_schema/test_schema_files.py +++ /dev/null @@ -1,14 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################### -# Copyright (c), Forschungszentrum Jülich GmbH, IAS-1/PGI-1, Germany. # -# All rights reserved. # -# This file is part of the AiiDA-FLEUR package. # -# # -# The code is hosted on GitHub at https://github.com/JuDFTteam/aiida-fleur # -# For further information on the license, see the LICENSE.txt file # -# For further information please visit http://www.flapw.de or # -# http://aiida-fleur.readthedocs.io/en/develop/ # -############################################################################### - -# test if every schema file in the folder is reconized by the plugin -# Ie the test should fail if a new schema file is added without adding it to the paths diff --git a/tests/parsers/test_fleur_parser.py b/tests/parsers/test_fleur_parser.py index e6cbe1a1b..5f5b3ca98 100644 --- a/tests/parsers/test_fleur_parser.py +++ b/tests/parsers/test_fleur_parser.py @@ -16,295 +16,16 @@ TEST_INP_XML_PATH1 = os.path.join(aiida_path, '../tests/parsers/fixtures/fleur/relax/inp.xml') -# parse_xmlout_file -def test_parse_xmlout_file(): - """ - tests if the routine that parsers the outputfile, produces the right output, no aiida datastructures, - with the right content - """ - from aiida_fleur.parsers.fleur import parse_xmlout_file - - filename = os.path.abspath('./files/outxml/BeTi_out.xml') - - simple_out, complex_out, parser_info_out, successful = parse_xmlout_file(filename) - - expected_simple_out_dict = { - 'bandgap': 0.0052350388, - 'bandgap_units': 'eV', - 'charge_den_xc_den_integral': -45.0947551412, - 'charge_density': 8.7984e-06, - 'creator_name': 'fleur 27', - 'creator_target_architecture': 'GEN', - 'creator_target_structure': ' ', - 'density_convergence_units': 'me/bohr^3', - 'energy': -23635.691961010132, - 'energy_core_electrons': -496.172547773, - 'energy_hartree': -868.5956587197, - 'energy_hartree_units': 'Htr', - 'energy_units': 'eV', - 'energy_valence_electrons': -7.1055909396, - 'fermi_energy': 0.3451127139, - 'fermi_energy_units': 'Htr', - 'force_largest': -0.0, - 'kmax': 4.5, - 'number_of_atom_types': 2, - 'number_of_atoms': 2, - 'number_of_iterations': 19, - 'number_of_iterations_total': 19, - 'number_of_kpoints': 56, - 'number_of_species': 1, - 'number_of_spin_components': 1, - 'number_of_symmetries': 48, - 'output_file_version': '0.27', - 'start_date': { - 'date': '2017/09/10', - 'time': '07:58:10' - }, - 'end_date': { - 'date': '2017/09/10', - 'time': '07:58:34' - }, - 'sum_of_eigenvalues': -503.2781387127, - 'title': 'Be-Ti, bulk compounds', - 'walltime': 24, - 'walltime_units': 'seconds', - 'warnings': { - 'debug': {}, - 'error': {}, - 'info': {}, - 'warning': {} - } - } - - expected_parser_info_out = {'parser_info': 'AiiDA Fleur Parser v0.3.2', 'parser_warnings': [], 'unparsed': []} - simple_out.pop('outputfile_path', None) # otherwise test will fail on different installations - # also this should go away any way... - - assert successful - assert expected_simple_out_dict == simple_out - assert expected_parser_info_out == parser_info_out - - -# test special cases parser behavior -def test_parse_xmlout_file_broken_xmlout_file(): - """ - tests the behavior of the parse_xmlout_file routine in the case of an broken out.xml file. - Here broken after serveral iteration, should parse all iteration except last. - (which can happen in the case of some kill, or Non regular termination of FLEUR) - """ - from aiida_fleur.parsers.fleur import parse_xmlout_file - - filename = os.path.abspath('./files/outxml/special/broken_BeTi_out.xml') - - simple_out, complex_out, parser_info_out, successful = parse_xmlout_file(filename) - - expected_parser_info_out = { - 'last_iteration_parsed': - 15, - 'parser_info': - 'AiiDA Fleur Parser v0.3.2', - 'parser_warnings': [ - 'The out.xml file is broken I try to repair it.', - 'Endtime was unparsed, inp.xml prob not complete, do not believe the walltime!' - ], - 'unparsed': [] - } - - assert successful - assert parser_info_out['last_iteration_parsed'] == 15 - assert expected_parser_info_out['unparsed'] == parser_info_out['unparsed'] - assert expected_parser_info_out['parser_warnings'] == parser_info_out['parser_warnings'] - - -def test_parse_xmlout_file_broken_first_xmlout_file(): - """ - tests the behavior of the parse_xmlout_file routine in the case of an broken out.xml file. - Here broken in first iteration, should parse nothing. - (which can happen in the case of some kill, or Non regular termination of FLEUR) - """ - from aiida_fleur.parsers.fleur import parse_xmlout_file - - filename = os.path.abspath('./files/outxml/special/broken_first_BeTi_out.xml') - - simple_out, complex_out, parser_info_out, successful = parse_xmlout_file(filename) - - expected_parser_info_out = { - 'last_iteration_parsed': - 1, - 'parser_info': - 'AiiDA Fleur Parser v0.3.2', - 'parser_warnings': [ - 'The out.xml file is broken I try to repair it.', - 'Can not get attributename: "units" from node "[]", because node is not an element of etree.', - 'Can not get attributename: "value" from node "[]", because node is not an element of etree.', - 'Could not convert: "None" to float, TypeError', 'Could not convert: "None" to float, TypeError', - 'Could not convert: "None" to float, TypeError', - 'Can not get attributename: "value" from node "[]", because node is not an element of etree.', - 'Could not convert: "None" to float, TypeError', - 'Can not get attributename: "value" from node "[]", because node is not an element of etree.', - 'Could not convert: "None" to float, TypeError', - 'Can not get attributename: "value" from node "[]", because node is not an element of etree.', - 'Could not convert: "None" to float, TypeError', - 'Can not get attributename: "value" from node "[]", because node is not an element of etree.', - 'Could not convert: "None" to float, TypeError', - 'Can not get attributename: "units" from node "[]", because node is not an element of etree.', - 'Can not get attributename: "value" from node "[]", because node is not an element of etree.', - 'Could not convert: "None" to float, TypeError', - 'Can not get attributename: "value" from node "[]", because node is not an element of etree.', - 'Could not convert: "None" to float, TypeError', - 'Can not get attributename: "units" from node "[]", because node is not an element of etree.', - 'Can not get attributename: "units" from node "[]", because node is not an element of etree.', - 'Can not get attributename: "distance" from node "[]", because node is not an element of etree.', - 'Could not convert: "None" to float, TypeError', - 'Endtime was unparsed, inp.xml prob not complete, do not believe the walltime!' - ], - 'unparsed': [{ - 'energy_hartree': None, - 'iteration': ' 1' - }, { - 'energy': None, - 'iteration': ' 1' - }, { - 'iteration': ' 1', - 'sum_of_eigenvalues': None - }, { - 'energy_core_electrons': None, - 'iteration': ' 1' - }, { - 'energy_valence_electrons': None, - 'iteration': ' 1' - }, { - 'charge_den_xc_den_integral': None, - 'iteration': ' 1' - }, { - 'bandgap': None, - 'iteration': ' 1' - }, { - 'fermi_energy': None, - 'iteration': ' 1' - }, { - 'charge_density': None, - 'iteration': ' 1' - }] - } - - assert successful - assert parser_info_out['last_iteration_parsed'] == 1 - assert expected_parser_info_out['unparsed'] == parser_info_out['unparsed'] - assert expected_parser_info_out['parser_warnings'] == parser_info_out['parser_warnings'] - - -def test_parse_xmlout_file_fortran_garbage_in_xmlout_file(): - """ - tests the behavior of the parse_xmlout_file routine in the case of an individual 'garbage' in the out.xml file. - Fortran NANs and INFs will be parsed fine, ** will not be parsed. - (which can happen in the case of some kill, or Non regular termination of FLEUR) - """ - from aiida_fleur.parsers.fleur import parse_xmlout_file - - filename = os.path.abspath('./files/outxml/special/Fortran_garbage_BeTi_out.xml') - - simple_out, complex_out, parser_info_out, successful = parse_xmlout_file(filename) - - exp_partial_simple_out_dict = { - 'bandgap_units': 'eV', - 'energy': float('Inf'), - 'energy_hartree': float('Inf'), - 'fermi_energy': float('NaN'), - 'warnings': { - 'debug': {}, - 'error': {}, - 'info': {}, - 'warning': {} - } - } - - expected_parser_info_out = { - 'parser_info': - 'AiiDA Fleur Parser v0.3.2', - 'parser_warnings': [ - 'Could not convert: "**" to float, ValueError', - 'Could not convert: " !#@)!(U$*(Y" to float, ValueError' - ], - 'unparsed': [{ - 'bandgap': '**', - 'iteration': ' 19' - }, { - 'charge_density': ' !#@)!(U$*(Y', - 'iteration': ' 19' - }] - } - - #TODO maybe in the case on unpared, things should be initialized, here they are missing... - def isNaN(num): - return math.isnan(num) #num != num - - assert successful - assert exp_partial_simple_out_dict['energy'] == simple_out['energy'] - assert exp_partial_simple_out_dict['energy_hartree'] == simple_out['energy_hartree'] - assert isNaN(exp_partial_simple_out_dict['fermi_energy']) == isNaN(simple_out['fermi_energy']) - assert 'bandgap' not in list(simple_out.keys()) - - assert expected_parser_info_out['unparsed'] == parser_info_out['unparsed'] - assert expected_parser_info_out['parser_warnings'] == parser_info_out['parser_warnings'] - - -def test_parse_xmlout_file_empty_file(): - """ - tests the behavior of the parse_xmlout_file routine in the case of an empty file - """ - from aiida_fleur.parsers.fleur import parse_xmlout_file - - filename = os.path.abspath('./files/outxml/special/empty_out.xml') - - simple_out, complex_out, parser_info_out, successful = parse_xmlout_file(filename) - - expected_parser_info_out = { - 'parser_info': - 'AiiDA Fleur Parser v0.3.2', - 'parser_warnings': [ - 'The out.xml file is broken I try to repair it.', - 'Skipping the parsing of the xml file. Repairing was not possible.' - ], - 'unparsed': [] - } - - assert not successful - assert expected_parser_info_out == parser_info_out - - -# test parser success for all out files in folder -file_path1 = '../files/outxml/all_test/' -outxmlfilefolder = os.path.dirname(os.path.abspath(__file__)) -outxmlfilefolder_valid = os.path.abspath(os.path.join(outxmlfilefolder, file_path1)) - -outxmlfilelist = [] -for subdir, dirs, files in os.walk(outxmlfilefolder_valid): - for file in files: - if file.endswith('.xml'): - outxmlfilelist.append(os.path.join(subdir, file)) - - -@pytest.mark.parametrize('xmloutfile', outxmlfilelist) -def test_fleurparse_all_xmlout_file(xmloutfile): - """ - tests if the routine that parsers the outputfile, succeeds for all out files - """ - from aiida_fleur.parsers.fleur import parse_xmlout_file - - simple_out, complex_out, parser_info_out, successful = parse_xmlout_file(xmloutfile) - - assert successful - - def test_fleurparse_relax_file(): """Test if parsing of a given relax.xml file is successfull""" from aiida_fleur.parsers.fleur import parse_relax_file + from masci_tools.io.parsers.fleur.fleur_schema import InputSchemaDict from aiida.orm import Dict + schema_dict = InputSchemaDict.fromVersion('0.34') filename = os.path.abspath('./files/relaxxml/Fe_relax.xml') with open(filename, 'r') as relaxfile: - result = parse_relax_file(relaxfile) + result = parse_relax_file(relaxfile, schema_dict) assert isinstance(result, Dict) assert result.get_dict() != {} diff --git a/tests/parsers/test_fleur_parser/test_fleur_parser_default_full.yml b/tests/parsers/test_fleur_parser/test_fleur_parser_default_full.yml index d68282aba..62684ca6e 100644 --- a/tests/parsers/test_fleur_parser/test_fleur_parser_default_full.yml +++ b/tests/parsers/test_fleur_parser/test_fleur_parser_default_full.yml @@ -2,7 +2,7 @@ output_parameters: bandgap: 0.85561712 bandgap_units: eV charge_den_xc_den_integral: -41.74447706 - charge_density: 3.29535e-05 + density_convergence: 3.29535e-05 density_convergence_units: me/bohr^3 energy: -15784.562940686706 energy_core_electrons: -316.8117176949 @@ -12,7 +12,25 @@ output_parameters: energy_valence_electrons: 0.1710194344 fermi_energy: 0.2022322894 fermi_energy_units: Htr - force_largest: -0.0 + film: false + fleur_modes: + band: false + bz_integration: hist + cf_coeff: false + dos: false + film: false + force_theorem: false + greensf: false + gw: false + jspin: 1 + ldahia: false + ldau: false + noco: false + plot: false + relax: false + soc: false + gmax: 11.0 + input_file_version: '0.31' kmax: 3.5 number_of_atom_types: 1 number_of_atoms: 2 @@ -22,14 +40,31 @@ output_parameters: number_of_species: 1 number_of_spin_components: 1 number_of_symmetries: 48 - parser_info: AiiDA Fleur Parser v0.3.2 - parser_warnings: [] + parser_critical: [] + parser_debug: [] + parser_errors: [] + parser_info: + - Masci-Tools Fleur out.xml Parser v0.5.0 + - 'Found fleur out file with the versions out: 0.31; inp: 0.31' + - 'The following defined constants were found: {''Pi'': 3.141592653589793, ''Deg'': + 0.017453292519943295, ''Ang'': 1.889726124772898, ''nm'': 18.89726124772898, ''pm'': + 0.01889726124772898, ''Bohr'': 1.0, ''Htr'': 1.0, ''eV'': 0.03674932217565499, + ''Ry'': 0.5}' + - 'The following Fleur modes were found: {''jspin'': 1, ''noco'': False, ''soc'': + False, ''relax'': False, ''gw'': False, ''force_theorem'': False, ''cf_coeff'': + False, ''plot'': False, ''film'': False, ''ldau'': False, ''dos'': False, ''band'': + False, ''bz_integration'': ''hist'', ''greensf'': False, ''ldahia'': False}' + parser_warnings: + - Ignoring '0.27' outputVersion for MaX4.0 release + - "Output file does not validate against the schema: \nLine 14: Element 'inputData':\ + \ The attribute 'fleurInputVersion' is required but missing. \nLine 377: Element\ + \ 'atomsInCell': The attribute 'n_hia' is required but missing. \nLine 384: Element\ + \ 'kPointList', attribute 'posScale': The attribute 'posScale' is not allowed.\ + \ \nLine 447: Element 'spinDependentCharge': This element is not expected. \n" + spin_dependent_charge_interstitial: 3.5415656 + spin_dependent_charge_mt_spheres: 24.4584344 + spin_dependent_charge_total: 28.0 sum_of_eigenvalues: -316.6406982605 - unparsed: [] + total_charge: 27.9999999876 walltime: 4 walltime_units: seconds - warnings: - debug: {} - error: {} - info: {} - warning: {} diff --git a/tests/parsers/test_fleur_parser/test_fleur_parser_relax.yml b/tests/parsers/test_fleur_parser/test_fleur_parser_relax.yml index 5cc7e75c9..bde767bfa 100644 --- a/tests/parsers/test_fleur_parser/test_fleur_parser_relax.yml +++ b/tests/parsers/test_fleur_parser/test_fleur_parser_relax.yml @@ -1,11 +1,12 @@ output_parameters: - abspos_x_type1: 0.0 - abspos_y_type1: 0.0 - abspos_z_type1: -0.741699 + abspos_atoms: + - - 1 + - - 0.0 + - 0.0 + - -0.741699 bandgap: 10.6740023301 bandgap_units: eV charge_den_xc_den_integral: -0.6753699578 - density_convergence_units: null energy: -31.649357888579065 energy_core_electrons: 0.0 energy_hartree: -1.1630924497 @@ -14,12 +15,32 @@ output_parameters: energy_valence_electrons: -0.747242106 fermi_energy: -0.373621053 fermi_energy_units: Htr - film: 'True' - force_largest: 0.02082935 + film: true + fleur_modes: + band: false + bz_integration: hist + cf_coeff: false + dos: false + film: true + force_theorem: false + greensf: false + gw: false + jspin: 1 + ldahia: false + ldau: false + noco: false + plot: false + relax: true + soc: false + force_atoms: + - - 1 + - - 0.0 + - 0.0 + - 0.02082935 + force_largest_component: 0.02082935 force_units: Htr/bohr - force_x_type1: 0.0 - force_y_type1: 0.0 - force_z_type1: 0.02082935 + gmax: 25.1 + input_file_version: '0.31' kmax: 5.0 number_of_atom_types: 1 number_of_atoms: 2 @@ -29,10 +50,28 @@ output_parameters: number_of_species: 1 number_of_spin_components: 1 number_of_symmetries: 16 - parser_info: AiiDA Fleur Parser v0.3.2 + parser_critical: [] + parser_debug: [] + parser_errors: [] + parser_info: + - Masci-Tools Fleur out.xml Parser v0.5.0 + - 'Found fleur out file with the versions out: 0.31; inp: 0.31' + - 'The following defined constants were found: {''Pi'': 3.141592653589793, ''Deg'': + 0.017453292519943295, ''Ang'': 1.889726124772898, ''nm'': 18.89726124772898, ''pm'': + 0.01889726124772898, ''Bohr'': 1.0, ''Htr'': 1.0, ''eV'': 0.03674932217565499, + ''Ry'': 0.5}' + - 'The following Fleur modes were found: {''jspin'': 1, ''noco'': False, ''soc'': + False, ''relax'': True, ''gw'': False, ''force_theorem'': False, ''cf_coeff'': + False, ''plot'': False, ''film'': True, ''ldau'': False, ''dos'': False, ''band'': + False, ''bz_integration'': ''hist'', ''greensf'': False, ''ldahia'': False}' parser_warnings: - - 'Can not get attributename: "units" from node "[]", because node is not an element - of etree.' + - Ignoring '0.27' outputVersion for MaX4.0 release + - "Output file does not validate against the schema: \nLine 15: Element 'inputData':\ + \ The attribute 'fleurInputVersion' is required but missing. \nLine 160: Element\ + \ 'atomsInCell': The attribute 'n_hia' is required but missing. \nLine 167: Element\ + \ 'kPointList', attribute 'posScale': The attribute 'posScale' is not allowed.\ + \ \nLine 172: Element 'spinDependentCharge': This element is not expected. Expected\ + \ is ( iteration ). \n" relax_atom_positions: - - 0.0 - 0.0 @@ -50,15 +89,13 @@ output_parameters: - - 0.0 - 0.0 - 4.63 + spin_dependent_charge_interstitial: 1.3187829 + spin_dependent_charge_mt_spheres: 0.5077481 + spin_dependent_charge_total: 2.0000006 sum_of_eigenvalues: -0.747242106 - unparsed: [] + total_charge: 2.0000005869 walltime: 10290 walltime_units: seconds - warnings: - debug: {} - error: {} - info: {} - warning: {} relax_parameters: displacements: - - 0.0 diff --git a/tests/parsers/test_inpgen_parser/test_inpgen_parser_default.yml b/tests/parsers/test_inpgen_parser/test_inpgen_parser_default.yml index 45b4bbfde..f6e6cca99 100644 --- a/tests/parsers/test_inpgen_parser/test_inpgen_parser_default.yml +++ b/tests/parsers/test_inpgen_parser/test_inpgen_parser_default.yml @@ -1,65 +1,81 @@ fleurinpData: atomGroups: - atomGroup: - - force: - calculate: true - relaxXYZ: TTT - nocoParams: - alpha: 0.0 - b_cons_x: '.00000000' - b_cons_y: '.00000000' - beta: '.00000000' - l_relax: F - relPos: - - .0000000000 .0000000000 .0000000000 - species: W-1 + - force: + calculate: true + relaxXYZ: TTT + labels: + ' 1': + - 0.0 + - 0.0 + - 0.0 + nocoParams: + alpha: 0.0 + b_cons_x: 0.0 + b_cons_y: 0.0 + beta: 0.0 + l_relax: false + relPos: + - - 0.0 + - 0.0 + - 0.0 + species: W-1 atomSpecies: - species: - - atomicCutoffs: - lmax: 12 - lnonsphr: 6 - atomicNumber: 74 - coreStates: 16 - electronConfig: - coreConfig: '[Kr] (4d3/2) (4d5/2) (4f5/2) (4f7/2)' - stateOccupation: - - spinDown: '.00000000' - spinUp: '2.00000000' - state: (5d3/2) - - spinDown: '.00000000' - spinUp: '2.00000000' - state: (5d5/2) - valenceConfig: (5s1/2) (5p1/2) (5p3/2) (6s1/2) (5d3/2) (5d5/2) - element: W - energyParameters: - d: 5 - f: 5 - p: 6 - s: 6 - flipSpin: true - lo: - - eDeriv: 0 - l: 0 - n: 5 - type: SCLO - - eDeriv: 0 - l: 1 - n: 5 - type: SCLO - magMom: 0.0 - mtSphere: - gridPoints: 981 - logIncrement: 0.016 - radius: 2.1 - name: W-1 - prodBasis: - lcutm: '4' - lcutwf: '11' - select: 4 0 4 2 + - atomicCutoffs: + lmax: 12 + lnonsphr: 6 + atomicNumber: 74 + coreStates: 16 + electronConfig: + coreConfig: + - '[Kr]' + - (4d3/2) + - (4d5/2) + - (4f5/2) + - (4f7/2) + stateOccupation: + - spinDown: 0.0 + spinUp: 2.0 + state: (5d3/2) + - spinDown: 0.0 + spinUp: 2.0 + state: (5d5/2) + valenceConfig: + - (5s1/2) + - (5p1/2) + - (5p3/2) + - (6s1/2) + - (5d3/2) + - (5d5/2) + element: W + energyParameters: + d: 5 + f: 5 + p: 6 + s: 6 + flipSpin: true + lo: + - eDeriv: 0 + l: 0 + n: 5 + type: SCLO + - eDeriv: 0 + l: 1 + n: 5 + type: SCLO + magMom: 0.0 + mtSphere: + gridPoints: 981 + logIncrement: 0.016 + radius: 2.1 + name: W-1 + prodBasis: + lcutm: 4 + lcutwf: 11 + select: 4 0 4 2 calculationSetup: bzIntegration: altKPointSet: - kPointCount: + - kPointCount: count: 240 gamma: false purpose: bands @@ -67,16 +83,29 @@ fleurinpData: kPointList: count: 4 kPoint: - - -0.000000 0.333333 0.333333 - - -0.333333 0.333333 0.333333 - - -0.000000 0.000000 0.333333 - - 0.000000 0.000000 0.000000 + - - -0.0 + - 0.333333 + - 0.333333 + - - -0.333333 + - 0.333333 + - 0.333333 + - - -0.0 + - 0.0 + - 0.333333 + - - 0.0 + - 0.0 + - 0.0 posScale: '1.00000000' + weight: + - 0.296296 + - 0.222222 + - 0.444444 + - 0.037037 weightScale: '1.00000000' mode: hist valenceElectrons: 14.0 coreElectrons: - coretail_lmax: '0' + coretail_lmax: 0 ctail: true frcor: false kcrel: 0 @@ -98,7 +127,7 @@ fleurinpData: forcemix: BFGS l_f: false ldaU: - - l_linMix: false + l_linMix: false mixParam: 0.05 spinf: 1.0 magnetism: @@ -107,24 +136,27 @@ fleurinpData: lflip: false swsp: false nocoParams: - l_constr: F - l_mperp: F + l_constr: false + l_mperp: false l_ss: false mix_b: '.00000000' - qss: .0000000000 .0000000000 .0000000000 + qss: + - 0.0 + - 0.0 + - 0.0 prodBasis: - bands: '0' - ewaldlambda: '3' - gcutm: '3.40000000' - lexp: '16' - tolerance: '.00010000' + bands: 0 + ewaldlambda: 3 + gcutm: 3.4 + lexp: 16 + tolerance: 0.0001 scfLoop: alpha: 0.05 imix: Anderson itmax: 15 maxIterBroyd: 99 minDistance: 1.0e-05 - precondParam: '0.0' + precondParam: 0.0 spinf: 2.0 soc: l_soc: false @@ -134,157 +166,741 @@ fleurinpData: cell: bulkLattice: bravaisMatrix: - row-1: -3.013812060000000 3.013812060000000 3.013812060000000 - row-2: 3.013812060000000 -3.013812060000000 3.013812060000000 - row-3: 3.013812060000000 3.013812060000000 -3.013812060000000 + row-1: + - -3.01381206 + - 3.01381206 + - 3.01381206 + row-2: + - 3.01381206 + - -3.01381206 + - 3.01381206 + row-3: + - 3.01381206 + - 3.01381206 + - -3.01381206 latnam: any scale: 1.0 symmetryOperations: - symOp: - - row-1: 1 0 0 .0000000000 - row-2: 0 1 0 .0000000000 - row-3: 0 0 1 .0000000000 - - row-1: -1 0 0 .0000000000 - row-2: -1 1 0 .0000000000 - row-3: -1 0 1 .0000000000 - - row-1: 1 -1 0 .0000000000 - row-2: 0 -1 0 .0000000000 - row-3: 0 -1 1 .0000000000 - - row-1: 0 -1 0 .0000000000 - row-2: 1 -1 0 .0000000000 - row-3: 0 -1 1 .0000000000 - - row-1: -1 1 0 .0000000000 - row-2: -1 0 0 .0000000000 - row-3: -1 0 1 .0000000000 - - row-1: 0 1 0 .0000000000 - row-2: 1 0 0 .0000000000 - row-3: 0 0 1 .0000000000 - - row-1: 1 0 -1 .0000000000 - row-2: 0 1 -1 .0000000000 - row-3: 0 0 -1 .0000000000 - - row-1: 0 0 -1 .0000000000 - row-2: 0 1 -1 .0000000000 - row-3: 1 0 -1 .0000000000 - - row-1: 0 1 -1 .0000000000 - row-2: 1 0 -1 .0000000000 - row-3: 0 0 -1 .0000000000 - - row-1: 0 1 -1 .0000000000 - row-2: 0 0 -1 .0000000000 - row-3: 1 0 -1 .0000000000 - - row-1: 1 0 -1 .0000000000 - row-2: 0 0 -1 .0000000000 - row-3: 0 1 -1 .0000000000 - - row-1: 0 0 -1 .0000000000 - row-2: 1 0 -1 .0000000000 - row-3: 0 1 -1 .0000000000 - - row-1: -1 0 0 .0000000000 - row-2: 0 -1 0 .0000000000 - row-3: 0 0 -1 .0000000000 - - row-1: 1 0 0 .0000000000 - row-2: 1 -1 0 .0000000000 - row-3: 1 0 -1 .0000000000 - - row-1: 0 -1 0 .0000000000 - row-2: -1 0 0 .0000000000 - row-3: 0 0 -1 .0000000000 - - row-1: 1 -1 0 .0000000000 - row-2: 1 0 0 .0000000000 - row-3: 1 0 -1 .0000000000 - - row-1: 0 1 0 .0000000000 - row-2: -1 1 0 .0000000000 - row-3: 0 1 -1 .0000000000 - - row-1: -1 1 0 .0000000000 - row-2: 0 1 0 .0000000000 - row-3: 0 1 -1 .0000000000 - - row-1: -1 0 0 .0000000000 - row-2: 0 0 -1 .0000000000 - row-3: 0 -1 0 .0000000000 - - row-1: 1 0 0 .0000000000 - row-2: 1 0 -1 .0000000000 - row-3: 1 -1 0 .0000000000 - - row-1: 0 -1 0 .0000000000 - row-2: 0 0 -1 .0000000000 - row-3: -1 0 0 .0000000000 - - row-1: 1 -1 0 .0000000000 - row-2: 1 0 -1 .0000000000 - row-3: 1 0 0 .0000000000 - - row-1: 0 1 0 .0000000000 - row-2: 0 1 -1 .0000000000 - row-3: -1 1 0 .0000000000 - - row-1: -1 1 0 .0000000000 - row-2: 0 1 -1 .0000000000 - row-3: 0 1 0 .0000000000 - - row-1: 0 0 -1 .0000000000 - row-2: -1 0 0 .0000000000 - row-3: 0 -1 0 .0000000000 - - row-1: 1 0 -1 .0000000000 - row-2: 1 0 0 .0000000000 - row-3: 1 -1 0 .0000000000 - - row-1: 0 0 -1 .0000000000 - row-2: 0 -1 0 .0000000000 - row-3: -1 0 0 .0000000000 - - row-1: 1 0 -1 .0000000000 - row-2: 1 -1 0 .0000000000 - row-3: 1 0 0 .0000000000 - - row-1: 0 1 -1 .0000000000 - row-2: 0 1 0 .0000000000 - row-3: -1 1 0 .0000000000 - - row-1: 0 1 -1 .0000000000 - row-2: -1 1 0 .0000000000 - row-3: 0 1 0 .0000000000 - - row-1: -1 0 1 .0000000000 - row-2: -1 1 0 .0000000000 - row-3: -1 0 0 .0000000000 - - row-1: 0 0 1 .0000000000 - row-2: 0 1 0 .0000000000 - row-3: 1 0 0 .0000000000 - - row-1: 0 -1 1 .0000000000 - row-2: 1 -1 0 .0000000000 - row-3: 0 -1 0 .0000000000 - - row-1: 0 -1 1 .0000000000 - row-2: 0 -1 0 .0000000000 - row-3: 1 -1 0 .0000000000 - - row-1: -1 0 1 .0000000000 - row-2: -1 0 0 .0000000000 - row-3: -1 1 0 .0000000000 - - row-1: 0 0 1 .0000000000 - row-2: 1 0 0 .0000000000 - row-3: 0 1 0 .0000000000 - - row-1: 1 -1 0 .0000000000 - row-2: 0 -1 1 .0000000000 - row-3: 0 -1 0 .0000000000 - - row-1: 0 -1 0 .0000000000 - row-2: 0 -1 1 .0000000000 - row-3: 1 -1 0 .0000000000 - - row-1: -1 1 0 .0000000000 - row-2: -1 0 1 .0000000000 - row-3: -1 0 0 .0000000000 - - row-1: 0 1 0 .0000000000 - row-2: 0 0 1 .0000000000 - row-3: 1 0 0 .0000000000 - - row-1: 1 0 0 .0000000000 - row-2: 0 0 1 .0000000000 - row-3: 0 1 0 .0000000000 - - row-1: -1 0 0 .0000000000 - row-2: -1 0 1 .0000000000 - row-3: -1 1 0 .0000000000 - - row-1: 0 0 1 .0000000000 - row-2: -1 0 1 .0000000000 - row-3: 0 -1 1 .0000000000 - - row-1: -1 0 1 .0000000000 - row-2: 0 0 1 .0000000000 - row-3: 0 -1 1 .0000000000 - - row-1: 0 0 1 .0000000000 - row-2: 0 -1 1 .0000000000 - row-3: -1 0 1 .0000000000 - - row-1: -1 0 1 .0000000000 - row-2: 0 -1 1 .0000000000 - row-3: 0 0 1 .0000000000 - - row-1: 0 -1 1 .0000000000 - row-2: 0 0 1 .0000000000 - row-3: -1 0 1 .0000000000 - - row-1: 0 -1 1 .0000000000 - row-2: -1 0 1 .0000000000 - row-3: 0 0 1 .0000000000 + - row-1: + - 1.0 + - 0.0 + - 0.0 + - 0.0 + row-2: + - 0.0 + - 1.0 + - 0.0 + - 0.0 + row-3: + - 0.0 + - 0.0 + - 1.0 + - 0.0 + - row-1: + - -1.0 + - 0.0 + - 0.0 + - 0.0 + row-2: + - -1.0 + - 1.0 + - 0.0 + - 0.0 + row-3: + - -1.0 + - 0.0 + - 1.0 + - 0.0 + - row-1: + - 1.0 + - -1.0 + - 0.0 + - 0.0 + row-2: + - 0.0 + - -1.0 + - 0.0 + - 0.0 + row-3: + - 0.0 + - -1.0 + - 1.0 + - 0.0 + - row-1: + - 0.0 + - -1.0 + - 0.0 + - 0.0 + row-2: + - 1.0 + - -1.0 + - 0.0 + - 0.0 + row-3: + - 0.0 + - -1.0 + - 1.0 + - 0.0 + - row-1: + - -1.0 + - 1.0 + - 0.0 + - 0.0 + row-2: + - -1.0 + - 0.0 + - 0.0 + - 0.0 + row-3: + - -1.0 + - 0.0 + - 1.0 + - 0.0 + - row-1: + - 0.0 + - 1.0 + - 0.0 + - 0.0 + row-2: + - 1.0 + - 0.0 + - 0.0 + - 0.0 + row-3: + - 0.0 + - 0.0 + - 1.0 + - 0.0 + - row-1: + - 1.0 + - 0.0 + - -1.0 + - 0.0 + row-2: + - 0.0 + - 1.0 + - -1.0 + - 0.0 + row-3: + - 0.0 + - 0.0 + - -1.0 + - 0.0 + - row-1: + - 0.0 + - 0.0 + - -1.0 + - 0.0 + row-2: + - 0.0 + - 1.0 + - -1.0 + - 0.0 + row-3: + - 1.0 + - 0.0 + - -1.0 + - 0.0 + - row-1: + - 0.0 + - 1.0 + - -1.0 + - 0.0 + row-2: + - 1.0 + - 0.0 + - -1.0 + - 0.0 + row-3: + - 0.0 + - 0.0 + - -1.0 + - 0.0 + - row-1: + - 0.0 + - 1.0 + - -1.0 + - 0.0 + row-2: + - 0.0 + - 0.0 + - -1.0 + - 0.0 + row-3: + - 1.0 + - 0.0 + - -1.0 + - 0.0 + - row-1: + - 1.0 + - 0.0 + - -1.0 + - 0.0 + row-2: + - 0.0 + - 0.0 + - -1.0 + - 0.0 + row-3: + - 0.0 + - 1.0 + - -1.0 + - 0.0 + - row-1: + - 0.0 + - 0.0 + - -1.0 + - 0.0 + row-2: + - 1.0 + - 0.0 + - -1.0 + - 0.0 + row-3: + - 0.0 + - 1.0 + - -1.0 + - 0.0 + - row-1: + - -1.0 + - 0.0 + - 0.0 + - 0.0 + row-2: + - 0.0 + - -1.0 + - 0.0 + - 0.0 + row-3: + - 0.0 + - 0.0 + - -1.0 + - 0.0 + - row-1: + - 1.0 + - 0.0 + - 0.0 + - 0.0 + row-2: + - 1.0 + - -1.0 + - 0.0 + - 0.0 + row-3: + - 1.0 + - 0.0 + - -1.0 + - 0.0 + - row-1: + - 0.0 + - -1.0 + - 0.0 + - 0.0 + row-2: + - -1.0 + - 0.0 + - 0.0 + - 0.0 + row-3: + - 0.0 + - 0.0 + - -1.0 + - 0.0 + - row-1: + - 1.0 + - -1.0 + - 0.0 + - 0.0 + row-2: + - 1.0 + - 0.0 + - 0.0 + - 0.0 + row-3: + - 1.0 + - 0.0 + - -1.0 + - 0.0 + - row-1: + - 0.0 + - 1.0 + - 0.0 + - 0.0 + row-2: + - -1.0 + - 1.0 + - 0.0 + - 0.0 + row-3: + - 0.0 + - 1.0 + - -1.0 + - 0.0 + - row-1: + - -1.0 + - 1.0 + - 0.0 + - 0.0 + row-2: + - 0.0 + - 1.0 + - 0.0 + - 0.0 + row-3: + - 0.0 + - 1.0 + - -1.0 + - 0.0 + - row-1: + - -1.0 + - 0.0 + - 0.0 + - 0.0 + row-2: + - 0.0 + - 0.0 + - -1.0 + - 0.0 + row-3: + - 0.0 + - -1.0 + - 0.0 + - 0.0 + - row-1: + - 1.0 + - 0.0 + - 0.0 + - 0.0 + row-2: + - 1.0 + - 0.0 + - -1.0 + - 0.0 + row-3: + - 1.0 + - -1.0 + - 0.0 + - 0.0 + - row-1: + - 0.0 + - -1.0 + - 0.0 + - 0.0 + row-2: + - 0.0 + - 0.0 + - -1.0 + - 0.0 + row-3: + - -1.0 + - 0.0 + - 0.0 + - 0.0 + - row-1: + - 1.0 + - -1.0 + - 0.0 + - 0.0 + row-2: + - 1.0 + - 0.0 + - -1.0 + - 0.0 + row-3: + - 1.0 + - 0.0 + - 0.0 + - 0.0 + - row-1: + - 0.0 + - 1.0 + - 0.0 + - 0.0 + row-2: + - 0.0 + - 1.0 + - -1.0 + - 0.0 + row-3: + - -1.0 + - 1.0 + - 0.0 + - 0.0 + - row-1: + - -1.0 + - 1.0 + - 0.0 + - 0.0 + row-2: + - 0.0 + - 1.0 + - -1.0 + - 0.0 + row-3: + - 0.0 + - 1.0 + - 0.0 + - 0.0 + - row-1: + - 0.0 + - 0.0 + - -1.0 + - 0.0 + row-2: + - -1.0 + - 0.0 + - 0.0 + - 0.0 + row-3: + - 0.0 + - -1.0 + - 0.0 + - 0.0 + - row-1: + - 1.0 + - 0.0 + - -1.0 + - 0.0 + row-2: + - 1.0 + - 0.0 + - 0.0 + - 0.0 + row-3: + - 1.0 + - -1.0 + - 0.0 + - 0.0 + - row-1: + - 0.0 + - 0.0 + - -1.0 + - 0.0 + row-2: + - 0.0 + - -1.0 + - 0.0 + - 0.0 + row-3: + - -1.0 + - 0.0 + - 0.0 + - 0.0 + - row-1: + - 1.0 + - 0.0 + - -1.0 + - 0.0 + row-2: + - 1.0 + - -1.0 + - 0.0 + - 0.0 + row-3: + - 1.0 + - 0.0 + - 0.0 + - 0.0 + - row-1: + - 0.0 + - 1.0 + - -1.0 + - 0.0 + row-2: + - 0.0 + - 1.0 + - 0.0 + - 0.0 + row-3: + - -1.0 + - 1.0 + - 0.0 + - 0.0 + - row-1: + - 0.0 + - 1.0 + - -1.0 + - 0.0 + row-2: + - -1.0 + - 1.0 + - 0.0 + - 0.0 + row-3: + - 0.0 + - 1.0 + - 0.0 + - 0.0 + - row-1: + - -1.0 + - 0.0 + - 1.0 + - 0.0 + row-2: + - -1.0 + - 1.0 + - 0.0 + - 0.0 + row-3: + - -1.0 + - 0.0 + - 0.0 + - 0.0 + - row-1: + - 0.0 + - 0.0 + - 1.0 + - 0.0 + row-2: + - 0.0 + - 1.0 + - 0.0 + - 0.0 + row-3: + - 1.0 + - 0.0 + - 0.0 + - 0.0 + - row-1: + - 0.0 + - -1.0 + - 1.0 + - 0.0 + row-2: + - 1.0 + - -1.0 + - 0.0 + - 0.0 + row-3: + - 0.0 + - -1.0 + - 0.0 + - 0.0 + - row-1: + - 0.0 + - -1.0 + - 1.0 + - 0.0 + row-2: + - 0.0 + - -1.0 + - 0.0 + - 0.0 + row-3: + - 1.0 + - -1.0 + - 0.0 + - 0.0 + - row-1: + - -1.0 + - 0.0 + - 1.0 + - 0.0 + row-2: + - -1.0 + - 0.0 + - 0.0 + - 0.0 + row-3: + - -1.0 + - 1.0 + - 0.0 + - 0.0 + - row-1: + - 0.0 + - 0.0 + - 1.0 + - 0.0 + row-2: + - 1.0 + - 0.0 + - 0.0 + - 0.0 + row-3: + - 0.0 + - 1.0 + - 0.0 + - 0.0 + - row-1: + - 1.0 + - -1.0 + - 0.0 + - 0.0 + row-2: + - 0.0 + - -1.0 + - 1.0 + - 0.0 + row-3: + - 0.0 + - -1.0 + - 0.0 + - 0.0 + - row-1: + - 0.0 + - -1.0 + - 0.0 + - 0.0 + row-2: + - 0.0 + - -1.0 + - 1.0 + - 0.0 + row-3: + - 1.0 + - -1.0 + - 0.0 + - 0.0 + - row-1: + - -1.0 + - 1.0 + - 0.0 + - 0.0 + row-2: + - -1.0 + - 0.0 + - 1.0 + - 0.0 + row-3: + - -1.0 + - 0.0 + - 0.0 + - 0.0 + - row-1: + - 0.0 + - 1.0 + - 0.0 + - 0.0 + row-2: + - 0.0 + - 0.0 + - 1.0 + - 0.0 + row-3: + - 1.0 + - 0.0 + - 0.0 + - 0.0 + - row-1: + - 1.0 + - 0.0 + - 0.0 + - 0.0 + row-2: + - 0.0 + - 0.0 + - 1.0 + - 0.0 + row-3: + - 0.0 + - 1.0 + - 0.0 + - 0.0 + - row-1: + - -1.0 + - 0.0 + - 0.0 + - 0.0 + row-2: + - -1.0 + - 0.0 + - 1.0 + - 0.0 + row-3: + - -1.0 + - 1.0 + - 0.0 + - 0.0 + - row-1: + - 0.0 + - 0.0 + - 1.0 + - 0.0 + row-2: + - -1.0 + - 0.0 + - 1.0 + - 0.0 + row-3: + - 0.0 + - -1.0 + - 1.0 + - 0.0 + - row-1: + - -1.0 + - 0.0 + - 1.0 + - 0.0 + row-2: + - 0.0 + - 0.0 + - 1.0 + - 0.0 + row-3: + - 0.0 + - -1.0 + - 1.0 + - 0.0 + - row-1: + - 0.0 + - 0.0 + - 1.0 + - 0.0 + row-2: + - 0.0 + - -1.0 + - 1.0 + - 0.0 + row-3: + - -1.0 + - 0.0 + - 1.0 + - 0.0 + - row-1: + - -1.0 + - 0.0 + - 1.0 + - 0.0 + row-2: + - 0.0 + - -1.0 + - 1.0 + - 0.0 + row-3: + - 0.0 + - 0.0 + - 1.0 + - 0.0 + - row-1: + - 0.0 + - -1.0 + - 1.0 + - 0.0 + row-2: + - 0.0 + - 0.0 + - 1.0 + - 0.0 + row-3: + - -1.0 + - 0.0 + - 1.0 + - 0.0 + - row-1: + - 0.0 + - -1.0 + - 1.0 + - 0.0 + row-2: + - -1.0 + - 0.0 + - 1.0 + - 0.0 + row-3: + - 0.0 + - 0.0 + - 1.0 + - 0.0 comment: A Fleur input generator calculation with aiida fleurInputVersion: '0.31' output: @@ -305,9 +921,9 @@ fleurinpData: sigma: 0.015 dos: false magneticCircularDichroism: - energyLo: '-10.00000000' - energyUp: '.00000000' - mcd: F + energyLo: -10.0 + energyUp: 0.0 + mcd: false plotting: iplot: 0 plplot: false @@ -317,10 +933,10 @@ fleurinpData: bmt: false eonly: false unfoldingBand: - supercellX: '1' - supercellY: '1' - supercellZ: '1' - unfoldBand: F + supercellX: 1 + supercellY: 1 + supercellZ: 1 + unfoldBand: false vacdos: false vacuumDOS: integ: false diff --git a/tests/run_all_cov.sh b/tests/run_all_cov.sh index 9f40c5792..0de3639c5 100755 --- a/tests/run_all_cov.sh +++ b/tests/run_all_cov.sh @@ -3,7 +3,7 @@ export AIIDA_PATH='.'; mkdir -p '.aiida'; #pytest -sv #pytest -v -pytest --cov-report=xml --cov=aiida_fleur +pytest --cov-report=xml --cov=aiida_fleur --cov=tests #pytest --cov-report=html --cov=aiida_fleur #pytest --cov-report=html --cov=aiida_fleur -vv -rXxs -x diff --git a/tests/run_all_cov_show.sh b/tests/run_all_cov_show.sh index 59dcd37f3..959d927a6 100755 --- a/tests/run_all_cov_show.sh +++ b/tests/run_all_cov_show.sh @@ -3,7 +3,7 @@ export AIIDA_PATH='.'; mkdir -p '.aiida'; #pytest -sv #pytest -v -pytest --cov-report=term-missing:skip-covered --cov=aiida_fleur +pytest --cov-report=term-missing:skip-covered --cov=aiida_fleur --cov=tests #pytest --cov-report=html --cov=aiida_fleur #pytest --cov-report=html --cov=aiida_fleur -vv -rXxs -x diff --git a/tests/test_calcfunctions_immutabledefaults.py b/tests/test_calcfunctions_immutabledefaults.py new file mode 100644 index 000000000..c5a643646 --- /dev/null +++ b/tests/test_calcfunctions_immutabledefaults.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- +############################################################################### +# Copyright (c), Forschungszentrum Jülich GmbH, IAS-1/PGI-1, Germany. # +# All rights reserved. # +# This file is part of the AiiDA-FLEUR package. # +# # +# The code is hosted on GitHub at https://github.com/JuDFTteam/aiida-fleur # +# For further information on the license, see the LICENSE.txt file # +# For further information please visit http://www.flapw.de or # +# http://aiida-fleur.readthedocs.io/en/develop/ # +############################################################################### +''' +Tests if all calcfunctions we use have immutable defaultvalues, +otherwise the test for the corresponding calcfunction will fail. +If defaults are not immutable strange things can happen in aiida. + +Also calcfunctions should not be inside a class, because that makes them not cacheable +''' + +import inspect + + +def get_default_args(func): + """ + Helpers to return the default kwargs of a given function + """ + signature = inspect.signature(func) + defaults = {} + for key, val in signature.parameters.items(): + if val.default is not inspect.Parameter.empty: + defaults[key] = val.default + return defaults + + +def test_check_immutable_defaults(): + """Test if defaults of calcfunctions are immutable + + Is there a way to automatically collect all calcfunctions, without adding other + register decorrators or so? Parsing source code? or integrate this in a git/precommit hook + """ + + from aiida_fleur.tools.merge_parameter import merge_parameter_cf + from aiida_fleur.tools.create_kpoints_from_distance import create_kpoints_from_distance_parameter + from aiida_fleur.tools.StructureData_util import center_film_wf, find_primitive_cell_wf, break_symmetry_wf, supercell, rescale + from aiida_fleur.tools.read_cif_folder import wf_struc_from_cif + + from aiida_fleur.workflows.eos import eos_structures + from aiida_fleur.workflows.create_magnetic_film import create_film_to_relax, create_substrate_bulk, magnetic_slab_from_relaxed_cf + from aiida_fleur.workflows.corehole import prepare_struc_corehole_wf + + immutable = (str, type(None), tuple) + + calcfunction_list = [ + merge_parameter_cf, create_kpoints_from_distance_parameter, center_film_wf, find_primitive_cell_wf, + break_symmetry_wf, supercell, rescale, wf_struc_from_cif, eos_structures, create_film_to_relax, + create_substrate_bulk, magnetic_slab_from_relaxed_cf, prepare_struc_corehole_wf + ] + + for calcf in calcfunction_list: + defaults = get_default_args(calcf) + for key, val in defaults.items(): + if not isinstance(val, immutable): + message = ('Default value of calcfunction not immutable: \n' + 'function: {}\n' + 'kwarg: {} : {}'.format(calcf, key, val)) + # Add reason explaination, https://github.com/JuDFTteam/aiida-fleur/issues/85 + assert False, message diff --git a/tests/test_entrypoints.py b/tests/test_entrypoints.py index 00ceae9c3..2406ab9e8 100644 --- a/tests/test_entrypoints.py +++ b/tests/test_entrypoints.py @@ -92,17 +92,17 @@ def test_fleur_eos_wc_entry_point(self): # this entry point has currently a problem... def test_fleur_initial_cls_wc_entry_point(self): from aiida.plugins import WorkflowFactory - from aiida_fleur.workflows.initial_cls import fleur_initial_cls_wc + from aiida_fleur.workflows.initial_cls import FleurInitialCLSWorkChain workflow = WorkflowFactory('fleur.init_cls') - assert workflow == fleur_initial_cls_wc + assert workflow == FleurInitialCLSWorkChain def test_fleur_corehole_wc_entry_point(self): from aiida.plugins import WorkflowFactory - from aiida_fleur.workflows.corehole import fleur_corehole_wc + from aiida_fleur.workflows.corehole import FleurCoreholeWorkChain workflow = WorkflowFactory('fleur.corehole') - assert workflow == fleur_corehole_wc + assert workflow == FleurCoreholeWorkChain def test_fleur_mae_wc_entry_point(self): from aiida.plugins import WorkflowFactory @@ -159,3 +159,10 @@ def test_fleur_create_magnetic_wc_entry_point(self): workflow = WorkflowFactory('fleur.create_magnetic') assert workflow == FleurCreateMagneticWorkChain + + def test_fleur_strain_wc_entry_point(self): + from aiida.plugins import WorkflowFactory + from aiida_fleur.workflows.strain import FleurStrainWorkChain + + workflow = WorkflowFactory('fleur.strain') + assert workflow == FleurStrainWorkChain diff --git a/tests/test_workflows_builder_init.py b/tests/test_workflows_builder_init.py index 15252f411..053d8625f 100644 --- a/tests/test_workflows_builder_init.py +++ b/tests/test_workflows_builder_init.py @@ -59,17 +59,17 @@ def test_fleur_corehole_wc_init(self): """ Test the interface of the corehole workchain """ - from aiida_fleur.workflows.corehole import fleur_corehole_wc + from aiida_fleur.workflows.corehole import FleurCoreholeWorkChain - builder = fleur_corehole_wc.get_builder() + builder = FleurCoreholeWorkChain.get_builder() def test_fleur_initial_cls_wc_init(self): """ Test the interface of the scf workchain """ - from aiida_fleur.workflows.initial_cls import fleur_initial_cls_wc + from aiida_fleur.workflows.initial_cls import FleurInitialCLSWorkChain - builder = fleur_initial_cls_wc.get_builder() + builder = FleurInitialCLSWorkChain.get_builder() def test_fleur_relax_wc_init(self): """ @@ -150,3 +150,11 @@ def test_fleur_create_magnetic_wc_init(self): from aiida_fleur.workflows.create_magnetic_film import FleurCreateMagneticWorkChain builder = FleurCreateMagneticWorkChain.get_builder() + + def test_fleur_strain_wc_init(self): + """ + Test the interface of the dmi workchain + """ + from aiida_fleur.workflows.strain import FleurStrainWorkChain + + builder = FleurStrainWorkChain.get_builder() diff --git a/tests/tools/test_common_fleur_wf.py b/tests/tools/test_common_fleur_wf.py index 92646476a..90d86672b 100644 --- a/tests/tools/test_common_fleur_wf.py +++ b/tests/tools/test_common_fleur_wf.py @@ -14,23 +14,22 @@ import pytest import os - # is_code -def test_is_code_interface(fixture_code): - """Test if is_code interface can take all inputs types without failure""" - from aiida_fleur.tools.common_fleur_wf import is_code +# def test_is_code_interface(fixture_code): +# """Test if is_code interface can take all inputs types without failure""" +# from aiida_fleur.tools.common_fleur_wf import is_code - assert is_code('random_string') is None - assert is_code('fleur.inpGUT') is None - assert is_code(99999) is None +# assert is_code('random_string') is None +# assert is_code('fleur.inpGUT') is None +# assert is_code(99999) is None - code = fixture_code('fleur.inpgen') - code.store() +# code = fixture_code('fleur.inpgen') +# code.store() - assert is_code(code.uuid) - assert is_code(code.pk) - assert is_code('@'.join([code.label, code.get_computer_name()])) - assert is_code(code) +# assert is_code(code.uuid) +# assert is_code(code.pk) +# assert is_code('@'.join([code.label, code.get_computer_name()])) +# assert is_code(code) def test_get_inputs_fleur(): @@ -54,8 +53,12 @@ def test_get_inputs_fleur(): 'settings': { 'test': 1 }, - 'serial': False, - 'only_even_MPI': True + 'add_comp_para': { + 'serial': False, + 'only_even_MPI': False, + 'max_queue_nodes': 20, + 'max_queue_wallclock_sec': 86400 + } } results = get_inputs_fleur(**inputs) @@ -68,7 +71,9 @@ def test_get_inputs_fleur(): assert results['parent_folder'] == 'remote' assert results['description'] == 'description' assert results['label'] == 'label' - assert results['only_even_MPI'] == Bool(True) + assert results['add_comp_para']['only_even_MPI'] is False + assert results['add_comp_para']['max_queue_nodes'] == 20 + assert results['add_comp_para']['max_queue_wallclock_sec'] == 86400 assert out_options == {'custom_scheduler_commands': 'test_command', 'withmpi': True} assert out_settings == {'test': 1} @@ -79,7 +84,12 @@ def test_get_inputs_fleur(): 'options': { 'custom_scheduler_commands': 'test_command' }, - 'serial': True + 'add_comp_para': { + 'serial': True, + 'only_even_MPI': False, + 'max_queue_nodes': 20, + 'max_queue_wallclock_sec': 86400 + } } results = get_inputs_fleur(**inputs) @@ -273,13 +283,12 @@ def test_performance_extract_calcs(fixture_localhost, generate_calc_job_node): 'energy_units': 'eV', 'kmax': 4.2, 'fermi_energy': 0.0605833326, - 'spin_density': 0.0792504665, + 'spin_density_convergence': 0.0792504665, 'bandgap_units': 'eV', 'force_largest': 0.0, 'energy_hartree': -5090.8728101494, 'walltime_units': 'seconds', - 'charge_density1': 0.0577674505, - 'charge_density2': 0.0461840944, + 'density_convergence': [0.0577674505, 0.0461840944], 'number_of_atoms': 4, 'parser_warnings': [], 'magnetic_moments': [3.3720063737, 3.3719345944, 3.3719329177, 3.3719329162], @@ -294,7 +303,7 @@ def test_performance_extract_calcs(fixture_localhost, generate_calc_job_node): 'number_of_symmetries': 8, 'energy_core_electrons': -2901.8120489845, 'magnetic_moment_units': 'muBohr', - 'overall_charge_density': 0.0682602474, + 'overall_density_convergence': 0.0682602474, 'creator_target_structure': ' ', 'energy_valence_electrons': -71.6009296831, 'magnetic_spin_up_charges': [9.1494766577, 9.1494806151, 9.1494806833, 9.1494806834], diff --git a/tests/tools/test_io_routines.py b/tests/tools/test_io_routines.py index 2cc7cef7c..38d26b96c 100644 --- a/tests/tools/test_io_routines.py +++ b/tests/tools/test_io_routines.py @@ -21,9 +21,8 @@ def test_write_results_to_file_interface(): destination = './outputfiletest' write_results_to_file(inputhead, data, destination=destination) isfile_ = isfile(abspath('./outputfiletest')) - test_file = open(destination, 'r') - content = test_file.read() - test_file.close() + with open(destination, 'r') as test_file: + content = test_file.read() content_exp = 'head\n1.00000000 3.00000000\n2.00000000 4.00000000\n' remove(destination) @@ -46,7 +45,7 @@ def test_write_xps_spectra_datafile_interface(): assert False -def test_compress_fleuroutxml(): +def test_compress_fleuroutxml(eval_xpath): """ test the compress_fleuroutxml function, checks if right number of iterations is kept, or deleted. Further checks if new file is written and if eigenvalues are deleted. @@ -55,7 +54,6 @@ def test_compress_fleuroutxml(): from os.path import abspath, isfile from os import remove from lxml import etree - from aiida_fleur.tools.xml_util import eval_xpath2 from aiida_fleur.tools.io_routines import compress_fleuroutxml testfilepath = abspath('./files/outxml/BeTi_out.xml') @@ -73,7 +71,7 @@ def get_npath(filepath, xpath): parser = etree.XMLParser(recover=False) tree = etree.parse(filepath, parser) - return len(eval_xpath2(tree.getroot(), xpath)) + return len(eval_xpath(tree.getroot(), xpath, list_return=True)) # test new file exists, and right number of iteration, eig del compress_fleuroutxml(testfilepath, dest_file_path=dest_path, iterations_to_keep=15) diff --git a/tests/tools/test_plot_fleur.py b/tests/tools/test_plot_fleur.py index cd474b257..cfe741ecf 100644 --- a/tests/tools/test_plot_fleur.py +++ b/tests/tools/test_plot_fleur.py @@ -80,7 +80,6 @@ def test_plot_fleur_multiple_wc_matplotlib(aiida_profile, read_dict_from_file): #assert isinstance(p_eos[0], type(Axes)) -@pytest.mark.skip(reason='does work, but requires current masci-tool develop branch >0.10.3') def test_plot_fleur_single_wc_bokeh(aiida_profile, read_dict_from_file): """test if plot fleur can visualize a single workchain with bokeh backend""" try: #bokeh is not a prerequisite of Aiida-Fleur, might become of masci-tools diff --git a/tests/tools/test_set_nmmpmat.py b/tests/tools/test_set_nmmpmat.py deleted file mode 100644 index 00b432ec0..000000000 --- a/tests/tools/test_set_nmmpmat.py +++ /dev/null @@ -1,279 +0,0 @@ -# -*- coding: utf-8 -*- -'''Contains tests for the set_nmmpmat routine used for modifying the - density matrix for LDA+U calculations.''' - -from __future__ import absolute_import -import os -import pytest -import aiida_fleur -import numpy as np - -aiida_path = os.path.dirname(aiida_fleur.__file__) -TEST_INP_XML_PATH = os.path.join(aiida_path, '../tests/files/inpxml/GaAsMultiForceXML/inp.xml') -TEST_NMMPMAT_PATH = os.path.join(aiida_path, '../tests/files/n_mmp_mat/n_mmp_mat_GaAsMultiForceXML') - - -def test_set_nmmpmat_nofile(inpxml_etree): - """Test setting of nmmpmat with no initial nmmpmat file given""" - from aiida_fleur.tools.set_nmmpmat import set_nmmpmat - etree = inpxml_etree(TEST_INP_XML_PATH) - - correct_result = [ - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 1.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 2.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 3.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 4.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 5.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1.0000000000000 0.0000000000000 -2.0000000000000', - ' 0.0000000000000 3.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 4.0000000000000 0.0000000000000 -5.0000000000000', - ' 0.0000000000000 6.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 7.0000000000000 0.0000000000000 -8.0000000000000', - ' 0.0000000000000 9.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000' - ] - - nmmp_lines = None - nmmp_lines = set_nmmpmat(etree, nmmp_lines, species_name='Ga-1', orbital=2, spin=1, occStates=[1, 2, 3, 4, 5]) - nmmp_lines = set_nmmpmat(etree, nmmp_lines, 'As-2', orbital=1, spin=1, denmat=[[1, -2, 3], [4, -5, 6], [7, -8, 9]]) - assert len(nmmp_lines) == len(correct_result) - for index, line in enumerate(nmmp_lines): - assert line == correct_result[index] - - -def test_set_nmmpmat_file(inpxml_etree): - """Test setting of nmmpmat with initial nmmpmat file given""" - from aiida_fleur.tools.set_nmmpmat import set_nmmpmat - etree = inpxml_etree(TEST_INP_XML_PATH) - - correct_result = [ - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.2508689292278 0.0000000000000 0.0116095434683', - ' -0.0116095434683 0.0000000000000 0.0164183738259 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0116095434683 0.0116095434683 0.2508689292278', - ' 0.0000000000000 -0.0116095434683 0.0116095434683 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 -0.0164183738259 -0.0116095434683', - ' -0.0116095434683 0.2508689292278 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 1.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 2.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 3.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 4.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 5.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 1.9974429541760 0.0000000000000 0.0002716144685 0.0001936327585 0.0003798727702', - ' -0.0000470947957 0.0003292935780 -0.0001359536491 -0.0023058909977 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0002716144685 -0.0001936327585 1.9997488451737 0.0000000000000 -0.0002134837829', - ' -0.0001663889872 0.0000000000000 0.0001356608195 0.0001359536491 -0.0003292935780 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0003798727702 0.0000470947957 -0.0002134837829 0.0001663889872 1.9951370631782', - ' 0.0000000000000 -0.0001663889872 -0.0002134837829 -0.0003798727702 -0.0000470947957 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0003292935780 0.0001359536491 0.0000000000000 -0.0001356608195 -0.0001663889872', - ' 0.0002134837829 1.9997488451737 0.0000000000000 0.0001936327585 0.0002716144685 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 -0.0023058909977 0.0000000000000 0.0001359536491 0.0003292935780 -0.0003798727702', - ' 0.0000470947957 0.0001936327585 -0.0002716144685 1.9974429541760 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1.0000000000000 0.0000000000000 -2.0000000000000', - ' 0.0000000000000 3.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 4.0000000000000 0.0000000000000 -5.0000000000000', - ' 0.0000000000000 6.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 7.0000000000000 0.0000000000000 -8.0000000000000', - ' 0.0000000000000 9.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000' - ] - - with open(TEST_NMMPMAT_PATH, mode='r') as nmmpfile: - nmmp_lines = nmmpfile.read().split('\n') - - nmmp_lines = set_nmmpmat(etree, nmmp_lines, species_name='Ga-1', orbital=2, spin=1, occStates=[1, 2, 3, 4, 5]) - nmmp_lines = set_nmmpmat(etree, nmmp_lines, 'As-2', orbital=1, spin=1, denmat=[[1, -2, 3], [4, -5, 6], [7, -8, 9]]) - assert len(nmmp_lines) == len(correct_result) - for index, line in enumerate(nmmp_lines): - assert line == correct_result[index] - - -def test_set_nmmpmat_file_get_wigner_matrix(inpxml_etree): - """Test get_wigner_matrix by calling set_nmmpmat_file with theta, or phi != None""" - from aiida_fleur.tools.set_nmmpmat import set_nmmpmat - - etree = inpxml_etree(TEST_INP_XML_PATH) - correct_result = [ - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.5000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.5000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.5000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.5000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 -1.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1.0000000000000 0.0000000000000', - ' 0.0000000000000 1.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ' 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000', - ] - nmmp_lines = None - - nmmp_lines = set_nmmpmat(etree, - nmmp_lines, - species_name='Ga-1', - orbital=1, - spin=1, - occStates=[1, 0, 1], - theta=np.pi / 2.0) - nmmp_lines = set_nmmpmat(etree, - nmmp_lines, - 'As-2', - orbital=1, - spin=1, - denmat=[[1, 0, 1], [0, 0, 0], [1, 0, 1]], - phi=np.pi / 4.0, - theta=np.pi / 2.0) - assert len(nmmp_lines) == len(correct_result) - for index, line in enumerate(nmmp_lines): - #Replace minus zero with plus zero - line = line.replace('-0.0000000000000', ' 0.0000000000000') - assert line == correct_result[index] - - -def test_validate_nmmpmat(inpxml_etree): - """Test validation method of nmmpmat file together with inp.xml file""" - from aiida_fleur.tools.set_nmmpmat import set_nmmpmat, validate_nmmpmat - etree = inpxml_etree(TEST_INP_XML_PATH) - - with open(TEST_NMMPMAT_PATH, mode='r') as nmmpfile: - nmmp_lines_orig = nmmpfile.read().split('\n') - - validate_nmmpmat(etree, nmmp_lines_orig) #should not raise - - #Test number of lines error - nmmp_lines = nmmp_lines_orig - nmmp_lines.append('0.0') - with pytest.raises(ValueError): - validate_nmmpmat(etree, nmmp_lines) - nmmp_lines.remove('0.0') - - #Test invalid diagonal element error - nmmp_lines = nmmp_lines_orig - nmmp_lines = set_nmmpmat(etree, nmmp_lines, species_name='Ga-1', orbital=2, spin=1, occStates=[1, 2, 3, 4, 5]) - nmmp_lines = set_nmmpmat(etree, nmmp_lines, 'As-2', orbital=1, spin=1, denmat=[[1, -2, 3], [4, -5, 6], [7, -8, 9]]) - with pytest.raises(ValueError): - validate_nmmpmat(etree, nmmp_lines) - - #Test invalid outsied value error - nmmp_lines = nmmp_lines_orig - nmmp_lines[ - 0] = ' 0.0000000000000 9.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000' - - with pytest.raises(ValueError): - validate_nmmpmat(etree, nmmp_lines) diff --git a/tests/tools/test_xml_util.py b/tests/tools/test_xml_util.py deleted file mode 100644 index 121f5f6d3..000000000 --- a/tests/tools/test_xml_util.py +++ /dev/null @@ -1,1061 +0,0 @@ -# -*- coding: utf-8 -*- -'''Contains tests for xml_utils within aiida-fleur, some test are autogenerated.''' - -from __future__ import absolute_import -import os -import pytest -import aiida_fleur - -aiida_path = os.path.dirname(aiida_fleur.__file__) -TEST_INP_XML_PATH = os.path.join(aiida_path, '../tests/files/inpxml/FePt/inp.xml') - - -def test_xml_set_attribv_occ(inpxml_etree): - from aiida_fleur.tools.xml_util import xml_set_attribv_occ, eval_xpath - etree = inpxml_etree(TEST_INP_XML_PATH) - - xml_set_attribv_occ(etree, '/fleurInput/calculationSetup/cutoffs', 'Gmax', 11.00) - assert float(eval_xpath(etree, '/fleurInput/calculationSetup/cutoffs/@Gmax')) == 11 - - xml_set_attribv_occ(etree, '/fleurInput/atomGroups/atomGroup', 'species', 'TEST-1', [1]) - assert eval_xpath(etree, '/fleurInput/atomGroups/atomGroup/@species') == ['Fe-1', 'TEST-1'] - - xml_set_attribv_occ(etree, '/fleurInput/atomGroups/atomGroup', 'species', 'TEST-2', [-1]) - assert eval_xpath(etree, '/fleurInput/atomGroups/atomGroup/@species') == ['TEST-2', 'TEST-2'] - - -def test_xml_set_first_attribv(inpxml_etree): - from aiida_fleur.tools.xml_util import xml_set_first_attribv, eval_xpath - etree = inpxml_etree(TEST_INP_XML_PATH) - - xml_set_first_attribv(etree, '/fleurInput/calculationSetup/cutoffs', 'Gmax', 11.00) - assert float(eval_xpath(etree, '/fleurInput/calculationSetup/cutoffs/@Gmax')) == 11 - - xml_set_first_attribv(etree, '/fleurInput/atomGroups/atomGroup', 'species', 'TEST-1') - assert eval_xpath(etree, '/fleurInput/atomGroups/atomGroup/@species') == ['TEST-1', 'Pt-1'] - - -def test_xml_set_all_attribv(inpxml_etree): - from aiida_fleur.tools.xml_util import xml_set_all_attribv, eval_xpath - etree = inpxml_etree(TEST_INP_XML_PATH) - - xml_set_all_attribv(etree, '/fleurInput/calculationSetup/cutoffs', 'Gmax', 11.00) - assert float(eval_xpath(etree, '/fleurInput/calculationSetup/cutoffs/@Gmax')) == 11 - - xml_set_all_attribv(etree, '/fleurInput/atomGroups/atomGroup', 'species', 'TEST-1') - assert eval_xpath(etree, '/fleurInput/atomGroups/atomGroup/@species') == ['TEST-1', 'TEST-1'] - - xml_set_all_attribv(etree, '/fleurInput/atomGroups/atomGroup', 'species', ['TEST-1', 23]) - assert eval_xpath(etree, '/fleurInput/atomGroups/atomGroup/@species') == ['TEST-1', '23'] - - -def test_xml_set_text(inpxml_etree): - from aiida_fleur.tools.xml_util import xml_set_text, eval_xpath2 - etree = inpxml_etree(TEST_INP_XML_PATH) - - second_text = eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/filmPos')[1].text - - xml_set_text(etree, '/fleurInput/atomGroups/atomGroup/filmPos', 'test_text') - assert eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/filmPos')[0].text == 'test_text' - assert eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/filmPos')[1].text == second_text - - -def test_xml_set_text_occ(inpxml_etree): - from aiida_fleur.tools.xml_util import xml_set_text_occ, eval_xpath2 - etree = inpxml_etree(TEST_INP_XML_PATH) - - first_text = eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/filmPos')[0].text - second_text = eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/filmPos')[1].text - - xml_set_text_occ(etree, '/fleurInput/atomGroups/atomGroup/filmPos', 'test_text', occ=0) - assert eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/filmPos')[0].text == 'test_text' - assert eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/filmPos')[1].text == second_text - - etree = inpxml_etree(TEST_INP_XML_PATH) - xml_set_text_occ(etree, '/fleurInput/atomGroups/atomGroup/filmPos', 'test_text', occ=1) - assert eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/filmPos')[0].text == first_text - assert eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/filmPos')[1].text == 'test_text' - - -def test_xml_set_all_text(inpxml_etree): - from aiida_fleur.tools.xml_util import xml_set_all_text, eval_xpath2 - etree = inpxml_etree(TEST_INP_XML_PATH) - - xml_set_all_text(etree, '/fleurInput/atomGroups/atomGroup/filmPos', 'test_text') - assert eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/filmPos')[0].text == 'test_text' - assert eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/filmPos')[1].text == 'test_text' - - xml_set_all_text(etree, '/fleurInput/atomGroups/atomGroup/filmPos', ['test_text2', 'test_ext3']) - assert eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/filmPos')[0].text == 'test_text2' - assert eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/filmPos')[1].text == 'test_ext3' - - -def test_create_tag(inpxml_etree): - from aiida_fleur.tools.xml_util import create_tag, eval_xpath3 - etree = inpxml_etree(TEST_INP_XML_PATH) - - create_tag(etree, '/fleurInput/cell/filmLattice/bravaisMatrix', 'TEST_TAG', create=False) - assert eval_xpath3(etree, '/fleurInput/cell/filmLattice/bravaisMatrix')[0][3].tag == 'TEST_TAG' - - create_tag(etree, '/fleurInput/cell/filmLattice/bravaisMatrix', 'TEST_TAG2', create=False, place_index=1) - tag_names = [x.tag for x in eval_xpath3(etree, '/fleurInput/cell/filmLattice/bravaisMatrix')[0]] - assert tag_names == ['row-1', 'TEST_TAG2', 'row-2', 'row-3', 'TEST_TAG'] - - create_tag(etree, - '/fleurInput/cell/filmLattice/bravaisMatrix', - 'TEST_TAG3', - create=False, - place_index=True, - tag_order=['row-1', 'TEST_TAG2', 'TEST_TAG3', 'row-2', 'row-3', 'TEST_TAG']) - tag_names = [x.tag for x in eval_xpath3(etree, '/fleurInput/cell/filmLattice/bravaisMatrix')[0]] - assert tag_names == ['row-1', 'TEST_TAG2', 'TEST_TAG3', 'row-2', 'row-3', 'TEST_TAG'] - - create_tag(etree, - '/fleurInput/cell/filmLattice/bravaisMatrix', - 'TEST_TAG4', - create=False, - place_index=True, - tag_order=['row-1', 'TEST_TAG2', 'TEST_TAG3', 'row-2', 'TEST_TAG4', 'row-3', 'TEST_TAG']) - tag_names = [x.tag for x in eval_xpath3(etree, '/fleurInput/cell/filmLattice/bravaisMatrix')[0]] - assert tag_names == ['row-1', 'TEST_TAG2', 'TEST_TAG3', 'row-2', 'TEST_TAG4', 'row-3', 'TEST_TAG'] - - create_tag(etree, - '/fleurInput/cell/filmLattice/bravaisMatrix', - 'TEST_TAG0', - create=False, - place_index=True, - tag_order=['TEST_TAG0', 'row-1', 'TEST_TAG2', 'TEST_TAG3', 'row-2', 'TEST_TAG4', 'row-3', 'TEST_TAG']) - tag_names = [x.tag for x in eval_xpath3(etree, '/fleurInput/cell/filmLattice/bravaisMatrix')[0]] - assert tag_names == ['TEST_TAG0', 'row-1', 'TEST_TAG2', 'TEST_TAG3', 'row-2', 'TEST_TAG4', 'row-3', 'TEST_TAG'] - - with pytest.raises(ValueError) as excinfo: - create_tag(etree, - '/fleurInput/cell/filmLattice/bravaisMatrix', - 'TEST_TAG5', - create=False, - place_index=True, - tag_order=[ - 'TEST_TAG0', 'row-1', 'TEST_TAG3', 'TEST_TAG2', 'TEST_TAG5', 'row-2', 'TEST_TAG4', 'row-3', - 'TEST_TAG' - ]) - assert str(excinfo.value) == 'Existing order does not correspond to tag_order list' - - with pytest.raises(ValueError) as excinfo: - create_tag( - etree, - '/fleurInput/cell/filmLattice/bravaisMatrix', - 'TEST_TAG5', - create=False, - place_index=True, - tag_order=['TEST_TAG0', 'row-1', 'TEST_TAG3', 'TEST_TAG2', 'row-2', 'TEST_TAG4', 'row-3', 'TEST_TAG']) - assert str(excinfo.value) == 'Did not find element name in the tag_order list' - - -def test_delete_att(inpxml_etree): - from aiida_fleur.tools.xml_util import delete_att, eval_xpath2 - etree = inpxml_etree(TEST_INP_XML_PATH) - - assert eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/filmPos/@label')[0] == ' 222' - - delete_att(etree, '/fleurInput/atomGroups/atomGroup/filmPos', 'label') - assert eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/filmPos/@label') == [] - - -def test_delete_tag(inpxml_etree): - from aiida_fleur.tools.xml_util import delete_tag, eval_xpath2 - etree = inpxml_etree(TEST_INP_XML_PATH) - - assert eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/filmPos') != [] - - delete_tag(etree, '/fleurInput/atomGroups/atomGroup/filmPos') - assert eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/filmPos') == [] - - -def test_replace_tag(inpxml_etree): - from aiida_fleur.tools.xml_util import replace_tag, eval_xpath2 - etree = inpxml_etree(TEST_INP_XML_PATH) - - to_insert = eval_xpath2(etree, '/fleurInput/calculationSetup/cutoffs')[0] - print(to_insert) - print(eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/filmPos')) - - replace_tag(etree, '/fleurInput/atomGroups/atomGroup/filmPos', to_insert) - - assert eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/filmPos') == [] - assert eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/cutoffs')[0] == to_insert - - -@pytest.mark.skip(reason='econfig extraction is not implemented') -def test_get_inpgen_para_from_xml(inpxml_etree): - from aiida_fleur.tools.xml_util import get_inpgen_para_from_xml - etree = inpxml_etree(TEST_INP_XML_PATH) - - result = { - 'comp': { - 'jspins': 2.0, - 'frcor': False, - 'ctail': True, - 'kcrel': '0', - 'gmax': 10.0, - 'gmaxxc': 8.7, - 'kmax': 4.0 - }, - 'atom0': { - 'z': 26, - 'rmt': 2.2, - 'dx': 0.016, - 'jri': 787, - 'lmax': 10, - 'lnonsph': 6, - # 'econfig': , - 'lo': '', - 'element': 'Fe' - }, - 'atom1': { - 'z': 78, - 'rmt': 2.2, - 'dx': 0.017, - 'jri': 787, - 'lmax': 10, - 'lnonsph': 6, - # 'econfig': , - 'lo': '', - 'element': 'Pt' - }, - 'title': 'A Fleur input generator calculation with aiida', - 'exco': { - 'xctyp': 'vwn' - } - } - - dict_result = get_inpgen_para_from_xml(etree) - assert dict_result == result - - -class TestSetSpecies: - """Tests for set_species""" - - paths = [ - 'mtSphere/@radius', 'atomicCutoffs/@lmax', 'energyParameters/@s', 'electronConfig/coreConfig', - 'electronConfig/stateOccupation/@state', 'electronConfig/stateOccupation/@state', 'special/@socscale', - 'ldaU/@test_att', 'ldaU/@test_att', 'lo/@test_att', 'lo/@test_att' - ] - - attdicts = [{ - 'mtSphere': { - 'radius': 3.333 - } - }, { - 'atomicCutoffs': { - 'lmax': 7.0 - } - }, { - 'energyParameters': { - 's': 3.0 - } - }, { - 'electronConfig': { - 'coreConfig': 'test' - } - }, { - 'electronConfig': { - 'stateOccupation': { - 'state': 'state' - } - } - }, { - 'electronConfig': { - 'stateOccupation': [{ - 'state': 'state' - }, { - 'state': 'state2' - }] - } - }, { - 'special': { - 'socscale': 1.0 - } - }, { - 'ldaU': { - 'test_att': 2.0 - } - }, { - 'ldaU': [{ - 'test_att': 2.0 - }, { - 'test_att': 23.0 - }] - }, { - 'lo': { - 'test_att': 2.0 - } - }, { - 'lo': [{ - 'test_att': 2.0 - }, { - 'test_att': 33.0 - }] - } - # 'nocoParams': {'test_att' : 2, 'qss' : '123 123 123'}, - ] - - results = [ - '3.333', '7.0', '3.0', 'test', 'state', ['state', 'state2'], '1.0', '2.0', ['2.0', '23.0'], '2.0', - ['2.0', '33.0'] - ] - - @staticmethod - @pytest.mark.parametrize('attr_dict,correct_result,path', zip(attdicts, results, paths)) - def test_set_species(inpxml_etree, attr_dict, correct_result, path): - from aiida_fleur.tools.xml_util import set_species, eval_xpath2 - etree = inpxml_etree(TEST_INP_XML_PATH) - - set_species(etree, 'Fe-1', attributedict=attr_dict) - - result = eval_xpath2(etree, '/fleurInput/atomSpecies/species[@name="Fe-1"]/' + path) - - if isinstance(correct_result, str): - if 'coreConfig' in path: - assert result[0].text == correct_result - else: - assert result[0] == correct_result - elif isinstance(correct_result, (float, int)): - assert result[0] == correct_result - else: - assert correct_result == result - - @staticmethod - @pytest.mark.parametrize('attr_dict,correct_result,path', zip(attdicts, results, paths)) - def test_set_species_label(inpxml_etree, attr_dict, correct_result, path): - from aiida_fleur.tools.xml_util import set_species_label, eval_xpath2 - etree = inpxml_etree(TEST_INP_XML_PATH) - - set_species_label(etree, ' 222', attributedict=attr_dict) - - result = eval_xpath2(etree, '/fleurInput/atomSpecies/species[@name="Fe-1"]/' + path) - - if isinstance(correct_result, str): - if 'coreConfig' in path: - assert result[0].text == correct_result - else: - assert result[0] == correct_result - elif isinstance(correct_result, (float, int)): - assert result[0] == correct_result - else: - assert correct_result == result - - @staticmethod - @pytest.mark.parametrize('attr_dict,correct_result,path', zip(attdicts, results, paths)) - def test_set_species_all_string(inpxml_etree, attr_dict, correct_result, path): - from aiida_fleur.tools.xml_util import set_species, eval_xpath2 - etree = inpxml_etree(TEST_INP_XML_PATH) - - set_species(etree, 'all-Fe', attributedict=attr_dict) - - result = eval_xpath2(etree, '/fleurInput/atomSpecies/species[@name="Fe-1"]/' + path) - - if isinstance(correct_result, str): - if 'coreConfig' in path: - assert result[0].text == correct_result - else: - assert result[0] == correct_result - elif isinstance(correct_result, (float, int)): - assert result[0] == correct_result - else: - assert correct_result == result - - results_all = [[x, x] if not isinstance(x, list) else [x[0], x[1], x[0], x[1]] for x in results] - - @staticmethod - @pytest.mark.parametrize('attr_dict,correct_result,path', zip(attdicts, results_all, paths)) - def test_set_species_all(inpxml_etree, attr_dict, correct_result, path): - from aiida_fleur.tools.xml_util import set_species, eval_xpath2 - etree = inpxml_etree(TEST_INP_XML_PATH) - - set_species(etree, 'all', attributedict=attr_dict) - - result = eval_xpath2(etree, '/fleurInput/atomSpecies/species/' + path) - - import lxml - print(lxml.etree.tostring(etree)) - - if 'coreConfig' in path: - assert [x.text for x in result] == correct_result - else: - assert result == correct_result - - @staticmethod - @pytest.mark.parametrize('attr_dict,correct_result,path', zip(attdicts, results_all, paths)) - def test_set_species_label_all(inpxml_etree, attr_dict, correct_result, path): - from aiida_fleur.tools.xml_util import set_species_label, eval_xpath2 - etree = inpxml_etree(TEST_INP_XML_PATH) - - set_species_label(etree, 'all', attributedict=attr_dict) - - result = eval_xpath2(etree, '/fleurInput/atomSpecies/species/' + path) - - if 'coreConfig' in path: - assert [x.text for x in result] == correct_result - else: - assert result == correct_result - - -class TestChangeAtomgrAtt: - """Tests for change_atomgr_att""" - - paths = ['force/@relaxXYZ', 'nocoParams/@beta'] - - attdicts = [{'force': [('relaxXYZ', 'FFF')]}, {'nocoParams': [('beta', 7.0)]}] - - results = ['FFF', '7.0'] - - @staticmethod - @pytest.mark.parametrize('attr_dict,correct_result,path', zip(attdicts, results, paths)) - def test_change_atomgr_att(inpxml_etree, attr_dict, correct_result, path): - from aiida_fleur.tools.xml_util import change_atomgr_att, eval_xpath2 - etree = inpxml_etree(TEST_INP_XML_PATH) - - change_atomgr_att(etree, attributedict=attr_dict, species='Fe-1') - - result = eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup[@species="Fe-1"]/' + path) - - assert result[0] == correct_result - - @staticmethod - @pytest.mark.parametrize('attr_dict,correct_result,path', zip(attdicts, results, paths)) - def test_change_atomgr_att_position(inpxml_etree, attr_dict, correct_result, path): - from aiida_fleur.tools.xml_util import change_atomgr_att, eval_xpath2 - etree = inpxml_etree(TEST_INP_XML_PATH) - - change_atomgr_att(etree, attributedict=attr_dict, position=1) - - result = eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/' + path) - - assert result[0] == correct_result - - @staticmethod - @pytest.mark.parametrize('attr_dict,correct_result,path', zip(attdicts, results, paths)) - def test_change_atomgr_att_label(inpxml_etree, attr_dict, correct_result, path): - from aiida_fleur.tools.xml_util import change_atomgr_att_label, eval_xpath2 - etree = inpxml_etree(TEST_INP_XML_PATH) - - change_atomgr_att_label(etree, attributedict=attr_dict, at_label=' 222') - - result = eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup[@species="Fe-1"]/' + path) - - assert result[0] == correct_result - - results_all = [[x, x] for x in results] - - @staticmethod - @pytest.mark.parametrize('attr_dict,correct_result,path', zip(attdicts, results_all, paths)) - def test_change_atomgr_att_all(inpxml_etree, attr_dict, correct_result, path): - from aiida_fleur.tools.xml_util import change_atomgr_att, eval_xpath2 - etree = inpxml_etree(TEST_INP_XML_PATH) - - change_atomgr_att(etree, attributedict=attr_dict, species='all') - - result = eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/' + path) - - assert result == correct_result - - @staticmethod - @pytest.mark.parametrize('attr_dict,correct_result,path', zip(attdicts, results_all, paths)) - def test_change_atomgr_att_label_all(inpxml_etree, attr_dict, correct_result, path): - from aiida_fleur.tools.xml_util import change_atomgr_att_label, eval_xpath2 - etree = inpxml_etree(TEST_INP_XML_PATH) - - change_atomgr_att_label(etree, attributedict=attr_dict, at_label='all') - - result = eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup/' + path) - - assert result == correct_result - - def test_change_atomgr_att_fail(self, inpxml_etree): - from aiida_fleur.tools.xml_util import change_atomgr_att, eval_xpath2 - etree = inpxml_etree(TEST_INP_XML_PATH) - - change_atomgr_att(etree, attributedict=self.attdicts[0]) - - result = eval_xpath2(etree, '/fleurInput/atomGroups/atomGroup[@species="Fe-1"]/' + self.paths[0]) - - assert result[0] == 'TTT' - - -class TestSetInpchanges: - """Tests group for set_inpchanges method""" - from aiida_fleur.tools.xml_util import get_inpxml_file_structure - - xml_structure = get_inpxml_file_structure() - - paths = xml_structure[12] - - @pytest.mark.parametrize('name,path', paths.items()) - def test_set_inpchanges(self, inpxml_etree, name, path): - from aiida_fleur.tools.xml_util import set_inpchanges, eval_xpath2 - etree = inpxml_etree(TEST_INP_XML_PATH) - - skip_paths = ['atomSpecies', 'atomGroups', 'bzIntegration', 'kPointCount', 'bulkLattice', 'bravaisMatrix', 'a1'] - - if any(x in path for x in skip_paths): - pytest.skip('This attribute is not tested for FePt/inp.xml') - elif name in self.xml_structure[11].keys(): - set_inpchanges(etree, change_dict={name: 'test'}) - if name not in ['relPos', 'absPos']: - result = eval_xpath2(etree, path)[0] - assert result.text == 'test' - elif name in self.xml_structure[0]: - set_inpchanges(etree, change_dict={name: 'T'}) - result = eval_xpath2(etree, path + '/@{}'.format(name)) - assert result[0] == 'T' - elif name in self.xml_structure[4] or name in self.xml_structure[3]: - set_inpchanges(etree, change_dict={name: 33}) - result = eval_xpath2(etree, path + '/@{}'.format(name)) - assert float(result[0]) == 33 - elif name in self.xml_structure[5]: - set_inpchanges(etree, change_dict={name: 'test'}) - if name == 'xcFunctional': - result = eval_xpath2(etree, path + '/@{}'.format('name')) - else: - result = eval_xpath2(etree, path + '/@{}'.format(name)) - assert result[0] == 'test' - else: - raise BaseException('A switch that you want to set is not one of the supported types.' - 'Or you made a mistake during new switch registration') - - def test_set_inpchanges_fail(self, inpxml_etree): - from aiida_fleur.tools.xml_util import set_inpchanges, eval_xpath2 - from aiida.common.exceptions import InputValidationError - - etree = inpxml_etree(TEST_INP_XML_PATH) - with pytest.raises(InputValidationError): - set_inpchanges(etree, change_dict={'not_existing': 'test'}) - - -def test_inpxml_to_dict(inpxml_etree): - from aiida_fleur.tools.xml_util import inpxml_todict, get_inpxml_file_structure, clear_xml - - correct = { - 'fleurInputVersion': '0.31', - 'comment': 'A Fleur input generator calculation with aiida', - 'calculationSetup': { - 'cutoffs': { - 'Kmax': 4.4, - 'Gmax': 10.9, - 'GmaxXC': 9.1, - 'numbands': 0, - }, - 'scfLoop': { - 'itmax': 60, - 'minDistance': 2e-05, - 'maxIterBroyd': 99, - 'imix': 'Anderson', - 'alpha': 0.05, - 'precondParam': '0.0', - 'spinf': 2.0, - }, - 'coreElectrons': { - 'ctail': False, - 'frcor': False, - 'kcrel': 0, - 'coretail_lmax': '0', - }, - 'magnetism': { - 'jspins': 2, - 'l_noco': True, - 'swsp': False, - 'lflip': False, - }, - 'soc': { - 'theta': 0.0, - 'phi': 0.0, - 'l_soc': False, - 'spav': False, - }, - 'prodBasis': { - 'gcutm': '2.90000000', - 'tolerance': '.00010000', - 'ewaldlambda': '3', - 'lexp': '16', - 'bands': '0', - }, - 'nocoParams': { - 'l_ss': True, - 'l_mperp': 'F', - 'l_constr': 'F', - 'mix_b': '.00000000', - 'qss': '0.0 0.0 0.0', - }, - 'expertModes': { - 'eig66': False, - 'gw': 0, - 'isec1': 99, - 'lpr': 0, - 'pot8': False, - 'secvar': False - }, - 'geometryOptimization': { - 'l_f': False, - 'forcealpha': 1.0, - 'forcemix': 'BFGS', - 'force_converged': 1e-05, - 'qfix': 0, - 'epsdisp': 1e-05, - 'epsforce': 1e-05, - }, - 'ldaU': [{ - 'l_linMix': False, - 'mixParam': 0.05, - 'spinf': 1.0 - }], - 'bzIntegration': { - 'altKPointSet': { - 'kPointCount': { - 'count': 240, - 'gamma': False - }, - 'purpose': 'bands' - }, - 'valenceElectrons': 18.0, - 'mode': 'hist', - 'fermiSmearingEnergy': 0.001, - 'kPointList': { - 'posScale': '1.00000000', - 'weightScale': '1.00000000', - 'count': 1, - 'kPoint': ['0.000000 0.000000 0.000000'] - }, - }, - 'energyParameterLimits': { - 'ellow': -0.8, - 'elup': 0.5 - }, - }, - 'cell': { - 'symmetryOperations': { - 'symOp': [{ - 'row-1': '1 0 0 .0000000000', - 'row-2': '0 1 0 .0000000000', - 'row-3': '0 0 1 .0000000000' - }, { - 'row-1': '1 0 0 .0000000000', - 'row-2': '0 -1 0 .0000000000', - 'row-3': '0 0 1 .0000000000' - }] - }, - 'filmLattice': { - 'scale': 1.0, - 'dVac': 7.35, - 'latnam': 'any', - 'dTilda': 10.91, - 'bravaisMatrix': { - 'row-1': '5.301179702900000 .000000000000000 .000000000000000', - 'row-2': '.000000000000000 7.497000033000000 .000000000000000', - 'row-3': '.000000000000000 .000000000000000 7.992850008800000' - }, - 'vacuumEnergyParameters': { - 'vacuum': '2', - 'spinUp': '-.25000000', - 'spinDown': '-.25000000' - }, - } - }, - 'xcFunctional': { - 'name': 'vwn', - 'relativisticCorrections': False - }, - 'atomSpecies': { - 'species': [{ - 'name': 'Fe-1', - 'element': 'Fe', - 'atomicNumber': 26, - 'coreStates': 7, - 'mtSphere': { - 'radius': 2.2, - 'gridPoints': 787, - 'logIncrement': 0.016 - }, - 'atomicCutoffs': { - 'lmax': 10, - 'lnonsphr': 6 - }, - 'energyParameters': { - 's': 4, - 'p': 4, - 'd': 3, - 'f': 4, - }, - 'flipSpin': True, - 'magMom': 2.2, - 'prodBasis': { - 'lcutm': '4', - 'lcutwf': '9', - 'select': '4 0 4 2' - }, - 'electronConfig': { - 'coreConfig': - '[Ar]', - 'valenceConfig': - '(4s1/2) (3d3/2) (3d5/2)', - 'stateOccupation': [{ - 'state': '(3d3/2)', - 'spinUp': '2.00000000', - 'spinDown': '1.00000000' - }, { - 'state': '(3d5/2)', - 'spinUp': '3.00000000', - 'spinDown': '.00000000' - }] - }, - }, { - 'name': 'Pt-1', - 'element': 'Pt', - 'atomicNumber': 78, - 'coreStates': 19, - 'mtSphere': { - 'radius': 2.3, - 'gridPoints': 787, - 'logIncrement': 0.017 - }, - 'atomicCutoffs': { - 'lmax': 10, - 'lnonsphr': 6 - }, - 'energyParameters': { - 's': 6, - 'p': 6, - 'd': 5, - 'f': 5, - }, - 'flipSpin': True, - 'magMom': 0.0, - 'prodBasis': { - 'lcutm': '4', - 'lcutwf': '9', - 'select': '4 0 4 2' - }, - 'electronConfig': { - 'coreConfig': - '[Xe] (4f5/2) (4f7/2)', - 'valenceConfig': - '(6s1/2) (5d3/2) (5d5/2)', - 'stateOccupation': [{ - 'state': '(6s1/2)', - 'spinUp': '.50000000', - 'spinDown': '.50000000' - }, { - 'state': '(5d5/2)', - 'spinUp': '3.00000000', - 'spinDown': '2.00000000' - }] - }, - }] - }, - 'atomGroups': { - 'atomGroup': [{ - 'species': 'Fe-1', - 'filmPos': ['.0000000000 .0000000000 -.9964250044'], - 'force': { - 'calculate': True, - 'relaxXYZ': 'TTT' - }, - 'nocoParams': { - 'l_relax': 'F', - 'alpha': 0.0, - 'beta': '1.570796326', - 'b_cons_x': '.00000000', - 'b_cons_y': '.00000000', - }, - }, { - 'species': 'Pt-1', - 'filmPos': ['1.000/2.000 1.000/2.000 .9964250044'], - 'force': { - 'calculate': True, - 'relaxXYZ': 'TTT' - }, - 'nocoParams': { - 'l_relax': 'F', - 'alpha': 0.0, - 'beta': '1.570796326', - 'b_cons_x': '.00000000', - 'b_cons_y': '.00000000', - }, - }] - }, - 'output': { - 'dos': False, - 'band': False, - 'vacdos': False, - 'slice': False, - 'mcd': 'F', - 'checks': { - 'vchk': False, - 'cdinf': False - }, - 'densityOfStates': { - 'ndir': 0, - 'minEnergy': -0.5, - 'maxEnergy': 0.5, - 'sigma': 0.015, - }, - 'vacuumDOS': { - 'layers': 0, - 'integ': False, - 'star': False, - 'nstars': 0, - 'locx1': 0.0, - 'locy1': 0.0, - 'locx2': 0.0, - 'locy2': 0.0, - 'nstm': 0, - 'tworkf': 0.0, - }, - 'unfoldingBand': { - 'unfoldBand': 'F', - 'supercellX': '1', - 'supercellY': '1', - 'supercellZ': '1', - }, - 'plotting': { - 'iplot': 0, - 'plplot': False, - 'score': False - }, - 'chargeDensitySlicing': { - 'numkpt': 0, - 'minEigenval': 0.0, - 'maxEigenval': 0.0, - 'nnne': 0, - 'pallst': False, - }, - 'specialOutput': { - 'eonly': False, - 'bmt': False - }, - 'magneticCircularDichroism': { - 'energyLo': '-10.00000000', - 'energyUp': '.00000000' - }, - }, - } - - xml_structure = get_inpxml_file_structure() - etree = inpxml_etree(TEST_INP_XML_PATH) - inpxml_dict = inpxml_todict(clear_xml(etree).getroot(), xml_structure) - - assert inpxml_dict == correct - - -class TestShiftValue: - """ Test group for test_shift value function """ - from aiida_fleur.tools.xml_util import get_inpxml_file_structure - xml_structure = get_inpxml_file_structure() - - attr_to_test = list(xml_structure[3]) - attr_to_test.extend(xml_structure[4]) - - @pytest.mark.parametrize('attr_name', attr_to_test) - def test_shift_value(self, inpxml_etree, attr_name): - from aiida_fleur.tools.xml_util import shift_value, eval_xpath2 - etree = inpxml_etree(TEST_INP_XML_PATH) - - path = self.xml_structure[12][attr_name] - result_before = eval_xpath2(etree, path + '/@{}'.format(attr_name)) - - if not result_before: - if attr_name in ['nx', 'ny', 'nz', 'scale']: - pytest.skip('This attribute is not tested for FePt/inp.xml') - raise BaseException('Can not find attribute that should exist in FePt/inp.xml') - else: - result_before = result_before[0] - shift_value(etree, {attr_name: 333}) - result = eval_xpath2(etree, path + '/@{}'.format(attr_name))[0] - - assert float(result) - float(result_before) == 333 - - shift_value(etree, {attr_name: 333}) - result = eval_xpath2(etree, path + '/@{}'.format(attr_name))[0] - assert float(result) - float(result_before) == 666 - - attr_to_test_float = list(xml_structure[4]) - - @pytest.mark.parametrize('attr_name', attr_to_test_float) - def test_shift_value_rel(self, inpxml_etree, attr_name): - import math - from aiida_fleur.tools.xml_util import shift_value, eval_xpath2 - etree = inpxml_etree(TEST_INP_XML_PATH) - - path = self.xml_structure[12][attr_name] - result_before = eval_xpath2(etree, path + '/@{}'.format(attr_name)) - - if not result_before: - if attr_name in ['scale']: - pytest.skip('This attribute is not tested for FePt/inp.xml') - raise BaseException('Can not find attribute that should exist in FePt/inp.xml') - else: - result_before = result_before[0] - shift_value(etree, {attr_name: 1.2442}, mode='rel') - result = eval_xpath2(etree, path + '/@{}'.format(attr_name))[0] - - if float(result_before) != 0: - assert math.isclose(float(result) / float(result_before), 1.2442, rel_tol=1e-6) - else: - assert float(result) == 0 - - def test_shift_value_errors(self, inpxml_etree, capsys): - from aiida_fleur.tools.xml_util import shift_value - etree = inpxml_etree(TEST_INP_XML_PATH) - - with pytest.raises(ValueError) as excinfo: - shift_value(etree, {'does_not_exist': 1.2442}) - assert 'Given attribute name either does not ex' in str(excinfo.value) - - with pytest.raises(ValueError) as excinfo: - shift_value(etree, {'jspins': 3.3}) - assert 'You are trying to write a float' in str(excinfo.value) - - with pytest.raises(ValueError) as excinfo: - shift_value(etree, {'l_noco': 33}) - assert 'Given attribute name either does not ex' in str(excinfo.value) - - with pytest.raises(ValueError) as excinfo: - shift_value(etree, {'jspins': 33}, mode='not_a_mode') - assert "Mode should be 'res' " in str(excinfo.value) - - shift_value(etree, {'nz': 333}) - captured = capsys.readouterr() - assert captured.out == 'Can not find nz attribute in the inp.xml, skip it\n' - - -class TestShiftSpeciesLabel: - """ Test group for test_shift species label function """ - attr_names = ['radius', 'gridPoints', 'logIncrement', 'lmax', 'lnonsphr', 's', 'p', 'd', 'f'] - tags = [ - 'mtSphere', 'mtSphere', 'mtSphere', 'atomicCutoffs', 'atomicCutoffs', 'energyParameters', 'energyParameters', - 'energyParameters', 'energyParameters' - ] - - @pytest.mark.parametrize('att_name,tag', zip(attr_names, tags)) - def test_shift_species_label(self, inpxml_etree, att_name, tag): - from aiida_fleur.tools.xml_util import shift_value_species_label, eval_xpath2 - import math - etree = inpxml_etree(TEST_INP_XML_PATH) - path = '/fleurInput/atomSpecies/species[@name = "Fe-1"]/' + tag + '/@' + att_name - old_result = eval_xpath2(etree, path)[0] - - shift_value_species_label(etree, ' 222', att_name, 3, mode='abs') - result = eval_xpath2(etree, path)[0] - - assert math.isclose(float(result) - float(old_result), 3) - - attr_names_float = ['radius', 'logIncrement'] - tags_float = ['mtSphere', 'mtSphere'] - - @pytest.mark.parametrize('att_name,tag', zip(attr_names_float, tags_float)) - def test_shift_species_label_rel(self, inpxml_etree, att_name, tag): - from aiida_fleur.tools.xml_util import shift_value_species_label, eval_xpath2 - import math - etree = inpxml_etree(TEST_INP_XML_PATH) - path = '/fleurInput/atomSpecies/species[@name = "Fe-1"]/' + tag + '/@' + att_name - untouched = '/fleurInput/atomSpecies/species[@name = "Pt-1"]/' + tag + '/@' + att_name - old_result = eval_xpath2(etree, path)[0] - untouched_result = eval_xpath2(etree, untouched)[0] - - shift_value_species_label(etree, ' 222', att_name, 3.2, mode='rel') - result = eval_xpath2(etree, path)[0] - untouched_result_new = eval_xpath2(etree, untouched)[0] - - assert math.isclose(float(result) / float(old_result), 3.2) - assert math.isclose(float(untouched_result_new) / float(untouched_result), 1.0) - - @pytest.mark.parametrize('att_name,tag', zip(attr_names, tags)) - def test_shift_species_label_all(self, inpxml_etree, att_name, tag): - from aiida_fleur.tools.xml_util import shift_value_species_label, eval_xpath2 - import numpy as np - etree = inpxml_etree(TEST_INP_XML_PATH) - path = '/fleurInput/atomSpecies/species/' + tag + '/@' + att_name - old_result = np.array(eval_xpath2(etree, path)).astype('float') - - shift_value_species_label(etree, 'all', att_name, 3, mode='abs') - result = np.array(eval_xpath2(etree, path)).astype('float') - - assert np.all(np.isclose(old_result + 3, result)) - - @pytest.mark.parametrize('att_name,tag', zip(attr_names, tags)) - def test_shift_species_label_all_rel(self, inpxml_etree, att_name, tag): - from aiida_fleur.tools.xml_util import shift_value_species_label, eval_xpath2 - import numpy as np - etree = inpxml_etree(TEST_INP_XML_PATH) - path = '/fleurInput/atomSpecies/species/' + tag + '/@' + att_name - old_result = np.array(eval_xpath2(etree, path)).astype('float') - - shift_value_species_label(etree, 'all', att_name, 2, mode='rel') - result = np.array(eval_xpath2(etree, path)).astype('float') - - assert np.all(np.isclose(old_result * 2, result)) - - -class TestAddNumToAtt: - """ Test group for add_num_to_att function """ - from aiida_fleur.tools.xml_util import get_inpxml_file_structure - xml_structure = get_inpxml_file_structure() - - attr_to_test = list(xml_structure[3]) - attr_to_test.extend(xml_structure[4]) - - @pytest.mark.parametrize('attr_name', attr_to_test) - def test_add_num_to_att(self, inpxml_etree, attr_name): - from aiida_fleur.tools.xml_util import add_num_to_att, eval_xpath2 - etree = inpxml_etree(TEST_INP_XML_PATH) - - path = self.xml_structure[12][attr_name] - result_before = eval_xpath2(etree, path + '/@{}'.format(attr_name)) - - if not result_before: - if attr_name in ['nx', 'ny', 'nz', 'scale']: - pytest.skip('This attribute is not tested for FePt/inp.xml') - raise BaseException('Can not find attribute that should exist in FePt/inp.xml') - else: - result_before = result_before[0] - add_num_to_att(etree, path, attr_name, 333) - result = eval_xpath2(etree, path + '/@{}'.format(attr_name))[0] - - assert float(result) - float(result_before) == 333 - - attr_to_test_float = list(xml_structure[4]) - - @pytest.mark.parametrize('attr_name', attr_to_test_float) - def test_ad_num_to_att_rel(self, inpxml_etree, attr_name): - import math - from aiida_fleur.tools.xml_util import add_num_to_att, eval_xpath2 - etree = inpxml_etree(TEST_INP_XML_PATH) - - path = self.xml_structure[12][attr_name] - result_before = eval_xpath2(etree, path + '/@{}'.format(attr_name)) - - if not result_before: - if attr_name in ['scale']: - pytest.skip('This attribute is not tested for FePt/inp.xml') - raise BaseException('Can not find attribute that should exist in FePt/inp.xml') - else: - result_before = result_before[0] - add_num_to_att(etree, path, attr_name, 1.2442, mode='rel') - result = eval_xpath2(etree, path + '/@{}'.format(attr_name))[0] - - if float(result_before) != 0: - assert math.isclose(float(result) / float(result_before), 1.2442, rel_tol=1e-6) - else: - assert float(result) == 0 - - -# get_xml_attribute -@pytest.mark.skip(reason='Test not implemented') -def test_get_xml_attribute(inpxml_etree): - from aiida_fleur.tools.xml_util import get_xml_attribute - return False - - -# get_inpxml_file_structure -# IMPORTANT: Here we need thats that tell us when the plugin has to be maintained, i.e Know thing in the inp schema where changed -# Is there a way to test for not yet know attributes? i.e if the plugin is complete? Back compatible? -# I.e make for each Fleur schema file, complete inp.xml file version a test if the attributes exists. diff --git a/tests/workflows/test_corehole_workchain.py b/tests/workflows/test_corehole_workchain.py index b1b6454a8..e7d8c7563 100644 --- a/tests/workflows/test_corehole_workchain.py +++ b/tests/workflows/test_corehole_workchain.py @@ -9,7 +9,7 @@ # For further information please visit http://www.flapw.de or # # http://aiida-fleur.readthedocs.io/en/develop/ # ############################################################################### -'''Contains tests for the Fleur_corehole_wc''' +'''Contains tests for the FleurCoreholeWorkChain''' from __future__ import absolute_import from __future__ import print_function @@ -17,16 +17,16 @@ import aiida_fleur import os from aiida.orm import Code, load_node, Dict, StructureData -from aiida_fleur.workflows.corehole import fleur_corehole_wc +from aiida_fleur.workflows.corehole import FleurCoreholeWorkChain from aiida_fleur.workflows.base_fleur import FleurBaseWorkChain from aiida_fleur.workflows.scf import FleurScfWorkChain # tests @pytest.mark.usefixtures('aiida_profile', 'clear_database') -class Test_fleur_corehole_wc(): +class Test_FleurCoreholeWorkChain(): """ - Regression tests for the fleur_corehole_wc + Regression tests for the FleurCoreholeWorkChain """ @pytest.mark.skip(reason='aiida-testing buggy, todo check, aiida-fleur fixture') @@ -37,7 +37,7 @@ def test_fleur_corehole_W( fleur_local_code, generate_structure_W): #, clear_spec): """ - full example using fleur_corehole_wc on W. + full example using FleurCoreholeWorkChain on W. Several fleur runs needed, calculation of all only certain coreholes """ from aiida.engine import run_get_node @@ -98,8 +98,8 @@ def test_fleur_corehole_W( # create process builder to set parameters inputs = { #'metadata' : { - # 'description' : 'Simple fleur_corehole_wc test with W bulk', - # 'label' : 'fleur_corehole_wc_test_W_bulk'}, + # 'description' : 'Simple FleurCoreholeWorkChain test with W bulk', + # 'label' : 'FleurCoreholeWorkChain_test_W_bulk'}, 'options': options, 'fleur': FleurCode, 'inpgen': InpgenCode, @@ -109,8 +109,8 @@ def test_fleur_corehole_W( } # now run calculation - #out, node = run_with_cache(inputs, process_class=fleur_corehole_wc) - out, node = run_get_node(fleur_corehole_wc, **inputs) + #out, node = run_with_cache(inputs, process_class=FleurCoreholeWorkChain) + out, node = run_get_node(FleurCoreholeWorkChain, **inputs) # check general run assert node.is_finished_ok @@ -134,7 +134,7 @@ def test_fleur_corehole_W( @pytest.mark.timeout(500, method='thread') def test_fleur_corehole_structure_Si_one(self, run_with_cache, mock_code_factory): """ - Full regression test of fleur_corehole_wc starting with a crystal structure and parameters, + Full regression test of FleurCoreholeWorkChain starting with a crystal structure and parameters, one corehole """ assert False @@ -143,7 +143,7 @@ def test_fleur_corehole_structure_Si_one(self, run_with_cache, mock_code_factory @pytest.mark.timeout(500, method='thread') def test_fleur_corehole_structure_Si_all(self, run_with_cache, mock_code_factory): """ - Full regression test of fleur_corehole_wc starting from a structure, calculating all possible + Full regression test of FleurCoreholeWorkChain starting from a structure, calculating all possible coreholes """ assert False @@ -152,7 +152,7 @@ def test_fleur_corehole_structure_Si_all(self, run_with_cache, mock_code_factory @pytest.mark.timeout(500, method='thread') def test_fleur_corehole_validation_wrong_inputs(self, run_with_cache, mock_code_factory): """ - Test the validation behavior of fleur_corehole_wc if wrong input is provided it should throw + Test the validation behavior of FleurCoreholeWorkChain if wrong input is provided it should throw an exitcode and not start a Fleur run or crash """ assert False diff --git a/tests/workflows/test_eos_workchain.py b/tests/workflows/test_eos_workchain.py index c79c9a540..75fc10ef7 100644 --- a/tests/workflows/test_eos_workchain.py +++ b/tests/workflows/test_eos_workchain.py @@ -204,3 +204,59 @@ def test_fleur_eos_validation_wrong_inputs(self, run_with_cache, mock_code_facto assert node.is_finished assert not node.is_finished_ok assert node.exit_status == 230 + + +@pytest.mark.usefixtures('aiida_profile', 'clear_database') +def test_birch_murnaghan_fit(): + """Test the birch murnaghan fit in of the eos workchain + + """ + import numpy as np + from aiida_fleur.workflows.eos import birch_murnaghan_fit + + # ignore numerical differences + dezi = 8 + should_vol = round(50.15185277312836, dezi) + should_bulk_mod = round(30.630869193205523, dezi) + should_bulk_deriv = round(-6.120875695109946, dezi) + should_residuals = [round(0.05862235697619352, dezi)] + energies = np.array([-1, -2, -3, -4, -3.2, -2.1, -1]) + base = 50.0 + scales = np.array([0.94, 0.96, 0.98, 1.0, 1.02, 1.04, 1.06]) + volumes = scales * base + volume, bulk_modulus, bulk_deriv, residuals = birch_murnaghan_fit(energies, volumes) + + # print(volume, bulk_modulus, bulk_deriv, residuals) + assert round(volume, dezi) == should_vol + assert round(bulk_modulus, dezi) == should_bulk_mod + assert round(bulk_deriv, dezi) == should_bulk_deriv + assert [round(res, dezi) for res in residuals] == should_residuals + + +@pytest.mark.usefixtures('aiida_profile', 'clear_database') +def test_birch_murnaghan(): + """Test the eval birch_murnaghan + + """ + import numpy as np + from aiida_fleur.workflows.eos import birch_murnaghan + + should_ev = [ + 0.00034115814926603393, 4.2469082027066444e-05, 1.2542478545309484e-06, 0.0, -1.1198946045507376e-06, + -3.386445023825032e-05, -0.00024303823850147386 + ] + should_vp = [ + 3.169784634427714, 2.0773219926762585, 1.019704802686063, 0.0, -0.9797734383823876, -1.9184258091168582, + -2.8154206900859817 + ] + + energies = np.array([-1, -2, -3, -4, -3.2, -2.1, -1]) + base = 50.00 + scales = np.array([0.94, 0.96, 0.98, 1.0, 1.02, 1.04, 1.06]) + volumes = base * scales + bulk_modulus0 = 50 + bulk_deriv0 = 1 + ev, vp = birch_murnaghan(volumes, base, bulk_modulus0, bulk_deriv0) + + assert ev == should_ev + assert vp == should_vp diff --git a/tests/workflows/test_initial_cls_workchain.py b/tests/workflows/test_initial_cls_workchain.py index 15db13e73..2ce44ed65 100644 --- a/tests/workflows/test_initial_cls_workchain.py +++ b/tests/workflows/test_initial_cls_workchain.py @@ -9,7 +9,7 @@ # For further information please visit http://www.flapw.de or # # http://aiida-fleur.readthedocs.io/en/develop/ # ############################################################################### -''' Contains tests for the fleur_initial_cls_wc. ''' +''' Contains tests for the FleurInitialCLSWorkChain. ''' from __future__ import absolute_import from __future__ import print_function @@ -19,22 +19,22 @@ import os from aiida.orm import load_node from aiida.engine import run_get_node -from aiida_fleur.workflows.initial_cls import fleur_initial_cls_wc +from aiida_fleur.workflows.initial_cls import FleurInitialCLSWorkChain # tests @pytest.mark.skip @pytest.mark.usefixtures('aiida_profile', 'clear_database') -class Test_fleur_initial_cls_wc(): +class Test_FleurInitialCLSWorkChain(): """ - Regression tests for the fleur_initial_cls_wc + Regression tests for the FleurInitialCLSWorkChain """ @pytest.mark.timeout(500, method='thread') def test_fleur_initial_cls_W(self, run_with_cache, inpgen_local_code, fleur_local_code, generate_structure_W, export_cache, load_cache, clear_spec): """ - full example using fleur_initial_cls_wc with just elemental W as input + full example using FleurInitialCLSWorkChain with just elemental W as input (W, onw atoms per unit cell) uses the same structure as reference. """ @@ -88,8 +88,8 @@ def test_fleur_initial_cls_W(self, run_with_cache, inpgen_local_code, fleur_loca # create process builder to set parameters inputs = { 'metadata': { - 'description': 'Simple fleur_initial_cls_wc test with W bulk', - 'label': 'fleur_initial_cls_wc_test_W_bulk' + 'description': 'Simple FleurInitialCLSWorkChain test with W bulk', + 'label': 'FleurInitialCLSWorkChain test_W_bulk' }, 'options': Dict(dict=options), 'fleur': FleurCode, @@ -100,7 +100,7 @@ def test_fleur_initial_cls_W(self, run_with_cache, inpgen_local_code, fleur_loca } # now run calculation - out, node = run_with_cache(inputs, process_class=fleur_initial_cls_wc) + out, node = run_with_cache(inputs, process_class=FleurInitialCLSWorkChain) # check general run assert node.is_finished_ok @@ -123,7 +123,7 @@ def test_fleur_initial_cls_W(self, run_with_cache, inpgen_local_code, fleur_loca @pytest.mark.timeout(500, method='thread') def test_fleur_initial_cls_wc_binary_with_given_ref(self, run_with_cache, mock_code_factory): """ - Full regression test of fleur_initial_cls_wc starting with a crystal structure and parameters + Full regression test of FleurInitialCLSWorkChain starting with a crystal structure and parameters """ assert False @@ -131,7 +131,7 @@ def test_fleur_initial_cls_wc_binary_with_given_ref(self, run_with_cache, mock_c @pytest.mark.timeout(500, method='thread') def test_fleur_initial_cls_wc_validation_wrong_inputs(self, run_with_cache, mock_code_factory): """ - Test the validation behavior of fleur_initial_cls_wc if wrong input is provided it should throw + Test the validation behavior of FleurInitialCLSWorkChain if wrong input is provided it should throw an exitcode and not start a Fleur run or crash """ assert False