From c610e48ff251dddff5e43607093feaa044a66184 Mon Sep 17 00:00:00 2001 From: broeder-j Date: Mon, 26 Oct 2020 17:21:16 +0100 Subject: [PATCH 01/53] Add first implemetation of a command line interface(CLI) This is a first layout with first commands implemented including: - data fleurinp list: query db for fleurinp - data fleurinp cat: cat a certain file, usually inp.xml - plot: exposing plot_fleur - data structure import: import structure from inp.xml - data parameter import - workflow res: print all content of all Dict outputs of a process - workflow inputdict: print content of all Dict inputs of a process The base command group is aiida-fleur. bash completion can be done with eval "$(_AIIDA_FLEUR_COMPLETE=source aiida-fleur)" There is yet no documentation, no CI tests, no similiar check as in aiida-core. Futher launch commands for all workchains and calcjob classes should be implemented. --- aiida_fleur/cmdline/__init__.py | 25 ++- aiida_fleur/cmdline/calculations/__init__.py | 25 +++ aiida_fleur/cmdline/calculations/fleur.py | 38 ++++ aiida_fleur/cmdline/calculations/inpgen.py | 39 ++++ aiida_fleur/cmdline/commands/fleurinpdata.py | 76 -------- aiida_fleur/cmdline/data/__init__.py | 26 +++ aiida_fleur/cmdline/data/fleurinp.py | 180 ++++++++++++++++++ aiida_fleur/cmdline/data/parameters.py | 66 +++++++ aiida_fleur/cmdline/data/structure.py | 61 ++++++ .../cmdline/{commands => util}/__init__.py | 0 aiida_fleur/cmdline/util/options.py | 68 +++++++ aiida_fleur/cmdline/visualization/__init__.py | 70 +++++++ aiida_fleur/cmdline/workflows/__init__.py | 150 +++++++++++++++ .../{commands/scf_wc.py => workflows/scf.py} | 35 ++-- .../{commands => workflows}/workchains.py | 0 setup.json | 5 +- 16 files changed, 762 insertions(+), 102 deletions(-) mode change 100644 => 100755 aiida_fleur/cmdline/__init__.py create mode 100755 aiida_fleur/cmdline/calculations/__init__.py create mode 100755 aiida_fleur/cmdline/calculations/fleur.py create mode 100755 aiida_fleur/cmdline/calculations/inpgen.py delete mode 100644 aiida_fleur/cmdline/commands/fleurinpdata.py create mode 100755 aiida_fleur/cmdline/data/__init__.py create mode 100755 aiida_fleur/cmdline/data/fleurinp.py create mode 100644 aiida_fleur/cmdline/data/parameters.py create mode 100755 aiida_fleur/cmdline/data/structure.py rename aiida_fleur/cmdline/{commands => util}/__init__.py (100%) mode change 100644 => 100755 create mode 100755 aiida_fleur/cmdline/util/options.py create mode 100755 aiida_fleur/cmdline/visualization/__init__.py create mode 100755 aiida_fleur/cmdline/workflows/__init__.py rename aiida_fleur/cmdline/{commands/scf_wc.py => workflows/scf.py} (67%) mode change 100644 => 100755 rename aiida_fleur/cmdline/{commands => workflows}/workchains.py (100%) mode change 100644 => 100755 diff --git a/aiida_fleur/cmdline/__init__.py b/aiida_fleur/cmdline/__init__.py old mode 100644 new mode 100755 index 6e91f601d..4f7c3dc1b --- a/aiida_fleur/cmdline/__init__.py +++ b/aiida_fleur/cmdline/__init__.py @@ -10,5 +10,28 @@ # http://aiida-fleur.readthedocs.io/en/develop/ # ############################################################################### ''' -AiiDA-FLEUR +Module for the command line interface of AiiDA-FLEUR ''' +import click +import click_completion + +from aiida.cmdline.params import options, types + +# Activate the completion of parameter types provided by the click_completion package +click_completion.init() + +# Instead of using entrypoints and directly injecting verdi commands into aiida-core +# we created our own separete CLI because verdi will prob change and become +# less material science specific + + +@click.group('aiida-fleur', context_settings={'help_option_names': ['-h', '--help']}) +@options.PROFILE(type=types.ProfileParamType(load_profile=True)) +def cmd_root(profile): # pylint: disable=unused-argument + """CLI for the `aiida-fleur` plugin.""" + + +from .calculations import cmd_calcjob +from .data import cmd_structure, cmd_fleurinp +from .workflows import cmd_workflow +from .visualization import cmd_plot diff --git a/aiida_fleur/cmdline/calculations/__init__.py b/aiida_fleur/cmdline/calculations/__init__.py new file mode 100755 index 000000000..f809d3c12 --- /dev/null +++ b/aiida_fleur/cmdline/calculations/__init__.py @@ -0,0 +1,25 @@ +# -*- 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/ # +############################################################################### +''' +Module with CLI commands for calcjob types of aiida-fleur. +''' +from .. import cmd_root + + +@cmd_root.group('calcjob') +def cmd_calcjob(): + """Commands to launch and inspect calcjobs of aiida-fleur.""" + + +# Import the sub commands to register them with the CLI +from .fleur import cmd_fleur +from .inpgen import cmd_inpgen diff --git a/aiida_fleur/cmdline/calculations/fleur.py b/aiida_fleur/cmdline/calculations/fleur.py new file mode 100755 index 000000000..a4710eb8c --- /dev/null +++ b/aiida_fleur/cmdline/calculations/fleur.py @@ -0,0 +1,38 @@ +# -*- 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/ # +############################################################################### +''' +Module with CLI commands for fleur calcs. +''' +import click +from . import cmd_calcjob + + +@cmd_calcjob.group('fleur') +def cmd_fleur(): + """Commands to handle `fleur` calcs.""" + + +@cmd_fleur.command('list') +def list_fleur(): + """ + List Fleur calc in the database with information + """ + click.echo('Not implemented yet, sorry. Please implement me!') + # do a query and list all reuse AiiDA code + + +@cmd_fleur.command('launch') +def launch_fleur(): + """ + Launch an fleur process + """ + click.echo('Not implemented yet, sorry. Please implement me!') diff --git a/aiida_fleur/cmdline/calculations/inpgen.py b/aiida_fleur/cmdline/calculations/inpgen.py new file mode 100755 index 000000000..af6434bda --- /dev/null +++ b/aiida_fleur/cmdline/calculations/inpgen.py @@ -0,0 +1,39 @@ +# -*- 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/ # +############################################################################### +''' +Module with CLI commands for inpgen calc. +''' +import click + +from . import cmd_calcjob + + +@cmd_calcjob.group('inpgen') +def cmd_inpgen(): + """Commands to handle `inpgen` calcs.""" + + +@cmd_inpgen.command('list') +def list_inpgen(): + """ + List FleurinpData in the database with information + """ + click.echo('Not implemented yet, sorry. Please implement me!') + # do a query and list all reuse AiiDA code + + +@cmd_inpgen.command('launch') +def launch_inpgen(): + """ + Launch an inpgen process + """ + click.echo('Not implemented yet, sorry. Please implement me!') diff --git a/aiida_fleur/cmdline/commands/fleurinpdata.py b/aiida_fleur/cmdline/commands/fleurinpdata.py deleted file mode 100644 index 4ac092fb0..000000000 --- a/aiida_fleur/cmdline/commands/fleurinpdata.py +++ /dev/null @@ -1,76 +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/ # -############################################################################### -""" -Contains verdi commands for fleurinpdata -""" -from __future__ import absolute_import -import click - - -# this will get replaced by data_plug.group -#@data_plug.group('fleur.fleurinp') -@click.group() -def fleurinp(): - pass - - -@fleurinp.command() -def list_fleurinp(): - """ - list all Fleurinp data in the database and displays some information - """ - click.echo('verdi data fleurinp list') - #do a query and list all reuse AiiDA code - - -@fleurinp.command() -def show(): - """ - Shows some basic information about the fleurinp datastructure and dumbs the - inp.xml - """ - click.echo('verdi data fleurinp list') - - -@fleurinp.command() -def open_inp(): - """ - opens the inp.xml in some editor, readonly. - inp.xml - """ - click.echo('verdi data fleurinp list') - - -# this is a maybe -@fleurinp.command() -def get_structure(): - """ - Prints some basic information about the structure data and return a structure uuid/pk - """ - click.echo('verdi data fleurinp list') - - -@fleurinp.command() -def get_kpoints(): - """ - Prints some basic information about the kpoints data and returns a kpoints uuid/pk - """ - click.echo('verdi data fleurinp kpoints') - - -@fleurinp.command() -def get_parameters(): - """ - Prints some basic information about the parameter data and returns a - parameter data uuid/pk - """ - click.echo('verdi data fleurinp parameter') diff --git a/aiida_fleur/cmdline/data/__init__.py b/aiida_fleur/cmdline/data/__init__.py new file mode 100755 index 000000000..5840bcd10 --- /dev/null +++ b/aiida_fleur/cmdline/data/__init__.py @@ -0,0 +1,26 @@ +# -*- 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/ # +############################################################################### +""" +Module with CLI commands for various data types. +""" +from .. import cmd_root + + +@cmd_root.group('data') +def cmd_data(): + """Commands to create and inspect data nodes.""" + + +# Import the sub commands to register them with the CLI +from .structure import cmd_structure +from .parameters import cmd_parameter +from .fleurinp import cmd_fleurinp diff --git a/aiida_fleur/cmdline/data/fleurinp.py b/aiida_fleur/cmdline/data/fleurinp.py new file mode 100755 index 000000000..1ab93e3f4 --- /dev/null +++ b/aiida_fleur/cmdline/data/fleurinp.py @@ -0,0 +1,180 @@ +# -*- 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/ # +############################################################################### +""" +Contains verdi commands for fleurinpdata +""" +from __future__ import absolute_import +import click +from aiida.cmdline.commands.cmd_data.cmd_list import query, list_options +from aiida.cmdline.params import arguments, options, types +from aiida.cmdline.utils import decorators, echo +from aiida.cmdline.params.types import DataParamType + +from aiida_fleur.data.fleurinp import FleurinpData +from . import cmd_data + + +@cmd_data.group('fleurinp') +def cmd_fleurinp(): + """Commands to handle `FleurinpData` nodes.""" + + +@cmd_fleurinp.command('list') +@list_options # usual aiida list options +@click.option('--uuid/--no-uuid', default=False, show_default=True, help='Display uuid of nodes.') +@click.option('--ctime/--no-ctime', default=False, show_default=True, help='Display ctime of nodes.') +@click.option('--extras/--no-extras', default=True, show_default=True, help='Display extras of nodes.') +@click.option('--strucinfo/--no-strucinfo', + default=False, + show_default=True, + help='Perpare additional information on the crystal structure to show. This slows down the query.') +@decorators.with_dbenv() +def list_fleurinp(raw, past_days, groups, all_users, strucinfo, uuid, ctime, extras): + """ + List stored FleurinpData in the database with additional information + """ + # do a query and list all reuse AiiDA code + from tabulate import tabulate + list_project_headers = ['Id', 'Label', 'Description', 'Files'] # these we always get + # 'UUID', 'Ctime', + columns_dict = { + 'ID': 'id', + 'Id': 'id', + 'UUID': 'uuid', + 'Ctime': 'ctime', + 'Label': 'label', + 'Description': 'description', + 'Files': 'attributes.files', + 'Extras': 'attributes.extras' + } + + if uuid: + list_project_headers.append('UUID') + if ctime: + list_project_headers.append('Ctime') + if extras: + list_project_headers.append('Extras') + + project = [columns_dict[k] for k in list_project_headers] + group_pks = None + if groups is not None: + group_pks = [g.pk for g in groups] + + data_fleurinp = query(FleurinpData, project, past_days, group_pks, all_users) + if strucinfo: # second query + # we get the whole node to get some extra information + project2 = '*' + fleurinps = query(FleurinpData, project2, past_days, group_pks, all_users) + list_project_headers.append('Formula') + counter = 0 + fleurinp_list_data = list() + + # , 'Formula', 'Symmetry' + # It is fastest for list commands to only display content from a query + if not raw: + fleurinp_list_data.append(list_project_headers) + for j, entry in enumerate(data_fleurinp): + #print(entry) + for i, value in enumerate(entry): + if isinstance(value, list): + new_entry = list() + for elm in value: + if elm is None: + new_entry.append('') + else: + new_entry.append(elm) + entry[i] = ','.join(new_entry) + if strucinfo: + structure = fleurinps[j][0].get_structuredata_ncf() + formula = structure.get_formula() + entry.append(formula) + for i in range(len(entry), len(list_project_headers)): + entry.append(None) + counter += 1 + fleurinp_list_data.extend(data_fleurinp) + if raw: + echo.echo(tabulate(fleurinp_list_data, tablefmt='plain')) + else: + echo.echo(tabulate(fleurinp_list_data, headers='firstrow')) + echo.echo('\nTotal results: {}\n'.format(counter)) + + +@cmd_fleurinp.command('cat') +@arguments.NODE('node', type=DataParamType(sub_classes=('aiida.data:fleur.fleurinp',))) +@click.option('-f', + '--filename', + 'filename', + default='inp.xml', + show_default=True, + help='Disply the file content of the given filename.') +def cat_file(node, filename): + """ + Dumb the content of a file contained in given fleurinpdata, per default dump + inp.xml + """ + #if filename is None: + # filename = 'inp.xml' + echo.echo(node.get_content(filename=filename)) + + #click.echo('Not implemented yet, sorry. Please implement me!') + + +@cmd_fleurinp.command('info') +def info(): + """ + Shows some basic information about the fleurinp datastructure and dumbs the + inp.xml + """ + click.echo('Not implemented yet, sorry. Please implement me!') + + +@cmd_fleurinp.command('show') +def cmd_show(): + """ + Shows the content of a certain file + """ + click.echo('Not implemented yet, sorry. Please implement me!') + + +@cmd_fleurinp.command('open') +def open_inp(): + """ + opens the inp.xml in some editor, readonly. + inp.xml this way looking at xml might be more convenient. + """ + click.echo('Not implemented yet, sorry. Please implement me!') + + +# this is a maybe +@cmd_fleurinp.command() +def get_structure(): + """ + Prints some basic information about the structure data and return a structure uuid/pk + """ + click.echo('Not implemented yet, sorry. Please implement me!') + + +@cmd_fleurinp.command() +def get_kpoints(): + """ + Prints some basic information about the kpoints data and returns a kpoints uuid/pk + """ + click.echo('Not implemented yet, sorry. Please implement me!') + + +@cmd_fleurinp.command() +def get_parameters(): + """ + Prints some basic information about the parameter data and returns a + parameter data uuid/pk + """ + click.echo('Not implemented yet, sorry. Please implement me!') diff --git a/aiida_fleur/cmdline/data/parameters.py b/aiida_fleur/cmdline/data/parameters.py new file mode 100644 index 000000000..6c2753f13 --- /dev/null +++ b/aiida_fleur/cmdline/data/parameters.py @@ -0,0 +1,66 @@ +# -*- 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/ # +############################################################################### +# pylint: disable=cyclic-import +"""Command line utilities to create and inspect `Dict` nodes with FLAPW parameters.""" +import click + +from aiida.cmdline.params import options +from aiida.cmdline.utils import decorators, echo + +from . import cmd_data + + +@cmd_data.group('parameter') +def cmd_parameter(): + """Commands to create and inspect `Dict` nodes containing FLAPW parameters ('calc_parameters').""" + + +@cmd_parameter.command('import') +@click.argument('filename', type=click.Path(exists=True)) +@click.option('--fleurinp/--no-fleurinp', + default=False, + 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() +@decorators.with_dbenv() +def cmd_param_import(filename, dry_run, fleurinp, show): + """ + Extract FLAPW parameters from a Fleur input file and store as Dict in the db. + + FILENAME is the name/path of the inp.xml file to use. + """ + from aiida_fleur.data.fleurinp import FleurinpData + + if not filename.endswith('.xml'): + echo.echo_failure('Error: Currently, we can only extract information from an inp.xml file.') + fleurinpd = FleurinpData(files=[filename]) + if not fleurinp or dry_run: + parameters = fleurinpd.get_parameterdata_ncf() + else: + parameters = fleurinpd.get_parameterdata(fleurinpd) + + if dry_run: + echo.echo_success('parsed FLAPW parameters') + else: + parameters.store() + echo.echo_success(f'parsed and stored FLAPW parameters<{parameters.pk}> <{parameters.uuid}>') + + if show: + echo.echo_dictionary(parameters.get_dict()) + + +# further ideas: +# query for certain FLAPW parameter nodes. +# Example show me all for Si +# query for options nodes +# command to split and merge parameters nodes together based on elements. diff --git a/aiida_fleur/cmdline/data/structure.py b/aiida_fleur/cmdline/data/structure.py new file mode 100755 index 000000000..5e4a179d5 --- /dev/null +++ b/aiida_fleur/cmdline/data/structure.py @@ -0,0 +1,61 @@ +# -*- 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/ # +############################################################################### +"""Command line utilities to create and inspect `StructureData` nodes.""" +import click + +from aiida.cmdline.params import options +from aiida.cmdline.utils import decorators, echo + +from . import cmd_data + + +@cmd_data.group('structure') +def cmd_structure(): + """Commands to create and inspect `StructureData` nodes.""" + + +# import filename +# import -N pk_fleurinp + + +@cmd_structure.command('import') +@click.argument('filename', type=click.Path(exists=True)) +@click.option('--fleurinp/--no-fleurinp', + default=False, + show_default=True, + help='Store also the fleurinp and the extractor calcfunction in the db.') +@options.DRY_RUN() +@decorators.with_dbenv() +def cmd_import(filename, dry_run, fleurinp): + """ + Import a `StructureData` from a Fleur input file. + + FILENAME is the name/path of the inp.xml file to use. + """ + from aiida_fleur.data.fleurinp import FleurinpData + + if not filename.endswith('.xml'): + echo.echo_failure('Error: Currently, only StructureData from a inp.xml file can be extracted.') + fleurinpd = FleurinpData(files=[filename]) + if not fleurinp or dry_run: + structure = fleurinpd.get_structuredata_ncf() + else: + structure = fleurinpd.get_structuredata() + formula = structure.get_formula() + + if dry_run: + echo.echo_success(f'parsed structure with formula {formula}') + else: + structure.store() + echo.echo_success( + f'parsed and stored StructureData<{structure.pk}> with formula {formula}, also stored FleurinpData<{fleurinpd.pk}>' + ) diff --git a/aiida_fleur/cmdline/commands/__init__.py b/aiida_fleur/cmdline/util/__init__.py old mode 100644 new mode 100755 similarity index 100% rename from aiida_fleur/cmdline/commands/__init__.py rename to aiida_fleur/cmdline/util/__init__.py diff --git a/aiida_fleur/cmdline/util/options.py b/aiida_fleur/cmdline/util/options.py new file mode 100755 index 000000000..409304a69 --- /dev/null +++ b/aiida_fleur/cmdline/util/options.py @@ -0,0 +1,68 @@ +# -*- 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/ # +############################################################################### +""" +Options commonly used throughout the aiida-fleur command line interface. +To standardize at least of the options, they are kept as close as possible to +aiida-core and aiida-quantumespresso +""" +import click +from aiida.cmdline.params import types +from aiida.cmdline.params.options import OverridableOption + +STRUCTURE = OverridableOption('-s', + '--structure', + type=types.DataParamType(sub_classes=('aiida.data:structure',)), + help='StructureData node.') + +MAX_NUM_MACHINES = OverridableOption('-m', + '--max-num-machines', + type=click.INT, + default=1, + show_default=True, + help='The maximum number of machines (nodes) to use for the calculations.') + +MAX_WALLCLOCK_SECONDS = OverridableOption('-w', + '--max-wallclock-seconds', + type=click.INT, + default=1800, + show_default=True, + help='the maximum wallclock time in seconds to set for the calculations.') + +WITH_MPI = OverridableOption('-i', + '--with-mpi', + is_flag=True, + default=False, + show_default=True, + help='Run the calculations with MPI enabled.') + +PARENT_FOLDER = OverridableOption('-P', + '--parent-folder', + 'parent_folder', + type=types.DataParamType(sub_classes=('aiida.data:remote',)), + show_default=True, + required=False, + help='The PK of a parent remote folder (for restarts).') + +DAEMON = OverridableOption('-d', + '--daemon', + is_flag=True, + default=False, + show_default=True, + help='Submit the process to the daemon instead of running it locally.') + +CLEAN_WORKDIR = OverridableOption( + '-x', + '--clean-workdir', + is_flag=True, + default=False, + show_default=True, + help='Clean the remote folder of all the launched calculations after completion of the workchain.') diff --git a/aiida_fleur/cmdline/visualization/__init__.py b/aiida_fleur/cmdline/visualization/__init__.py new file mode 100755 index 000000000..19df9087c --- /dev/null +++ b/aiida_fleur/cmdline/visualization/__init__.py @@ -0,0 +1,70 @@ +# -*- 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/ # +############################################################################### +# pylint: disable=cyclic-import +# ,reimported,unused-import,wrong-import-position +""" +Module with CLI commands for various visualizations of data types. +""" +import click +from aiida.cmdline.utils import decorators +from aiida.cmdline.params import arguments +from .. import cmd_root + + +@cmd_root.command('plot') +@arguments.NODES('nodes') +# help='The pks for the nodes to be parsed to plot_fleur') +# type=click.Path(exists=True)) +@click.option('-f', 'filename', type=click.File('r'), default=None) +@click.option('--save', + type=click.BOOL, + default=False, + show_default=True, + help='Should the result of plot_fleur be saved to a files.') +@click.option('--show/--no-show', default=True, show_default=True, help='Show the output of plot_fleur.') +@click.option('--show_dict/--no-show_dict', default=False, show_default=True, help='Show the output of plot_fleur.') +@click.option('--bokeh', 'backend', flag_value='bokeh') +@click.option('--matplotlib', 'backend', flag_value='matplotlib', default=True) +#@decorators.with_dbenv() +def cmd_plot(nodes, filename, save, show_dict, backend, show): + """ + Invoke the plot_fleur command on given nodes + """ + if backend != 'bokeh': + # Try to set a working GUI backend for matplotlib + # normally we assume to be on ipython which is not good. + # The order is arbitrary. + import matplotlib + gui_env = [ + 'WebAgg', 'GTK3Agg', 'GTK3Cairo', 'MacOSX', 'Qt4Agg', 'Qt4Cairo', 'Qt5Agg', 'Qt5Cairo', 'TkAgg', 'TkCairo', + 'WX', 'WXAgg', 'WXCairo', 'nbAgg', 'agg', 'cairo', 'pdf', 'pgf', 'ps', 'svg' + ] + for gui in gui_env: + try: + print('testing', gui) + matplotlib.use(gui, force=True) + from matplotlib import pyplot as plt + break + except ImportError as ex: + print(ex) + continue + print('Using:', matplotlib.get_backend()) + + from aiida_fleur.tools.plot.fleur import plot_fleur + + nodes = list(nodes) + nodesf = [] + if filename is not None: + nodesf = filename.read().split() + filename.close() + nodes = nodes + nodesf + p = plot_fleur(nodes, save=save, show_dict=show_dict, backend=backend, show=show) diff --git a/aiida_fleur/cmdline/workflows/__init__.py b/aiida_fleur/cmdline/workflows/__init__.py new file mode 100755 index 000000000..a6ad84846 --- /dev/null +++ b/aiida_fleur/cmdline/workflows/__init__.py @@ -0,0 +1,150 @@ +# -*- 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/ # +############################################################################### +""" +Module with CLI commands to launch and inspect various aiida-fleur workchains. +""" +import click +from .. import cmd_root +from aiida.cmdline.params.types import ProcessParamType +from aiida.cmdline.params import arguments, options +from aiida.cmdline.utils import decorators, echo + + +@cmd_root.group('workflow') +def cmd_workflow(): + """Commands to launch and inspect various workchains.""" + + +# Import the sub commands to register them with the CLI +from .scf import cmd_scf +#from .relax import cmd_relax +#from .banddos import cmd_banddos +#from .eos import cmd_eos +#from .corehole import cmd_corehole +#from .initial_cls import cmd_initial_cls + +#from .dmi import cmd_dmi +#from .mae import cmd_mae +#from .ssdisp import cmd_ssdisp + +# general further commands for fleur workchains + + +@cmd_workflow.command('res') +@arguments.PROCESS('process', type=ProcessParamType() + ) #, type=WorkflowParamType(sub_classes=('aiida.node:process.workflow.workchain',))) +@click.option('--info/--no-info', default=False, help='Print an info header above each node.') +@click.option('-l', '--label', 'label', type=str, help='Print only output dicts with a certain link_label.') +@options.DICT_KEYS() +@options.DICT_FORMAT() +@decorators.with_dbenv() +def workchain_res(process, fmt, keys, label, info): + """Print data from Dict nodes returned or created by any fleur process.""" + #from aiida.cmdline.utils.echo import echo_dictionary + from aiida.orm import Dict + + returned_dicts_info = [] + returned_dicts = [] + try: + results = process.get_outgoing().all() + except ValueError as exception: + echo.echo_critical(str(exception)) + for result in results: + if isinstance(result.node, Dict): + if label is not None: + if label == result.link_label: + returned_dicts.append(result.node.get_dict()) + returned_dicts_info.append(result) + else: + returned_dicts.append(result.node.get_dict()) + returned_dicts_info.append(result) + + for i, re_dict in enumerate(returned_dicts): + if keys is not None: + try: + result = {k: re_dict[k] for k in keys} + except KeyError as exc: + echo.echo_critical("key '{}' was not found in the results dictionary".format(exc.args[0])) + else: + result = re_dict + if info: + echo.echo('# Info: {} {} dict:'.format(returned_dicts_info[i].link_label, returned_dicts_info[i].node)) + echo.echo_dictionary(result, fmt=fmt) + + +@cmd_workflow.command('inputdict') +@arguments.PROCESS('process', type=ProcessParamType() + ) #, type=WorkflowParamType(sub_classes=('aiida.node:process.workflow.workchain',))) +@click.option('--info/--no-info', default=False, help='Print an info header above each node.') +@click.option('-l', '--label', 'label', type=str, help='Print only output dicts with a certain link_label.') +@options.DICT_KEYS() +@options.DICT_FORMAT() +@decorators.with_dbenv() +def workchain_inputdict(process, fmt, keys, label, info): + """Print data from Dict nodes inputed into any fleur process.""" + #from aiida.cmdline.utils.echo import echo_dictionary + from aiida.orm import Dict + + returned_dicts_info = [] + returned_dicts = [] + try: + results = process.get_incoming().all() + except ValueError as exception: + echo.echo_critical(str(exception)) + for result in results: + if isinstance(result.node, Dict): + if label is not None: + if label == result.link_label: + returned_dicts.append(result.node.get_dict()) + returned_dicts_info.append(result) + else: + returned_dicts.append(result.node.get_dict()) + returned_dicts_info.append(result) + + for i, re_dict in enumerate(returned_dicts): + if keys is not None: + try: + result = {k: re_dict[k] for k in keys} + except KeyError as exc: + echo.echo_critical("key '{}' was not found in the results dictionary".format(exc.args[0])) + else: + result = re_dict + if info: + echo.echo('# Info: {} {} dict:'.format(returned_dicts_info[i].link_label, returned_dicts_info[i].node)) + echo.echo_dictionary(result, fmt=fmt) + + +''' +@cmd_workflow.command('inputls') +def inputls_wc(): + """ + Prints verdi node show for all workchain inputs. + """ + click.echo('verdi aiida-fleur scf res') + +@cmd_workflow.command('show') +def show_wc(): + """ + Shows the node ans structure of a workchain. + Similar to verdi node show + """ + click.echo('verdi aiida-fleur scf show') + + +@cmd_workflow.command('list') +def list_wc(): + """ + Similar to the verdi process list command, but this has preset filters and + displays also some specific information about fleur workchains + """ + click.echo('verdi aiida-fleur scf list') +''' diff --git a/aiida_fleur/cmdline/commands/scf_wc.py b/aiida_fleur/cmdline/workflows/scf.py old mode 100644 new mode 100755 similarity index 67% rename from aiida_fleur/cmdline/commands/scf_wc.py rename to aiida_fleur/cmdline/workflows/scf.py index e7d76c829..2295b48f5 --- a/aiida_fleur/cmdline/commands/scf_wc.py +++ b/aiida_fleur/cmdline/workflows/scf.py @@ -9,40 +9,27 @@ # For further information please visit http://www.flapw.de or # # http://aiida-fleur.readthedocs.io/en/develop/ # ############################################################################### +# pylint: disable=cyclic-import +# ,reimported,unused-import,wrong-import-position """ -contains verdi commands for the scf workchain +Contains verdi commands for the scf workchain in general these should become options of verdi aiida-fleur workchains """ from __future__ import absolute_import import click +from . import cmd_workflow -@click.group() -def scf_wc(): - pass +@cmd_workflow.group('scf') +def cmd_scf(): + """Commands to launch and inspect scf workchains.""" -@scf_wc.command() -def res_scf(): - """ - Prints the result node to screen - """ - click.echo('verdi aiida-fleur scf res') - -@scf_wc.command() -def show_scf(): - """ - plots the results of a +@cmd_scf.command('launch') +def launch_scf(): """ - click.echo('verdi aiida-fleur scf show') - - -@scf_wc.command() -def list_scf(): - """ - similar to the verdi work list command, but this displays also some - specific information about the scfs + Prints the result node to screen """ - click.echo('verdi aiida-fleur scf list') + click.echo('Not implemented yet, sorry. Please implement me!') diff --git a/aiida_fleur/cmdline/commands/workchains.py b/aiida_fleur/cmdline/workflows/workchains.py old mode 100644 new mode 100755 similarity index 100% rename from aiida_fleur/cmdline/commands/workchains.py rename to aiida_fleur/cmdline/workflows/workchains.py diff --git a/setup.json b/setup.json index 0ba464a90..9be88df3d 100644 --- a/setup.json +++ b/setup.json @@ -32,7 +32,7 @@ "pytest-cov >= 2.5.0", "numpy>=1.16.4,<1.18.0", "sympy", - "masci-tools==0.3.12-dev4", + "masci-tools>=0.3.12-dev4", "future", "ase", "pymatgen", @@ -89,6 +89,9 @@ "fleur.create_magnetic = aiida_fleur.workflows.create_magnetic_film:FleurCreateMagneticWorkChain", "fleur.base_relax = aiida_fleur.workflows.base_relax:FleurBaseRelaxWorkChain", "fleur.base = aiida_fleur.workflows.base_fleur:FleurBaseWorkChain" + ], + "console_scripts": [ + "aiida-fleur = aiida_fleur.cmdline:cmd_root" ] } } From 5c6c53d299e683c8b38ff146d84614fdc6c9802f Mon Sep 17 00:00:00 2001 From: broeder-j Date: Mon, 2 Nov 2020 08:53:20 +0100 Subject: [PATCH 02/53] Make PyPi show README.md and loose masci-tools dep --- setup.json | 4 ++-- setup.py | 11 +++++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/setup.json b/setup.json index 85750bc6a..b069a2877 100644 --- a/setup.json +++ b/setup.json @@ -30,9 +30,9 @@ "aiida-core>=1.3.0,<2.0.0", "lxml >= 3.6.4", "pytest-cov >= 2.5.0", - "numpy>=1.16.4,<1.18.0", + "numpy>=1.16.4,<1.20.0", "sympy", - "masci-tools==0.3.12-dev4", + "masci-tools>=0.3.12-dev4", "future", "ase", "pymatgen", diff --git a/setup.py b/setup.py index 73ebd7f5a..d02ae26aa 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from __future__ import absolute_import from setuptools import setup, find_packages import json @@ -10,4 +9,12 @@ # such that it can be discovered automatically with open('setup.json', 'r') as info: kwargs = json.load(info) - setup(packages=find_packages(exclude='aiida'), **kwargs) + setup( + packages=find_packages(exclude=['tests*']), + # this doesn't work when placed in setup.json (something to do with str type) + package_data={ + '': ['*'], + }, + long_description=open('README.md').read(), + long_description_content_type='text/markdown', + **kwargs) From b6d6433cef972f1a10c75765d0d3b427ae512b87 Mon Sep 17 00:00:00 2001 From: broeder-j Date: Mon, 16 Nov 2020 11:51:31 +0100 Subject: [PATCH 03/53] Introduce cli launch command First implementation of launch command for various workchains. Has still to be made robust. This commit also includes fixes by precommit, a dict util helper to remove keys with None values from dicts. For the CLI options are introduced, also one option representing a structure, which can be given as a node a are file of some format parsable by ase. --- aiida_fleur/cmdline/__init__.py | 2 +- aiida_fleur/cmdline/calculations/fleur.py | 38 -- aiida_fleur/cmdline/calculations/inpgen.py | 39 -- aiida_fleur/cmdline/data/fleurinp.py | 5 +- aiida_fleur/cmdline/data/structure.py | 3 + .../{workflows/scf.py => launch/__init__.py} | 28 +- aiida_fleur/cmdline/launch/launch.py | 386 ++++++++++++++++++ .../{calculations => list}/__init__.py | 9 +- aiida_fleur/cmdline/util/defaults.py | 91 +++++ aiida_fleur/cmdline/util/options.py | 93 ++++- aiida_fleur/cmdline/util/types.py | 64 +++ aiida_fleur/cmdline/util/utils.py | 65 +++ aiida_fleur/cmdline/workflows/__init__.py | 4 +- aiida_fleur/cmdline/workflows/workchains.py | 48 --- aiida_fleur/tools/dict_util.py | 19 + aiida_fleur/workflows/mae_conv.py | 2 +- aiida_fleur/workflows/ssdisp_conv.py | 2 +- tests/tools/test_dict_util.py | 10 + 18 files changed, 740 insertions(+), 168 deletions(-) delete mode 100755 aiida_fleur/cmdline/calculations/fleur.py delete mode 100755 aiida_fleur/cmdline/calculations/inpgen.py rename aiida_fleur/cmdline/{workflows/scf.py => launch/__init__.py} (60%) mode change 100755 => 100644 create mode 100755 aiida_fleur/cmdline/launch/launch.py rename aiida_fleur/cmdline/{calculations => list}/__init__.py (81%) create mode 100644 aiida_fleur/cmdline/util/defaults.py create mode 100644 aiida_fleur/cmdline/util/types.py create mode 100644 aiida_fleur/cmdline/util/utils.py delete mode 100755 aiida_fleur/cmdline/workflows/workchains.py diff --git a/aiida_fleur/cmdline/__init__.py b/aiida_fleur/cmdline/__init__.py index 4f7c3dc1b..a1073f871 100755 --- a/aiida_fleur/cmdline/__init__.py +++ b/aiida_fleur/cmdline/__init__.py @@ -31,7 +31,7 @@ def cmd_root(profile): # pylint: disable=unused-argument """CLI for the `aiida-fleur` plugin.""" -from .calculations import cmd_calcjob +from .launch import cmd_launch from .data import cmd_structure, cmd_fleurinp from .workflows import cmd_workflow from .visualization import cmd_plot diff --git a/aiida_fleur/cmdline/calculations/fleur.py b/aiida_fleur/cmdline/calculations/fleur.py deleted file mode 100755 index a4710eb8c..000000000 --- a/aiida_fleur/cmdline/calculations/fleur.py +++ /dev/null @@ -1,38 +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/ # -############################################################################### -''' -Module with CLI commands for fleur calcs. -''' -import click -from . import cmd_calcjob - - -@cmd_calcjob.group('fleur') -def cmd_fleur(): - """Commands to handle `fleur` calcs.""" - - -@cmd_fleur.command('list') -def list_fleur(): - """ - List Fleur calc in the database with information - """ - click.echo('Not implemented yet, sorry. Please implement me!') - # do a query and list all reuse AiiDA code - - -@cmd_fleur.command('launch') -def launch_fleur(): - """ - Launch an fleur process - """ - click.echo('Not implemented yet, sorry. Please implement me!') diff --git a/aiida_fleur/cmdline/calculations/inpgen.py b/aiida_fleur/cmdline/calculations/inpgen.py deleted file mode 100755 index af6434bda..000000000 --- a/aiida_fleur/cmdline/calculations/inpgen.py +++ /dev/null @@ -1,39 +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/ # -############################################################################### -''' -Module with CLI commands for inpgen calc. -''' -import click - -from . import cmd_calcjob - - -@cmd_calcjob.group('inpgen') -def cmd_inpgen(): - """Commands to handle `inpgen` calcs.""" - - -@cmd_inpgen.command('list') -def list_inpgen(): - """ - List FleurinpData in the database with information - """ - click.echo('Not implemented yet, sorry. Please implement me!') - # do a query and list all reuse AiiDA code - - -@cmd_inpgen.command('launch') -def launch_inpgen(): - """ - Launch an inpgen process - """ - click.echo('Not implemented yet, sorry. Please implement me!') diff --git a/aiida_fleur/cmdline/data/fleurinp.py b/aiida_fleur/cmdline/data/fleurinp.py index 1ab93e3f4..ef339e82c 100755 --- a/aiida_fleur/cmdline/data/fleurinp.py +++ b/aiida_fleur/cmdline/data/fleurinp.py @@ -18,9 +18,10 @@ from aiida.cmdline.params import arguments, options, types from aiida.cmdline.utils import decorators, echo from aiida.cmdline.params.types import DataParamType - -from aiida_fleur.data.fleurinp import FleurinpData +from aiida.plugins import DataFactory +#from aiida_fleur.data.fleurinp import FleurinpData from . import cmd_data +FleurinpData = DataFactory('fleur.fleurinp') @cmd_data.group('fleurinp') diff --git a/aiida_fleur/cmdline/data/structure.py b/aiida_fleur/cmdline/data/structure.py index 5e4a179d5..c13e9eeab 100755 --- a/aiida_fleur/cmdline/data/structure.py +++ b/aiida_fleur/cmdline/data/structure.py @@ -40,6 +40,9 @@ def cmd_import(filename, dry_run, fleurinp): Import a `StructureData` from a Fleur input file. FILENAME is the name/path of the inp.xml file to use. + + If you want to import a structure from any file type you can use + 'verdi data structure import -ase ' instead. """ from aiida_fleur.data.fleurinp import FleurinpData diff --git a/aiida_fleur/cmdline/workflows/scf.py b/aiida_fleur/cmdline/launch/__init__.py old mode 100755 new mode 100644 similarity index 60% rename from aiida_fleur/cmdline/workflows/scf.py rename to aiida_fleur/cmdline/launch/__init__.py index 2295b48f5..740e86821 --- a/aiida_fleur/cmdline/workflows/scf.py +++ b/aiida_fleur/cmdline/launch/__init__.py @@ -9,27 +9,15 @@ # For further information please visit http://www.flapw.de or # # http://aiida-fleur.readthedocs.io/en/develop/ # ############################################################################### -# pylint: disable=cyclic-import -# ,reimported,unused-import,wrong-import-position -""" -Contains verdi commands for the scf workchain -in general these should become options of verdi aiida-fleur workchains -""" +''' +Module with CLI commands for calcjob types of aiida-fleur. +''' +from .. import cmd_root -from __future__ import absolute_import -import click -from . import cmd_workflow +@cmd_root.group('launch') +def cmd_launch(): + """Commands to launch workflows and calcjobs of aiida-fleur.""" -@cmd_workflow.group('scf') -def cmd_scf(): - """Commands to launch and inspect scf workchains.""" - - -@cmd_scf.command('launch') -def launch_scf(): - """ - Prints the result node to screen - """ - click.echo('Not implemented yet, sorry. Please implement me!') +from .launch import * diff --git a/aiida_fleur/cmdline/launch/launch.py b/aiida_fleur/cmdline/launch/launch.py new file mode 100755 index 000000000..5a30af0c9 --- /dev/null +++ b/aiida_fleur/cmdline/launch/launch.py @@ -0,0 +1,386 @@ +# -*- 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/ # +############################################################################### +''' +Module with CLI commands to launch for calcjob and workflows of aiida-fleur. + +# TODO: these launch commands should be put in seperate files, if this one becomes to large.. +''' +import click +from . import cmd_launch +from ..util import options +from ..util.utils import launch_process +from ..util import defaults +#from ..util.defaults import si_structure +from aiida_fleur.tools.dict_util import clean_nones +from aiida.orm import Code, load_node, Dict +from aiida.plugins import WorkflowFactory +from aiida.plugins import CalculationFactory + + +@cmd_launch.command('inpgen') +@options.STRUCTURE_OR_FILE(default=defaults.get_si_bulk_structure, show_default=True) +@options.INPGEN() +@options.CALC_PARAMETERS() +@options.SETTINGS() +@options.DAEMON() +def launch_inpgen(structure, inpgen, calc_parameters, daemon, settings): #, num_machines, wallclock_seconds, daemon): + """ + Launch an inpgen calcjob on given input + + If no code is given it queries the DB for inpgen codes and uses the one with + the newest creation time. + + Either structure or anysource_structure can be specified. + Default structure is Si bulk. + """ + + process_class = CalculationFactory('fleur.inpgen') + inputs = { + 'structure': structure, + 'parameters': calc_parameters, + 'settings': settings, + 'metadata': { + 'options': { + 'withmpi': False, + 'resources': { + 'wallclock_seconds': 600, + 'num_machines': 1, + 'num_mpiprocs_per_machine': 1, + } + } + } + } + + builder = process_class.get_builder(code=load_node(inpgen), **inputs) + + launch_process(builder, daemon) + + +@cmd_launch.command('fleur') +@options.FLEURINP() +@options.FLEUR() +@options.WF_PARAMETERS() +@options.REMOTE() +@options.SETTINGS() +@options.DAEMON() +@options.MAX_NUM_MACHINES() +@options.MAX_WALLCLOCK_SECONDS() +@options.NUM_MPIPROCS_PER_MACHINE() +@options.OPTION_NODE() +@options.WITH_MPI() +@click.option('--launch_base/--no-launch_base', + is_flag=True, + default=True, + show_default=True, + help=('Run the base_fleur workchain, which also handles errors instead ' + 'of a single fleur calcjob.')) +def launch_fleur(fleurinp, fleur, wf_parameters, parent_folder, daemon, launch_base, settings, max_num_machines, + num_mpiprocs_per_machine, max_wallclock_seconds, with_mpi, option_node): + """ + Launch a base_fleur workchain. + If launch_base is False launch a single fleur calcjob instead. + + """ + + process_class = CalculationFactory('fleur.fleur') + workchain_class = WorkflowFactory('fleur.base') + + inputs = { + 'fleurinpdata': fleurinp, + 'wf_parameters': wf_parameters, + 'parent_folder': parent_folder, + 'settings': settings, + 'metadata': { + 'options': { + 'withmpi': with_mpi, + 'resources': { + 'wallclock_seconds': max_wallclock_seconds, + 'num_machines': max_num_machines, + 'num_mpiprocs_per_machine': num_mpiprocs_per_machine, + } + } + } + } + + if not launch_base: + inputs = clean_nones(inputs) + builder = process_class.get_builder(code=load_node(fleur), **inputs) + else: + if option_node is None: + option_node = Dict( + dict={ + 'withmpi': with_mpi, + 'resources': { + 'wallclock_seconds': max_wallclock_seconds, + 'num_machines': max_num_machines, + 'num_mpiprocs_per_machine': num_mpiprocs_per_machine + } + }) + + inputs_base = { + 'fleurinpdata': fleurinp, + 'wf_parameters': wf_parameters, + 'parent_folder': parent_folder, + 'settings': settings, + 'options': option_node + } + inputs_base = clean_nones(inputs_base) + builder = workchain_class.get_builder(code=load_node(fleur), **inputs_base) + + launch_process(builder, daemon) + + +@cmd_launch.command('scf') +@options.STRUCTURE_OR_FILE(default=defaults.get_si_bulk_structure, show_default=True) +@options.INPGEN() +@options.CALC_PARAMETERS() +@options.SETTINGS() +@options.FLEURINP() +@options.FLEUR() +@options.WF_PARAMETERS() +@options.REMOTE() +@options.SETTINGS() +@options.DAEMON() +@options.OPTION_NODE() +def launch_scf(structure, inpgen, calc_parameters, fleurinp, fleur, wf_parameters, parent_folder, daemon, settings, + option_node): + """ + Launch a scf workchain + """ + workchain_class = WorkflowFactory('fleur.scf') + inputs = { + 'structure': structure, + 'fleurinpdata': fleurinp, + 'wf_parameters': wf_parameters, + 'calc_parameters': calc_parameters, + 'remote_data': parent_folder, + 'settings': settings, + 'options': option_node + } + if inpgen is not None: + inputs['inpgen'] = load_node(inpgen) + inputs = clean_nones(inputs) + builder = workchain_class.get_builder(code=load_node(fleur)) + builder.update(inputs) + launch_process(builder, daemon) + + +@cmd_launch.command('relax') +@options.STRUCTURE_OR_FILE(default=defaults.get_si_bulk_structure, show_default=True) +@options.INPGEN() +@options.CALC_PARAMETERS() +@options.SETTINGS() +@options.FLEUR() +@options.WF_PARAMETERS() +@options.SCF_PARAMETERS() +@options.DAEMON() +@options.OPTION_NODE() +def launch_relax(structure, inpgen, calc_parameters, fleur, wf_parameters, scf_parameters, daemon, settings, + option_node): + """ + Launch a base relax workchain + + # TODO final scf input + """ + workchain_class = WorkflowFactory('fleur.base_relax') + inputs = { + 'scf': { + 'wf_parameters': scf_parameters, + 'structure': structure, + 'calc_parameters': calc_parameters, + 'options': option_node, + 'inpgen': inpgen, + 'fleur': fleur + }, + 'wf_parameters': wf_parameters + } + inputs = clean_nones(inputs) + builder = workchain_class.get_builder() + builder.update(inputs) + launch_process(builder, daemon) + + +@cmd_launch.command('eos') +@options.STRUCTURE_OR_FILE(default=defaults.get_si_bulk_structure, show_default=True) +@options.INPGEN() +@options.CALC_PARAMETERS() +@options.SETTINGS() +@options.FLEUR() +@options.WF_PARAMETERS() +@options.SCF_PARAMETERS() +@options.DAEMON() +@options.OPTION_NODE() +def launch_eos(structure, inpgen, calc_parameters, fleur, wf_parameters, scf_parameters, daemon, settings, option_node): + """ + Launch a eos workchain + """ + workchain_class = WorkflowFactory('fleur.eos') + inputs = { + 'scf': { + 'wf_parameters': scf_parameters, + 'calc_parameters': calc_parameters, + 'options': option_node, + 'inpgen': inpgen, + 'fleur': fleur + }, + 'wf_parameters': wf_parameters, + 'structure': structure + } + inputs = clean_nones(inputs) + builder = workchain_class.get_builder() + builder.update(inputs) + launch_process(builder, daemon) + + +@cmd_launch.command('banddos') +@options.FLEURINP() +@options.FLEUR() +@options.WF_PARAMETERS() +@options.REMOTE() +@options.DAEMON() +@options.OPTION_NODE() +def launch_banddos(fleurinp, fleur, wf_parameters, parent_folder, daemon, settings, option_node): + """ + Launch a banddos workchain + """ + click.echo('Not implemented yet, sorry. Please implement me!') + workchain_class = WorkflowFactory('fleur.banddos') + inputs = { + 'wf_parameters': wf_parameters, + 'fleur': fleur, + 'remote': parent_folder, + 'fleurinp': fleurinp, + 'options': option_node + } + inputs = clean_nones(inputs) + builder = workchain_class.get_builder() + builder.update(inputs) + launch_process(builder, daemon) + + +@cmd_launch.command('init_cls') +@options.STRUCTURE_OR_FILE(default=defaults.get_si_bulk_structure, show_default=True) +@options.INPGEN() +@options.CALC_PARAMETERS() +@options.SETTINGS() +@options.FLEURINP() +@options.FLEUR() +@options.WF_PARAMETERS() +@options.DAEMON() +@options.OPTION_NODE() +def launch_init_cls(structure, inpgen, calc_parameters, fleurinp, fleur, wf_parameters, daemon, settings, option_node): + """ + Launch an init_cls workchain + """ + click.echo('Not implemented yet, sorry. Please implement me!') + workchain_class = WorkflowFactory('fleur.init_cls') + inputs = { + 'calc_parameters': calc_parameters, + 'options': option_node, + 'inpgen': inpgen, + 'fleur': fleur, + 'wf_parameters': wf_parameters, + 'structure': structure + } + inputs = clean_nones(inputs) + builder = workchain_class.get_builder() + builder.update(inputs) + launch_process(builder, daemon) + + +@cmd_launch.command('corehole') +@options.STRUCTURE_OR_FILE(default=defaults.get_si_bulk_structure, show_default=True) +@options.INPGEN() +@options.CALC_PARAMETERS() +@options.SETTINGS() +@options.FLEURINP() +@options.FLEUR() +@options.WF_PARAMETERS() +@options.DAEMON() +@options.OPTION_NODE() +def launch_corehole(structure, inpgen, calc_parameters, fleurinp, fleur, wf_parameters, daemon, settings, option_node): + """ + Launch a corehole workchain + """ + click.echo('Not implemented yet, sorry. Please implement me!') + workchain_class = WorkflowFactory('fleur.corehole') + inputs = { + 'calc_parameters': calc_parameters, + 'options': option_node, + 'inpgen': inpgen, + 'fleur': fleur, + 'wf_parameters': wf_parameters, + 'structure': structure + } + inputs = clean_nones(inputs) + builder = workchain_class.get_builder() + builder.update(inputs) + launch_process(builder, daemon) + + +@cmd_launch.command('mae') +@options.STRUCTURE_OR_FILE(default=defaults.get_si_bulk_structure, show_default=True) +@options.INPGEN() +@options.CALC_PARAMETERS() +@options.SETTINGS() +@options.FLEURINP() +@options.FLEUR() +@options.WF_PARAMETERS() +@options.REMOTE() +@options.DAEMON() +@options.OPTION_NODE() +def launch_mae(structure_or_file, inpgen, calc_parameters, fleurinp, fleur, wf_parameter, remote, daemon, settings, + option_node): + """ + Launch a mae workchain + """ + click.echo('Not implemented yet, sorry. Please implement me!') + #workchain_class = WorkflowFactory('fleur.mae') + #inputs = {} + #builder = workchain_class.get_builder(code=load_node(fleur),**inputs) + #launch_process(builder, daemon) + + +@cmd_launch.command('ssdisp') +def launch_ssdisp(): + """ + Launch a ssdisp workchain + """ + click.echo('Not implemented yet, sorry. Please implement me!') + #workchain_class = WorkflowFactory('fleur.ssdisp') + #inputs = {} + #builder = workchain_class.get_builder(code=load_node(fleur),**inputs) + #launch_process(builder, daemon) + + +@cmd_launch.command('dmi') +def launch_dmi(): + """ + Launch a dmi workchain + """ + click.echo('Not implemented yet, sorry. Please implement me!') + #workchain_class = WorkflowFactory('fleur.dmi') + #inputs = {} + #builder = workchain_class.get_builder(code=load_node(fleur),**inputs) + #launch_process(builder, daemon) + + +@cmd_launch.command('create_magnetic') +def launch_create_magnetic(): + """ + Launch a create_magnetic workchain + """ + click.echo('Not implemented yet, sorry. Please implement me!') + #workchain_class = WorkflowFactory('fleur.create_magnetic') + #inputs = {} + #builder = workchain_class.get_builder(code=load_node(fleur),**inputs) + #launch_process(builder, daemon) diff --git a/aiida_fleur/cmdline/calculations/__init__.py b/aiida_fleur/cmdline/list/__init__.py similarity index 81% rename from aiida_fleur/cmdline/calculations/__init__.py rename to aiida_fleur/cmdline/list/__init__.py index f809d3c12..318a115bb 100755 --- a/aiida_fleur/cmdline/calculations/__init__.py +++ b/aiida_fleur/cmdline/list/__init__.py @@ -15,11 +15,6 @@ from .. import cmd_root -@cmd_root.group('calcjob') +@cmd_root.group('list') def cmd_calcjob(): - """Commands to launch and inspect calcjobs of aiida-fleur.""" - - -# Import the sub commands to register them with the CLI -from .fleur import cmd_fleur -from .inpgen import cmd_inpgen + """Command group to list calcjobs and workflows of aiida-fleur.""" diff --git a/aiida_fleur/cmdline/util/defaults.py b/aiida_fleur/cmdline/util/defaults.py new file mode 100644 index 000000000..5374d0bbc --- /dev/null +++ b/aiida_fleur/cmdline/util/defaults.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +""" +Here we specify some defaults for cli commands +""" + +# Structures + + +def get_si_bulk_structure(): + """Return a `StructureData` representing bulk silicon. + + The database will first be queried for the existence of a bulk silicon crystal. If this is not the case, one is + created and stored. This function should be used as a default for CLI options that require a `StructureData` node. + This way new users can launch the command without having to construct or import a structure first. This is the + reason that we hardcode a bulk silicon crystal to be returned. More flexibility is not required for this purpose. + + :return: a `StructureData` representing bulk silicon + """ + from ase.spacegroup import crystal + from aiida.orm import QueryBuilder, StructureData + + # Filters that will match any elemental Silicon structure with 2 or less sites in total + filters = { + 'attributes.sites': { + 'of_length': 2 + }, + 'attributes.kinds': { + 'of_length': 1 + }, + 'attributes.kinds.0.symbols.0': 'Si' + } + + builder = QueryBuilder().append(StructureData, filters=filters) + results = builder.first() + + if not results: + alat = 5.43 + ase_structure = crystal( + 'Si', + [(0, 0, 0)], + spacegroup=227, + cellpar=[alat, alat, alat, 90, 90, 90], + primitive_cell=True, + ) + structure = StructureData(ase=ase_structure) + structure.store() + else: + structure = results[0] + + return structure.uuid + + +# Codes +def get_inpgen(): + """Return a `Code` node of the latest added inpgen executable in the database.""" + + return get_last_code('fleur.inpgen') + + +def get_fleur(): + """Return a `Code` node of the latest added inpgen executable in the database.""" + + return get_last_code('fleur.fleur') + + +def get_last_code(entry_point_name): + """Return a `Code` node of the latest code executable of the given entry_point_name in the database. + + The database will be queried for the existence of a inpgen node. + If this is not exists and NotExistent error is raised. + + + :param entry_point_name: string + :return: the uuid of a inpgen `Code` node + :raise: aiida.common.exceptions.NotExistent + """ + from aiida.orm import QueryBuilder, Code + from aiida.common.exceptions import NotExistent + + filters = {'attributes.input_plugin': {'==': entry_point_name}} + + builder = QueryBuilder().append(Code, filters=filters) + builder.order_by({Code: {'ctime': 'asc'}}) + results = builder.first() + + if not results: + raise NotExistent(f'ERROR: Could not find any Code in the database with entry point: {entry_point_name}!') + else: + inpgen = results[0] + + return inpgen.uuid diff --git a/aiida_fleur/cmdline/util/options.py b/aiida_fleur/cmdline/util/options.py index 409304a69..515249e62 100755 --- a/aiida_fleur/cmdline/util/options.py +++ b/aiida_fleur/cmdline/util/options.py @@ -17,11 +17,79 @@ import click from aiida.cmdline.params import types from aiida.cmdline.params.options import OverridableOption +from .defaults import get_inpgen, get_fleur, get_si_bulk_structure +from .types import StructureNodeOrFileParamType + +#ANYSOURCE_STRUCTURE = OverridableOption( +# '-as', '--anysource_structure', +# type=click.Path(exists=True), +# help=('A path to a file which contains structural information which will ' +# 'be parsed with ase or pymatgen extracting a StructureData node.')) + +STRUCTURE_OR_FILE = OverridableOption( + '-s', + '--structure', + type=StructureNodeOrFileParamType(), + help='StructureData node, given by pk or uuid or file in any for mat which will be converted.') STRUCTURE = OverridableOption('-s', '--structure', type=types.DataParamType(sub_classes=('aiida.data:structure',)), - help='StructureData node.') + help='StructureData node, given by pk or uuid.') + +FULL_PROVENANCE = OverridableOption('-fp', + '--full-provenance', + is_flag=True, + default=False, + show_default=True, + help=('Store the full or reduced provenance. Example with the "-as" ' + 'also the given file will be stored in the database together with a ' + 'calcfunction extracting the structure.')) + +INPGEN = OverridableOption('-inpgen', + '--inpgen', + type=types.CodeParamType(entry_point='fleur.inpgen'), + default=get_inpgen, + show_default=True, + help='A code node or label for an inpgen executable.') + +FLEUR = OverridableOption('-fleur', + '--fleur', + type=types.CodeParamType(entry_point='fleur.fleur'), + default=get_fleur, + show_default=True, + help='A code node or label for a fleur executable.') + +FLEURINP = OverridableOption('-inp', + '--fleurinp', + type=types.DataParamType(sub_classes=('aiida.data:fleur.fleurinp',)), + help='FleurinpData node for the fleur calculation.') + +CALC_PARAMETERS = OverridableOption( + '-cp', + '--calc-parameters', + type=types.DataParamType(sub_classes=('aiida.data:dict',)), + help='Dict with calculation (FLAPW) parameters to build, which will be given to inpgen.') + +SETTINGS = OverridableOption('-se', + '--settings', + type=types.DataParamType(sub_classes=('aiida.data:dict',)), + help='Settings node for the calcjob.') + +WF_PARAMETERS = OverridableOption('-wp', + '--wf-parameters', + type=types.DataParamType(sub_classes=('aiida.data:dict',)), + help='Dict containing parameters given to the workchain.') + +SCF_PARAMETERS = OverridableOption('-sp', + '--scf-parameters', + type=types.DataParamType(sub_classes=('aiida.data:dict',)), + help='Dict containing parameters given to the sub SCF workchains.') + +OPTION_NODE = OverridableOption('-on', + '--option-node', + type=types.DataParamType(sub_classes=('aiida.data:dict',)), + help='Dict, an option node for the workchain.') MAX_NUM_MACHINES = OverridableOption('-m', '--max-num-machines', @@ -35,7 +103,14 @@ type=click.INT, default=1800, show_default=True, - help='the maximum wallclock time in seconds to set for the calculations.') + help='The maximum wallclock time in seconds to set for the calculations.') + +NUM_MPIPROCS_PER_MACHINE = OverridableOption('-n', + '--num-mpiprocs-per-machine', + type=click.INT, + default=12, + show_default=True, + help='Run the simulation with so many num-mpi-procs-per-machine.') WITH_MPI = OverridableOption('-i', '--with-mpi', @@ -44,13 +119,13 @@ show_default=True, help='Run the calculations with MPI enabled.') -PARENT_FOLDER = OverridableOption('-P', - '--parent-folder', - 'parent_folder', - type=types.DataParamType(sub_classes=('aiida.data:remote',)), - show_default=True, - required=False, - help='The PK of a parent remote folder (for restarts).') +REMOTE = OverridableOption('-P', + '--parent-folder', + 'parent_folder', + type=types.DataParamType(sub_classes=('aiida.data:remote',)), + show_default=True, + required=False, + help='The PK of a parent remote folder (for restarts).') DAEMON = OverridableOption('-d', '--daemon', diff --git a/aiida_fleur/cmdline/util/types.py b/aiida_fleur/cmdline/util/types.py new file mode 100644 index 000000000..b6e835f0a --- /dev/null +++ b/aiida_fleur/cmdline/util/types.py @@ -0,0 +1,64 @@ +# -*- 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 click option types specific to aiida-fleur +''' +import click +from aiida.cmdline.params import types +from aiida.cmdline.utils import echo +from aiida.common.exceptions import NotExistent +from aiida.plugins import DataFactory + + +#StructureOption = types.DataParamType(sub_classes=('aiida.data:structure',)) +class StructureNodeOrFileParamType(types.DataParamType): + """ + The ParamType for identifying a structure by node or to extract it from a given file + + Pro: It is convenient + Con: If users only use other formats to launch their workflows it will create many + more structures in the database. + """ + + name = 'StructureFile' + sub_classes = ('aiida.data:structure',) + + def convert(self, value, param, ctx): + is_path = False + # Alternative one could check if int or uuid + # aiida allows also for shorten uuids + + try: + structure = super().convert(value, param, ctx) + except NotExistent: + echo.echo(f'Tried to load node, could not fine one for {value}.' + 'I will further check if it is a filepath.') + is_path = True + + if is_path: + StructureData = DataFactory('structure') + # If it is a path to a file try to convert the structure + pathtype = click.Path(exists=True, dir_okay=False, resolve_path=True) + filename = pathtype.convert(value, param, ctx) + try: + import ase.io + except ImportError: + echo.echo_critical('You have not installed the package ase. \nYou can install it with: pip install ase') + + try: + asecell = ase.io.read(filename) + structure = StructureData(ase=asecell) + except ValueError as err: + echo.echo_critical(str(err)) + # do not store structure, since this option is for calculation and workflow + # input, which will store the structure anyway. + return structure diff --git a/aiida_fleur/cmdline/util/utils.py b/aiida_fleur/cmdline/util/utils.py new file mode 100644 index 000000000..4b64fe013 --- /dev/null +++ b/aiida_fleur/cmdline/util/utils.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +""" +Module with utitlies for the CLI. + +The echo_process_results and launch_process routines have been copied from +aiida-common-workflows repository found under cli utils +""" +import click + + +def echo_process_results(node): + """Display a formatted table of the outputs registered for the given process node. + + :param node: the `ProcessNode` of a terminated process. + """ + from aiida.common.links import LinkType + + class_name = node.process_class.__name__ + outputs = node.get_outgoing(link_type=(LinkType.CREATE, LinkType.RETURN)).all() + + if node.is_finished and node.exit_message: + state = '{} [{}] `{}`'.format(node.process_state.value, node.exit_status, node.exit_message) + elif node.is_finished: + state = '{} [{}]'.format(node.process_state.value, node.exit_status) + else: + state = node.process_state.value + + click.echo('{}<{}> terminated with state: {}'.format(class_name, node.pk, state)) + + if not outputs: + click.echo('{}<{}> registered no outputs'.format(class_name, node.pk)) + return + + click.echo('\n{link:25s} {node}'.format(link='Output link', node='Node pk and type')) + click.echo('{s}'.format(s='-' * 60)) + + for triple in sorted(outputs, key=lambda triple: triple.link_label): + click.echo('{:25s} {}<{}> '.format(triple.link_label, triple.node.__class__.__name__, triple.node.pk)) + + +def launch_process(process, daemon, **inputs): + """Launch a process with the given inputs. + + If not sent to the daemon, the results will be displayed after the calculation finishes. + + :param process: the process class or process builder. + :param daemon: boolean, if True will submit to the daemon instead of running in current interpreter. + :param inputs: inputs for the process if the process is not already a fully prepared builder. + """ + from aiida.engine import launch, Process, ProcessBuilder + + if isinstance(process, ProcessBuilder): + process_name = process.process_class.__name__ + elif issubclass(process, Process): + process_name = process.__name__ + else: + raise TypeError('invalid type for process: {}'.format(process)) + + if daemon: + node = launch.submit(process, **inputs) + click.echo('Submitted {}<{}> to the daemon'.format(process_name, node.pk)) + else: + click.echo('Running a {}...'.format(process_name)) + _, node = launch.run_get_node(process, **inputs) + echo_process_results(node) diff --git a/aiida_fleur/cmdline/workflows/__init__.py b/aiida_fleur/cmdline/workflows/__init__.py index a6ad84846..6627948b3 100755 --- a/aiida_fleur/cmdline/workflows/__init__.py +++ b/aiida_fleur/cmdline/workflows/__init__.py @@ -21,11 +21,11 @@ @cmd_root.group('workflow') def cmd_workflow(): - """Commands to launch and inspect various workchains.""" + """Commands to inspect aiida-fleur workchains.""" # Import the sub commands to register them with the CLI -from .scf import cmd_scf +#from .scf import cmd_scf #from .relax import cmd_relax #from .banddos import cmd_banddos #from .eos import cmd_eos diff --git a/aiida_fleur/cmdline/workflows/workchains.py b/aiida_fleur/cmdline/workflows/workchains.py deleted file mode 100755 index ca4905af8..000000000 --- a/aiida_fleur/cmdline/workflows/workchains.py +++ /dev/null @@ -1,48 +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/ # -############################################################################### -""" -contains verdi commands that are useful for fleur workchains -""" - -from __future__ import absolute_import -import click - - -@click.group() -def workchains(): - pass - - -@workchains.command() -def res_wc(): - """ - Prints the result node to screen - """ - click.echo('verdi aiida-fleur workchains res pk/uuid/list') - - -@workchains.command() -def show_wc(): - """ - plots the results of a workchain - """ - click.echo('verdi aiida-fleur workchains show pk/uuid/list') - - -@workchains.command() -def list_wc(): - """ - similar to the verdi work list command, but this displays also some - specific information about the fleur workchains, can be filtered for - certain workchains... - """ - click.echo('verdi aiida-fleur workchians list -scf -A -p') diff --git a/aiida_fleur/tools/dict_util.py b/aiida_fleur/tools/dict_util.py index b6f3bc345..9ecfbb2aa 100644 --- a/aiida_fleur/tools/dict_util.py +++ b/aiida_fleur/tools/dict_util.py @@ -75,3 +75,22 @@ def dict_merger(dict1, dict2): else: print(("don't know what to do with element : {}".format(key))) return new_dict + + +def clean_nones(dict_to_clean): + """Recursively remove all keys which values are None from a nested dictionary + return the cleaned dictionary + + :param dict_to_clean: (dict): python dictionary to remove keys with None as value + :return: dict, cleaned dictionary + """ + new_dict = {} + for key, val in dict_to_clean.items(): + if isinstance(val, dict): + new_val = clean_nones(val) + else: + new_val = val + if new_val is not None: # currently we keep empty dicts + new_dict[key] = new_val + + return new_dict diff --git a/aiida_fleur/workflows/mae_conv.py b/aiida_fleur/workflows/mae_conv.py index fdddfb93d..4643ea389 100644 --- a/aiida_fleur/workflows/mae_conv.py +++ b/aiida_fleur/workflows/mae_conv.py @@ -201,7 +201,7 @@ def return_results(self): 'workflow_version': self._workflowversion, # 'initial_structure': self.inputs.structure.uuid, 'mae': self.ctx.energydict, - 'original_mae' : self.ctx.original_energydict, + 'original_mae': self.ctx.original_energydict, 'sqa': self.ctx.wf_dict['sqas'], 'failed_labels': failed_labels, 'mae_units': 'eV', diff --git a/aiida_fleur/workflows/ssdisp_conv.py b/aiida_fleur/workflows/ssdisp_conv.py index 05aa57dac..e5fd5af09 100644 --- a/aiida_fleur/workflows/ssdisp_conv.py +++ b/aiida_fleur/workflows/ssdisp_conv.py @@ -215,7 +215,7 @@ def return_results(self): 'workflow_version': self._workflowversion, # 'initial_structure': self.inputs.structure.uuid, 'energies': self.ctx.energydict, - 'original_energies' : self.ctx.original_energydict, + 'original_energies': self.ctx.original_energydict, 'q_vectors': self.ctx.wf_dict['q_vectors'], 'failed_labels': failed_labels, 'energy_units': 'eV', diff --git a/tests/tools/test_dict_util.py b/tests/tools/test_dict_util.py index ceebbb6e1..d848a6fd2 100644 --- a/tests/tools/test_dict_util.py +++ b/tests/tools/test_dict_util.py @@ -88,3 +88,13 @@ def test_extract_elementpara_interface_W(): para_dict = {'a': 1, 'atom': {'element': 'H', 'rmt': 1}, 'atom1': {'element': 'W', 'rmt': 4}} assert extract_elementpara(para_dict, 'W') == {'a': 1, 'atom1': {'element': 'W', 'rmt': 4}} + + +def test_clean_nones(): + from aiida_fleur.tools.dict_util import clean_nones + + test_dict = {1: None, 2: 3, 4: {1: None}} + expected = {2: 3, 4: {}} + out_dict = clean_nones(test_dict) + + assert out_dict == expected From 6755d5b40a697f613cc78ad93b5c8f3b1a5572fc Mon Sep 17 00:00:00 2001 From: broeder-j Date: Mon, 16 Nov 2020 12:30:24 +0100 Subject: [PATCH 04/53] Minor bugfixes of CLI from previous commit --- aiida_fleur/cmdline/launch/launch.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/aiida_fleur/cmdline/launch/launch.py b/aiida_fleur/cmdline/launch/launch.py index 5a30af0c9..c7496b3ea 100755 --- a/aiida_fleur/cmdline/launch/launch.py +++ b/aiida_fleur/cmdline/launch/launch.py @@ -60,7 +60,7 @@ def launch_inpgen(structure, inpgen, calc_parameters, daemon, settings): #, num } } - builder = process_class.get_builder(code=load_node(inpgen), **inputs) + builder = process_class.get_builder(code=inpgen, **inputs) launch_process(builder, daemon) @@ -68,7 +68,6 @@ def launch_inpgen(structure, inpgen, calc_parameters, daemon, settings): #, num @cmd_launch.command('fleur') @options.FLEURINP() @options.FLEUR() -@options.WF_PARAMETERS() @options.REMOTE() @options.SETTINGS() @options.DAEMON() @@ -83,7 +82,7 @@ def launch_inpgen(structure, inpgen, calc_parameters, daemon, settings): #, num show_default=True, help=('Run the base_fleur workchain, which also handles errors instead ' 'of a single fleur calcjob.')) -def launch_fleur(fleurinp, fleur, wf_parameters, parent_folder, daemon, launch_base, settings, max_num_machines, +def launch_fleur(fleurinp, fleur, parent_folder, daemon, launch_base, settings, max_num_machines, num_mpiprocs_per_machine, max_wallclock_seconds, with_mpi, option_node): """ Launch a base_fleur workchain. @@ -95,15 +94,15 @@ def launch_fleur(fleurinp, fleur, wf_parameters, parent_folder, daemon, launch_b workchain_class = WorkflowFactory('fleur.base') inputs = { + 'code': fleur, 'fleurinpdata': fleurinp, - 'wf_parameters': wf_parameters, 'parent_folder': parent_folder, 'settings': settings, 'metadata': { 'options': { 'withmpi': with_mpi, + 'max_wallclock_seconds': max_wallclock_seconds, 'resources': { - 'wallclock_seconds': max_wallclock_seconds, 'num_machines': max_num_machines, 'num_mpiprocs_per_machine': num_mpiprocs_per_machine, } @@ -113,28 +112,30 @@ def launch_fleur(fleurinp, fleur, wf_parameters, parent_folder, daemon, launch_b if not launch_base: inputs = clean_nones(inputs) - builder = process_class.get_builder(code=load_node(fleur), **inputs) + builder = process_class.get_builder() + builder.update(inputs) else: if option_node is None: option_node = Dict( dict={ 'withmpi': with_mpi, + 'max_wallclock_seconds': max_wallclock_seconds, 'resources': { - 'wallclock_seconds': max_wallclock_seconds, 'num_machines': max_num_machines, 'num_mpiprocs_per_machine': num_mpiprocs_per_machine } }) inputs_base = { + 'code': fleur, 'fleurinpdata': fleurinp, - 'wf_parameters': wf_parameters, 'parent_folder': parent_folder, 'settings': settings, 'options': option_node } inputs_base = clean_nones(inputs_base) - builder = workchain_class.get_builder(code=load_node(fleur), **inputs_base) + builder = workchain_class.get_builder() + builder.update(**inputs_base) launch_process(builder, daemon) From 01f16d7f34165619de8f5f082e384932ceff4021 Mon Sep 17 00:00:00 2001 From: broeder-j Date: Tue, 17 Nov 2020 09:26:36 +0100 Subject: [PATCH 05/53] Bugfix in base_relax, CLI structure type and launch --- aiida_fleur/cmdline/launch/launch.py | 9 +++++---- aiida_fleur/cmdline/util/types.py | 9 ++++----- aiida_fleur/workflows/base_relax.py | 6 +++++- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/aiida_fleur/cmdline/launch/launch.py b/aiida_fleur/cmdline/launch/launch.py index c7496b3ea..5b2a013eb 100755 --- a/aiida_fleur/cmdline/launch/launch.py +++ b/aiida_fleur/cmdline/launch/launch.py @@ -45,23 +45,24 @@ def launch_inpgen(structure, inpgen, calc_parameters, daemon, settings): #, num process_class = CalculationFactory('fleur.inpgen') inputs = { + 'code': inpgen, 'structure': structure, 'parameters': calc_parameters, 'settings': settings, 'metadata': { 'options': { 'withmpi': False, + 'max_wallclock_seconds': 6000, 'resources': { - 'wallclock_seconds': 600, 'num_machines': 1, 'num_mpiprocs_per_machine': 1, } } } } - - builder = process_class.get_builder(code=inpgen, **inputs) - + inputs = clean_nones(inputs) + builder = process_class.get_builder() + builder.update(inputs) launch_process(builder, daemon) diff --git a/aiida_fleur/cmdline/util/types.py b/aiida_fleur/cmdline/util/types.py index b6e835f0a..68de3d68b 100644 --- a/aiida_fleur/cmdline/util/types.py +++ b/aiida_fleur/cmdline/util/types.py @@ -20,7 +20,7 @@ #StructureOption = types.DataParamType(sub_classes=('aiida.data:structure',)) -class StructureNodeOrFileParamType(types.DataParamType): +class StructureNodeOrFileParamType(click.ParamType): #types.DataParamType): """ The ParamType for identifying a structure by node or to extract it from a given file @@ -30,7 +30,6 @@ class StructureNodeOrFileParamType(types.DataParamType): """ name = 'StructureFile' - sub_classes = ('aiida.data:structure',) def convert(self, value, param, ctx): is_path = False @@ -38,9 +37,9 @@ def convert(self, value, param, ctx): # aiida allows also for shorten uuids try: - structure = super().convert(value, param, ctx) - except NotExistent: - echo.echo(f'Tried to load node, could not fine one for {value}.' + structure = types.DataParamType(sub_classes=('aiida.data:structure',)).convert(value, param, ctx) + except (NotExistent, click.exceptions.BadParameter) as er: + echo.echo(f'Tried to load node, could not fine one for {value}. ' 'I will further check if it is a filepath.') is_path = True diff --git a/aiida_fleur/workflows/base_relax.py b/aiida_fleur/workflows/base_relax.py index dc18c2fdf..11df482e5 100644 --- a/aiida_fleur/workflows/base_relax.py +++ b/aiida_fleur/workflows/base_relax.py @@ -89,7 +89,11 @@ def pop_non_stacking_inpxml_changes(self): """ pops some inpxml_changes that do not stack, for example shift_value. """ - wf_param = self.ctx.inputs.scf.wf_parameters.get_dict() + if 'wf_parameters' in self.ctx.inputs.scf: + wf_param = self.ctx.inputs.scf.wf_parameters.get_dict() + else: + wf_param = {} + if 'inpxml_changes' not in wf_param: return From 8b6d40aafc8a3d0199818769e63c82d2a048bd1c Mon Sep 17 00:00:00 2001 From: broeder-j Date: Tue, 17 Nov 2020 09:35:58 +0100 Subject: [PATCH 06/53] Fix small bug in cli type and launch --- aiida_fleur/cmdline/launch/launch.py | 7 ++++--- aiida_fleur/cmdline/util/types.py | 3 +-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/aiida_fleur/cmdline/launch/launch.py b/aiida_fleur/cmdline/launch/launch.py index 5b2a013eb..3d3da0dcc 100755 --- a/aiida_fleur/cmdline/launch/launch.py +++ b/aiida_fleur/cmdline/launch/launch.py @@ -160,6 +160,8 @@ def launch_scf(structure, inpgen, calc_parameters, fleurinp, fleur, wf_parameter """ workchain_class = WorkflowFactory('fleur.scf') inputs = { + 'inpgen': inpgen, + 'fleur': fleur, 'structure': structure, 'fleurinpdata': fleurinp, 'wf_parameters': wf_parameters, @@ -168,10 +170,9 @@ def launch_scf(structure, inpgen, calc_parameters, fleurinp, fleur, wf_parameter 'settings': settings, 'options': option_node } - if inpgen is not None: - inputs['inpgen'] = load_node(inpgen) + inputs = clean_nones(inputs) - builder = workchain_class.get_builder(code=load_node(fleur)) + builder = workchain_class.get_builder() builder.update(inputs) launch_process(builder, daemon) diff --git a/aiida_fleur/cmdline/util/types.py b/aiida_fleur/cmdline/util/types.py index 68de3d68b..cf57aaeb1 100644 --- a/aiida_fleur/cmdline/util/types.py +++ b/aiida_fleur/cmdline/util/types.py @@ -19,8 +19,7 @@ from aiida.plugins import DataFactory -#StructureOption = types.DataParamType(sub_classes=('aiida.data:structure',)) -class StructureNodeOrFileParamType(click.ParamType): #types.DataParamType): +class StructureNodeOrFileParamType(click.ParamType): """ The ParamType for identifying a structure by node or to extract it from a given file From 21d78f4b3ee80fddd5b05fa53a96cb8367428fca Mon Sep 17 00:00:00 2001 From: broeder-j Date: Fri, 20 Nov 2020 15:15:25 +0100 Subject: [PATCH 07/53] Add cmdline launch commands for workflows Also added first tests for these commands and by these tests debuged some workflows to make them more robust. --- aiida_fleur/cmdline/__init__.py | 8 +- aiida_fleur/cmdline/data/__init__.py | 11 +- aiida_fleur/cmdline/data/fleurinp.py | 5 +- aiida_fleur/cmdline/data/structure.py | 4 +- aiida_fleur/cmdline/launch/launch.py | 117 +++++++++++----- aiida_fleur/cmdline/util/defaults.py | 32 +++++ aiida_fleur/cmdline/util/options.py | 38 ++--- aiida_fleur/workflows/banddos.py | 4 +- aiida_fleur/workflows/corehole.py | 103 ++++++-------- aiida_fleur/workflows/initial_cls.py | 30 +++- tests/.aiida/config.json | 7 - tests/cmdline/__init__.py | 10 -- tests/cmdline/conftest.py | 66 +++++++++ tests/cmdline/data/__init__.py | 0 tests/cmdline/launch/__init__.py | 0 tests/cmdline/launch/test_launch.py | 171 +++++++++++++++++++++++ tests/cmdline/test_base_all_commands.py | 23 +++ tests/cmdline/visualization/__init__.py | 0 tests/cmdline/visualization/test_plot.py | 15 ++ tests/cmdline/workflows/__init__.py | 0 tests/conftest.py | 1 + tests/workflows_regression_manually.py | 14 ++ 22 files changed, 510 insertions(+), 149 deletions(-) delete mode 100644 tests/.aiida/config.json create mode 100644 tests/cmdline/conftest.py create mode 100644 tests/cmdline/data/__init__.py create mode 100644 tests/cmdline/launch/__init__.py create mode 100644 tests/cmdline/launch/test_launch.py create mode 100644 tests/cmdline/test_base_all_commands.py create mode 100644 tests/cmdline/visualization/__init__.py create mode 100644 tests/cmdline/visualization/test_plot.py create mode 100644 tests/cmdline/workflows/__init__.py create mode 100644 tests/workflows_regression_manually.py diff --git a/aiida_fleur/cmdline/__init__.py b/aiida_fleur/cmdline/__init__.py index a1073f871..4a17e100f 100755 --- a/aiida_fleur/cmdline/__init__.py +++ b/aiida_fleur/cmdline/__init__.py @@ -31,7 +31,7 @@ def cmd_root(profile): # pylint: disable=unused-argument """CLI for the `aiida-fleur` plugin.""" -from .launch import cmd_launch -from .data import cmd_structure, cmd_fleurinp -from .workflows import cmd_workflow -from .visualization import cmd_plot +#from .launch import cmd_launch +#from .data import cmd_structure, cmd_fleurinp +#from .workflows import cmd_workflow +#from .visualization import cmd_plot diff --git a/aiida_fleur/cmdline/data/__init__.py b/aiida_fleur/cmdline/data/__init__.py index 5840bcd10..82b61a245 100755 --- a/aiida_fleur/cmdline/data/__init__.py +++ b/aiida_fleur/cmdline/data/__init__.py @@ -12,15 +12,18 @@ """ Module with CLI commands for various data types. """ +import click from .. import cmd_root -@cmd_root.group('data') +@click.group('data') def cmd_data(): """Commands to create and inspect data nodes.""" +cmd_root.add_command(cmd_data) + # Import the sub commands to register them with the CLI -from .structure import cmd_structure -from .parameters import cmd_parameter -from .fleurinp import cmd_fleurinp +#from .structure import cmd_structure +#from .parameters import cmd_parameter +#from .fleurinp import cmd_fleurinp diff --git a/aiida_fleur/cmdline/data/fleurinp.py b/aiida_fleur/cmdline/data/fleurinp.py index ef339e82c..d6c871143 100755 --- a/aiida_fleur/cmdline/data/fleurinp.py +++ b/aiida_fleur/cmdline/data/fleurinp.py @@ -24,11 +24,14 @@ FleurinpData = DataFactory('fleur.fleurinp') -@cmd_data.group('fleurinp') +@click.group('fleurinp') def cmd_fleurinp(): """Commands to handle `FleurinpData` nodes.""" +cmd_data.add_command(cmd_fleurinp) + + @cmd_fleurinp.command('list') @list_options # usual aiida list options @click.option('--uuid/--no-uuid', default=False, show_default=True, help='Display uuid of nodes.') diff --git a/aiida_fleur/cmdline/data/structure.py b/aiida_fleur/cmdline/data/structure.py index c13e9eeab..28760374d 100755 --- a/aiida_fleur/cmdline/data/structure.py +++ b/aiida_fleur/cmdline/data/structure.py @@ -11,18 +11,18 @@ ############################################################################### """Command line utilities to create and inspect `StructureData` nodes.""" import click - from aiida.cmdline.params import options from aiida.cmdline.utils import decorators, echo from . import cmd_data -@cmd_data.group('structure') +@click.group('structure') def cmd_structure(): """Commands to create and inspect `StructureData` nodes.""" +cmd_data.add_command(cmd_structure) # import filename # import -N pk_fleurinp diff --git a/aiida_fleur/cmdline/launch/launch.py b/aiida_fleur/cmdline/launch/launch.py index 3d3da0dcc..d9ae026e8 100755 --- a/aiida_fleur/cmdline/launch/launch.py +++ b/aiida_fleur/cmdline/launch/launch.py @@ -19,7 +19,6 @@ from ..util import options from ..util.utils import launch_process from ..util import defaults -#from ..util.defaults import si_structure from aiida_fleur.tools.dict_util import clean_nones from aiida.orm import Code, load_node, Dict from aiida.plugins import WorkflowFactory @@ -32,7 +31,7 @@ @options.CALC_PARAMETERS() @options.SETTINGS() @options.DAEMON() -def launch_inpgen(structure, inpgen, calc_parameters, daemon, settings): #, num_machines, wallclock_seconds, daemon): +def launch_inpgen(structure, inpgen, calc_parameters, settings, daemon): """ Launch an inpgen calcjob on given input @@ -83,8 +82,8 @@ def launch_inpgen(structure, inpgen, calc_parameters, daemon, settings): #, num show_default=True, help=('Run the base_fleur workchain, which also handles errors instead ' 'of a single fleur calcjob.')) -def launch_fleur(fleurinp, fleur, parent_folder, daemon, launch_base, settings, max_num_machines, - num_mpiprocs_per_machine, max_wallclock_seconds, with_mpi, option_node): +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): """ Launch a base_fleur workchain. If launch_base is False launch a single fleur calcjob instead. @@ -150,8 +149,8 @@ def launch_fleur(fleurinp, fleur, parent_folder, daemon, launch_base, settings, @options.FLEUR() @options.WF_PARAMETERS() @options.REMOTE() -@options.SETTINGS() @options.DAEMON() +@options.SETTINGS() @options.OPTION_NODE() def launch_scf(structure, inpgen, calc_parameters, fleurinp, fleur, wf_parameters, parent_folder, daemon, settings, option_node): @@ -163,7 +162,7 @@ def launch_scf(structure, inpgen, calc_parameters, fleurinp, fleur, wf_parameter 'inpgen': inpgen, 'fleur': fleur, 'structure': structure, - 'fleurinpdata': fleurinp, + 'fleurinp': fleurinp, 'wf_parameters': wf_parameters, 'calc_parameters': calc_parameters, 'remote_data': parent_folder, @@ -181,11 +180,11 @@ def launch_scf(structure, inpgen, calc_parameters, fleurinp, fleur, wf_parameter @options.STRUCTURE_OR_FILE(default=defaults.get_si_bulk_structure, show_default=True) @options.INPGEN() @options.CALC_PARAMETERS() -@options.SETTINGS() @options.FLEUR() @options.WF_PARAMETERS() @options.SCF_PARAMETERS() @options.DAEMON() +@options.SETTINGS() @options.OPTION_NODE() def launch_relax(structure, inpgen, calc_parameters, fleur, wf_parameters, scf_parameters, daemon, settings, option_node): @@ -216,11 +215,11 @@ def launch_relax(structure, inpgen, calc_parameters, fleur, wf_parameters, scf_p @options.STRUCTURE_OR_FILE(default=defaults.get_si_bulk_structure, show_default=True) @options.INPGEN() @options.CALC_PARAMETERS() -@options.SETTINGS() @options.FLEUR() @options.WF_PARAMETERS() @options.SCF_PARAMETERS() @options.DAEMON() +@options.SETTINGS() @options.OPTION_NODE() def launch_eos(structure, inpgen, calc_parameters, fleur, wf_parameters, scf_parameters, daemon, settings, option_node): """ @@ -250,12 +249,12 @@ def launch_eos(structure, inpgen, calc_parameters, fleur, wf_parameters, scf_par @options.WF_PARAMETERS() @options.REMOTE() @options.DAEMON() +@options.SETTINGS() @options.OPTION_NODE() def launch_banddos(fleurinp, fleur, wf_parameters, parent_folder, daemon, settings, option_node): """ Launch a banddos workchain """ - click.echo('Not implemented yet, sorry. Please implement me!') workchain_class = WorkflowFactory('fleur.banddos') inputs = { 'wf_parameters': wf_parameters, @@ -274,17 +273,16 @@ def launch_banddos(fleurinp, fleur, wf_parameters, parent_folder, daemon, settin @options.STRUCTURE_OR_FILE(default=defaults.get_si_bulk_structure, show_default=True) @options.INPGEN() @options.CALC_PARAMETERS() -@options.SETTINGS() @options.FLEURINP() @options.FLEUR() @options.WF_PARAMETERS() @options.DAEMON() +@options.SETTINGS() @options.OPTION_NODE() def launch_init_cls(structure, inpgen, calc_parameters, fleurinp, fleur, wf_parameters, daemon, settings, option_node): """ Launch an init_cls workchain """ - click.echo('Not implemented yet, sorry. Please implement me!') workchain_class = WorkflowFactory('fleur.init_cls') inputs = { 'calc_parameters': calc_parameters, @@ -304,17 +302,16 @@ def launch_init_cls(structure, inpgen, calc_parameters, fleurinp, fleur, wf_para @options.STRUCTURE_OR_FILE(default=defaults.get_si_bulk_structure, show_default=True) @options.INPGEN() @options.CALC_PARAMETERS() -@options.SETTINGS() @options.FLEURINP() @options.FLEUR() @options.WF_PARAMETERS() @options.DAEMON() +@options.SETTINGS() @options.OPTION_NODE() def launch_corehole(structure, inpgen, calc_parameters, fleurinp, fleur, wf_parameters, daemon, settings, option_node): """ Launch a corehole workchain """ - click.echo('Not implemented yet, sorry. Please implement me!') workchain_class = WorkflowFactory('fleur.corehole') inputs = { 'calc_parameters': calc_parameters, @@ -331,26 +328,90 @@ def launch_corehole(structure, inpgen, calc_parameters, fleurinp, fleur, wf_para @cmd_launch.command('mae') -@options.STRUCTURE_OR_FILE(default=defaults.get_si_bulk_structure, show_default=True) +@options.STRUCTURE_OR_FILE(default=defaults.get_fept_film_structure, show_default=True) @options.INPGEN() @options.CALC_PARAMETERS() -@options.SETTINGS() @options.FLEURINP() @options.FLEUR() @options.WF_PARAMETERS() +@options.SCF_PARAMETERS() @options.REMOTE() @options.DAEMON() +@options.SETTINGS() @options.OPTION_NODE() -def launch_mae(structure_or_file, inpgen, calc_parameters, fleurinp, fleur, wf_parameter, remote, daemon, settings, - option_node): +def launch_mae(structure, inpgen, calc_parameters, fleurinp, fleur, wf_parameters, scf_parameters, parent_folder, + daemon, settings, option_node): """ Launch a mae workchain """ - click.echo('Not implemented yet, sorry. Please implement me!') - #workchain_class = WorkflowFactory('fleur.mae') - #inputs = {} - #builder = workchain_class.get_builder(code=load_node(fleur),**inputs) - #launch_process(builder, daemon) + workchain_class = WorkflowFactory('fleur.mae') + inputs = { + 'scf': { + 'wf_parameters': scf_parameters, + 'structure': structure, + 'calc_parameters': calc_parameters, + 'settings': settings, + 'inpgen': inpgen, + 'fleur': fleur + }, + 'wf_parameters': wf_parameters, + 'fleurinp': fleurinp, + 'remote': parent_folder, + 'fleur': fleur, + 'options': option_node + } + + inputs = clean_nones(inputs) + builder = workchain_class.get_builder() + builder.update(inputs) + launch_process(builder, daemon) + + +@cmd_launch.command('create_magnetic') +@options.INPGEN() +@options.CALC_PARAMETERS() +@options.FLEUR() +@options.WF_PARAMETERS(required=True) +@options.EOS_PARAMETERS() +@options.SCF_PARAMETERS() +@options.RELAX_PARAMETERS() +@options.DAEMON() +@options.OPTION_NODE() +def launch_create_magnetic(inpgen, calc_parameters, fleur, wf_parameters, eos_parameters, scf_parameters, + relax_parameters, daemon, option_node): + """ + Launch a create_magnetic workchain + """ + workchain_class = WorkflowFactory('fleur.create_magnetic') + inputs = { + 'eos': { + 'scf': { + 'wf_parameters': scf_parameters, + 'calc_parameters': calc_parameters, + 'options': option_node, + 'inpgen': inpgen, + 'fleur': fleur + }, + 'wf_parameters': eos_parameters + }, + 'relax': { + 'scf': { + 'wf_parameters': scf_parameters, + 'calc_parameters': calc_parameters, + 'options': option_node, + 'inpgen': inpgen, + 'fleur': fleur + }, + 'wf_parameters': relax_parameters, + 'label': 'relaxation', + }, + 'wf_parameters': wf_parameters + } + + inputs = clean_nones(inputs) + builder = workchain_class.get_builder() + builder.update(inputs) + launch_process(builder, daemon) @cmd_launch.command('ssdisp') @@ -375,15 +436,3 @@ def launch_dmi(): #inputs = {} #builder = workchain_class.get_builder(code=load_node(fleur),**inputs) #launch_process(builder, daemon) - - -@cmd_launch.command('create_magnetic') -def launch_create_magnetic(): - """ - Launch a create_magnetic workchain - """ - click.echo('Not implemented yet, sorry. Please implement me!') - #workchain_class = WorkflowFactory('fleur.create_magnetic') - #inputs = {} - #builder = workchain_class.get_builder(code=load_node(fleur),**inputs) - #launch_process(builder, daemon) diff --git a/aiida_fleur/cmdline/util/defaults.py b/aiida_fleur/cmdline/util/defaults.py index 5374d0bbc..8982068ed 100644 --- a/aiida_fleur/cmdline/util/defaults.py +++ b/aiida_fleur/cmdline/util/defaults.py @@ -50,6 +50,38 @@ def get_si_bulk_structure(): return structure.uuid +def get_fept_film_structure(): + """Return a `StructureData` representing FePt film. + + The database will first be queried for the existence of a FePt film. If this is not the case, one is + created and stored. This function should be used as a default for CLI options that require a `StructureData` node. + This way new users can launch the command without having to construct or import a structure first. This is the + reason that we hardcode a FePt film to be returned. More flexibility is not required for this purpose. + + :return: the uuid of `StructureData` representing a FePt film + """ + from aiida.orm import StructureData, QueryBuilder + + bohr_a_0 = 0.52917721092 # A + a = 7.497 * bohr_a_0 + cell = [[0.7071068 * a, 0.0, 0.0], [0.0, 1.0 * a, 0.0], [0.0, 0.0, 0.7071068 * a]] + structure = StructureData(cell=cell) + structure.append_atom(position=(0.0, 0.0, -1.99285 * bohr_a_0), symbols='Fe', name='Fe123') + structure.append_atom(position=(0.5 * 0.7071068 * a, 0.5 * a, 0.0), symbols='Pt') + structure.append_atom(position=(0., 0., 2.65059 * bohr_a_0), symbols='Pt') + structure.pbc = (True, True, False) + + builder = QueryBuilder().append(StructureData, filters={'extras._aiida_hash': structure._get_hash()}) + results = builder.first() + + if not results: + structure.store() + else: + structure = results[0] + + return structure.uuid + + # Codes def get_inpgen(): """Return a `Code` node of the latest added inpgen executable in the database.""" diff --git a/aiida_fleur/cmdline/util/options.py b/aiida_fleur/cmdline/util/options.py index 515249e62..25968f419 100755 --- a/aiida_fleur/cmdline/util/options.py +++ b/aiida_fleur/cmdline/util/options.py @@ -20,12 +20,6 @@ from .defaults import get_inpgen, get_fleur, get_si_bulk_structure from .types import StructureNodeOrFileParamType -#ANYSOURCE_STRUCTURE = OverridableOption( -# '-as', '--anysource_structure', -# type=click.Path(exists=True), -# help=('A path to a file which contains structural information which will ' -# 'be parsed with ase or pymatgen extracting a StructureData node.')) - STRUCTURE_OR_FILE = OverridableOption( '-s', '--structure', @@ -46,14 +40,14 @@ 'also the given file will be stored in the database together with a ' 'calcfunction extracting the structure.')) -INPGEN = OverridableOption('-inpgen', +INPGEN = OverridableOption('-i', '--inpgen', type=types.CodeParamType(entry_point='fleur.inpgen'), default=get_inpgen, show_default=True, help='A code node or label for an inpgen executable.') -FLEUR = OverridableOption('-fleur', +FLEUR = OverridableOption('-f', '--fleur', type=types.CodeParamType(entry_point='fleur.fleur'), default=get_fleur, @@ -66,53 +60,63 @@ help='FleurinpData node for the fleur calculation.') CALC_PARAMETERS = OverridableOption( - '-cp', + '-calc_p', '--calc-parameters', type=types.DataParamType(sub_classes=('aiida.data:dict',)), help='Dict with calculation (FLAPW) parameters to build, which will be given to inpgen.') -SETTINGS = OverridableOption('-se', +SETTINGS = OverridableOption('-set', '--settings', type=types.DataParamType(sub_classes=('aiida.data:dict',)), help='Settings node for the calcjob.') -WF_PARAMETERS = OverridableOption('-wp', +WF_PARAMETERS = OverridableOption('-wf', '--wf-parameters', type=types.DataParamType(sub_classes=('aiida.data:dict',)), help='Dict containing parameters given to the workchain.') -SCF_PARAMETERS = OverridableOption('-sp', +SCF_PARAMETERS = OverridableOption('-scf', '--scf-parameters', type=types.DataParamType(sub_classes=('aiida.data:dict',)), help='Dict containing parameters given to the sub SCF workchains.') -OPTION_NODE = OverridableOption('-on', +EOS_PARAMETERS = OverridableOption('-eos', + '--eos-parameters', + type=types.DataParamType(sub_classes=('aiida.data:dict',)), + help='Dict containing wf parameters given to the sub EOS workchains.') + +RELAX_PARAMETERS = OverridableOption('-relax', + '--relax-parameters', + type=types.DataParamType(sub_classes=('aiida.data:dict',)), + help='Dict containing wf parameters given to the sub relax workchains.') + +OPTION_NODE = OverridableOption('-opt', '--option-node', type=types.DataParamType(sub_classes=('aiida.data:dict',)), help='Dict, an option node for the workchain.') -MAX_NUM_MACHINES = OverridableOption('-m', +MAX_NUM_MACHINES = OverridableOption('-N', '--max-num-machines', type=click.INT, default=1, show_default=True, help='The maximum number of machines (nodes) to use for the calculations.') -MAX_WALLCLOCK_SECONDS = OverridableOption('-w', +MAX_WALLCLOCK_SECONDS = OverridableOption('-W', '--max-wallclock-seconds', type=click.INT, default=1800, show_default=True, help='The maximum wallclock time in seconds to set for the calculations.') -NUM_MPIPROCS_PER_MACHINE = OverridableOption('-n', +NUM_MPIPROCS_PER_MACHINE = OverridableOption('-M', '--num-mpiprocs-per-machine', type=click.INT, default=12, show_default=True, help='Run the simulation with so many num-mpi-procs-per-machine.') -WITH_MPI = OverridableOption('-i', +WITH_MPI = OverridableOption('-I', '--with-mpi', is_flag=True, default=False, diff --git a/aiida_fleur/workflows/banddos.py b/aiida_fleur/workflows/banddos.py index 50189f727..745f21dd1 100644 --- a/aiida_fleur/workflows/banddos.py +++ b/aiida_fleur/workflows/banddos.py @@ -301,8 +301,8 @@ def return_results(self): efermi_scf = scf_results.fermi_energy bandgap_scf = scf_results.bandgap - efermi_band = last_calc_out_dict['fermi_energy'] - bandgap_band = last_calc_out_dict['bandgap'] + efermi_band = last_calc_out_dict.get('fermi_energy', None) + bandgap_band = last_calc_out_dict.get('bandgap', None) diff_efermi = efermi_scf - efermi_band diff_bandgap = bandgap_scf - bandgap_band diff --git a/aiida_fleur/workflows/corehole.py b/aiida_fleur/workflows/corehole.py index b6ee6391f..ced0a21b6 100644 --- a/aiida_fleur/workflows/corehole.py +++ b/aiida_fleur/workflows/corehole.py @@ -99,8 +99,8 @@ class fleur_corehole_wc(WorkChain): # #'references' : 'calculate',# at some point aiida will have fast forwarding # 'relax' : False, # relax the unit cell first? # 'relax_mode': 'Fleur', # what releaxation do you want - # 'relax_para' : 'default', # parameter dict for the relaxation - # 'scf_para' : 'default', # wf parameter dict for the scfs + # '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 # 'resources' : {"num_machines": 1},# resources per job # 'max_wallclock_seconds' : 6*60*60, # walltime per job @@ -126,61 +126,36 @@ class fleur_corehole_wc(WorkChain): #'import_sys_environment': False, #'environment_variables': {} } + _default_wf_para = { + 'method': 'valence', # what method to use, default for valence to highest open shell + 'hole_charge': 1.0, # what is the charge of the corehole? 0<1.0 + 'atoms': + ['all'], # coreholes on what atoms, positions or index for list, or element ['Be', (0.0, 0.5, 0.334), 3] + 'corelevel': ['all'], # coreholes on which corelevels [ 'Be1s', 'W4f', 'Oall'...] + 'supercell_size': [2, 1, 1], # size of the supercell [nx,ny,nz] + 'para_group': None, # use parameter nodes from a parameter group + #'references' : 'calculate',# at some point aiida will have fast forwarding + #'relax' : False, # relax the unit cell first? + #'relax_mode': 'Fleur', # what releaxation do you want + #'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? + #'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 + } @classmethod def define(cls, spec): super(fleur_corehole_wc, cls).define(spec) - spec.input( - 'wf_parameters', - valid_type=Dict, - required=False, - #default=Dict( - # dict={ - # 'method': - # 'valence', # what method to use, default for valence to highest open shell - # 'hole_charge': 1.0, # what is the charge of the corehole? 0<1.0 - # 'atoms': [ - # 'all' - # ], # coreholes on what atoms, positions or index for list, or element ['Be', (0.0, 0.5, 0.334), 3] - # 'corelevel': ['all' - # ], # coreholes on which corelevels [ 'Be1s', 'W4f', 'Oall'...] - # 'supercell_size': [2, 1, 1], # size of the supercell [nx,ny,nz] - # 'para_group': None, # use parameter nodes from a parameter group - # #'references' : 'calculate',# at some point aiida will have fast forwarding - # #'relax' : False, # relax the unit cell first? - # #'relax_mode': 'Fleur', # what releaxation do you want - # #'relax_para' : 'default', # parameter dict for the relaxation - # 'scf_para': 'default', # 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? - # #'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 - # } - #) - ) + spec.input('wf_parameters', valid_type=Dict, required=False, default=lambda: Dict(dict=cls._default_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=True) spec.input('structure', valid_type=StructureData, required=False) spec.input('calc_parameters', valid_type=Dict, required=False) - spec.input( - 'options', - valid_type=Dict, - required=False #, - #default=Dict( - # dict={ - # 'resources': { - # "num_machines": 1, "num_mpiprocs_per_machine": 1, - # }, - # 'max_wallclock_seconds': 60 * 60, - # 'queue_name': '', - # 'custom_scheduler_commands': '', - # 'import_sys_environment': False, - # 'environment_variables': {} - # } - #) - ) + spec.input('options', valid_type=Dict, required=False) #, default=lambda: Dict(dict=cls._default_options)) + spec.outline( cls.check_input, # first check if input is consistent if_(cls.relaxation_needed)( # ggf relax the given cell @@ -648,8 +623,8 @@ def create_coreholes(self): #pprint('inpxml_changes {}'.format(corehole['inpxml_changes'])) # create_wf para or write in last line what should be in 'fleur_change' # for scf, which with the changes in the inp.xml needed - para = self.ctx.scf_para.copy() # Otherwise inline edit... What about Provenance? TODO check - if para == 'default': + para = self.ctx.scf_para # Otherwise inline edit... What about Provenance? TODO check + if para is None: wf_parameter = {} else: wf_parameter = para @@ -697,7 +672,7 @@ def run_ref_scf(self): self.report('INFO: In run_ref_scf fleur_corehole_wc') print('INFO: In run_ref_scf fleur_corehole_wc') para = self.ctx.scf_para - if para == 'default': + if para is None: wf_parameter = {} else: wf_parameter = para @@ -841,7 +816,7 @@ def run_scfs(self): self.report('INFO: In run_scfs fleur_corehole_wc') print('INFO: In run_scfs fleur_corehole_wc') para = self.ctx.scf_para - if para == 'default': + if para is None: wf_parameter = {} else: wf_parameter = para @@ -1098,7 +1073,9 @@ def create_corehole_result_node(**kwargs): #*args): # 'inpxml_changes' : fleurinp_change} @cf def prepare_struc_corehole_wf( - base_supercell, wf_para, para + base_supercell, + wf_para, + para=None, ): #, _label='prepare_struc_corehole_wf', _description='WF, used in the corehole_wc, breaks the symmetry and moves the cell, prepares the inpgen parameters for a corehole.'): """ calcfunction which does all/some the structure+calcparameter manipulations together @@ -1106,6 +1083,7 @@ def prepare_struc_corehole_wf( wf_para: Dict node dict: {'site' : sites[8], 'kindname' : 'W1', 'econfig': "[Kr] 5s2 4d10 4f13 | 5p6 5d5 6s2", 'fleurinp_change' : []} """ from aiida_fleur.tools.StructureData_util import move_atoms_incell + #from aiida.orm.data.structure import Site wf_para_dict = wf_para.get_dict() @@ -1120,12 +1098,14 @@ def prepare_struc_corehole_wf( npos = -np.array(pos) # break the symmetry, make corehole atoms its own species. # pos has to be tuple, unpack problem here.. #TODO rather not so nice - new_struc, new_para = break_symmetry(base_supercell, - atoms=[], - site=[], - pos=[(pos[0], pos[1], pos[2])], - new_kinds_names=new_kinds_names, - parameterdata=para) + inputs = dict(structure=base_supercell, + atoms=[], + site=[], + pos=[(pos[0], pos[1], pos[2])], + new_kinds_names=new_kinds_names) + if para is not None: + inputs['parameterdata'] = para + new_struc, new_para = break_symmetry(**inputs) #kinds = new_struc.kinds #for kind in kinds: # if kind.name == broke_kn: @@ -1160,11 +1140,12 @@ def extract_results_corehole(calcs): print(calc.exit_status, calc.exit_message) print(calc.get_outgoing().all()) try: - calc_uuids.append(calc.outputs.output_scf_wc_para.get_dict()['last_calc_uuid']) + calc_uuid = calc.outputs.output_scf_wc_para.get_dict()['last_calc_uuid'] except (KeyError, AttributeError): print('continue') continue - #calc_uuids.append(calc['output_scf_wc_para'].get_dict()['last_calc_uuid']) + if calc_uuid is not None: + calc_uuids.append(calc_uuid) #print(calc_uuids) #all_corelevels = {} diff --git a/aiida_fleur/workflows/initial_cls.py b/aiida_fleur/workflows/initial_cls.py index 6a69161a9..69d942076 100644 --- a/aiida_fleur/workflows/initial_cls.py +++ b/aiida_fleur/workflows/initial_cls.py @@ -108,7 +108,7 @@ class fleur_initial_cls_wc(WorkChain): @classmethod def define(cls, spec): super(fleur_initial_cls_wc, cls).define(spec) - spec.input('wf_parameters', valid_type=Dict, required=False, default=Dict(dict=cls._default_wf_para)) + spec.input('wf_parameters', valid_type=Dict, required=False, default=lambda: Dict(dict=cls._default_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) @@ -163,6 +163,7 @@ def check_input(self): self.ctx.bandgaps = {} self.ctx.atomtypes = {} # set values, or defaults for Wf_para + # 'wf_parameters' always there wf_dict = self.inputs.wf_parameters.get_dict() default = self._default_wf_para @@ -174,7 +175,7 @@ def check_input(self): self.ctx.relax_para = wf_dict.get('relax_para', default.get('dos_para')) defaultoptions = self._default_options - if self.inputs.options: + if 'options' in self.inputs: options = self.inputs.options.get_dict() else: options = defaultoptions @@ -774,34 +775,47 @@ def return_results(self): if self.ctx.errors: self.ctx.warnings.append(self.ctx.errors) + material = list(efermi.keys()) + if material: + material = material[0] + fermi_energy = list(efermi.values()) + if fermi_energy: + fermi_energy = fermi_energy[0] + total_energy = list(tE.values()) + if total_energy: + total_energy = total_energy[0] + bandgap = list(gap.values()) + if bandgap: + bandgap = bandgap[0] + outputnode_dict = {} outputnode_dict['workflow_name'] = self.__class__.__name__ outputnode_dict['workflow_version'] = self._workflowversion outputnode_dict['warnings'] = self.ctx.warnings outputnode_dict['successful'] = self.ctx.successful - outputnode_dict['material'] = list(efermi.keys())[0] + outputnode_dict['material'] = material outputnode_dict['corelevel_energies'] = cl outputnode_dict['corelevel_energies_units'] = 'htr' #'eV' outputnode_dict['reference_corelevel_energies'] = ref_cl outputnode_dict['reference_corelevel_energies_units'] = 'htr' #'eV' outputnode_dict['reference_fermi_energy'] = list(ref_efermi.values()) outputnode_dict['reference_fermi_energy_des'] = list(ref_efermi.keys()) - outputnode_dict['fermi_energy'] = list(efermi.values())[0] + outputnode_dict['fermi_energy'] = efermi outputnode_dict['fermi_energy_units'] = 'htr' outputnode_dict['corelevelshifts'] = cls outputnode_dict['corelevelshifts_units'] = 'htr' outputnode_dict['binding_energy_convention'] = 'negativ' #outputnode_dict['coresetup'] = []#cls #outputnode_dict['reference_coresetup'] = []#cls - outputnode_dict['bandgap'] = list(gap.values())[0] + outputnode_dict['bandgap'] = bandgap outputnode_dict['bandgap_units'] = 'htr' outputnode_dict['reference_bandgaps'] = list(ref_gap.values()) outputnode_dict['reference_bandgaps_des'] = list(ref_gap.keys()) outputnode_dict['atomtypes'] = at outputnode_dict['formation_energy'] = formE outputnode_dict['formation_energy_units'] = 'eV/atom' - outputnode_dict['total_energy'] = list(tE.values())[0] + outputnode_dict['total_energy'] = total_energy outputnode_dict['total_energy_units'] = 'eV' outputnode_dict['total_energy_ref'] = list(tE_ref.values()) outputnode_dict['total_energy_ref_des'] = list(tE_ref.keys()) @@ -926,12 +940,14 @@ def extract_results(calcs): for calc in calcs: #print(calc) try: - calc_uuids.append(calc.get_outgoing().get_node_by_label('output_scf_wc_para').get_dict()['last_calc_uuid']) + calc_uuid = calc.get_outgoing().get_node_by_label('output_scf_wc_para').get_dict()['last_calc_uuid'] except (NotExistent, MultipleObjectsError, ValueError, TypeError, KeyError): #TODO which error logmsg = ('ERROR: No output_scf_wc_para node found or no "last_calc_uuid" ' 'key in it for calculation: {}'.format(calc)) log.append(logmsg) continue + if calc_uuid is not None: + calc_uuids.append(calc_uuid) #calc_uuids.append(calc['output_scf_wc_para'].get_dict()['last_calc_uuid']) all_corelevels = {} diff --git a/tests/.aiida/config.json b/tests/.aiida/config.json deleted file mode 100644 index d74fb51db..000000000 --- a/tests/.aiida/config.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "CONFIG_VERSION": { - "CURRENT": 3, - "OLDEST_COMPATIBLE": 3 - }, - "profiles": {} -} diff --git a/tests/cmdline/__init__.py b/tests/cmdline/__init__.py index 713971d59..e69de29bb 100644 --- a/tests/cmdline/__init__.py +++ b/tests/cmdline/__init__.py @@ -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/cmdline/conftest.py b/tests/cmdline/conftest.py new file mode 100644 index 000000000..a60e7aa3a --- /dev/null +++ b/tests/cmdline/conftest.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +# pylint: disable=redefined-outer-name +"""Fixtures for the command line interface. +Most of these fixtures are taken from the aiida-quantum espresso package since they +are needed to mock commands running aiida process +""" +import pytest + + +def mock_launch_process(*_, **__): + """Mock the :meth:`~aiida_fleur.cmdline.util.utils.launch_process` to be a no-op.""" + return + + +@pytest.fixture +def run_cli_command(): + """Run a `click` command with the given options. + + The call will raise if the command triggered an exception or the exit code returned is non-zero. + """ + + def _run_cli_command(command, options=None, raises=None): + """Run the command and check the result. + + :param command: the command to invoke + :param options: the list of command line options to pass to the command invocation + :param raises: optionally an exception class that is expected to be raised + """ + import traceback + from click.testing import CliRunner + + runner = CliRunner() + result = runner.invoke(command, options or []) + + if raises is not None: + assert result.exception is not None, result.output + assert result.exit_code != 0 + else: + assert result.exception is None, ''.join(traceback.format_exception(*result.exc_info)) + assert result.exit_code == 0, result.output + + result.output_lines = [line.strip() for line in result.output.split('\n') if line.strip()] + + return result + + return _run_cli_command + + +@pytest.fixture +def run_cli_process_launch_command(run_cli_command, monkeypatch): + """Run a process launch command with the given options. + + The call will raise if the command triggered an exception or the exit code returned is non-zero. + + :param command: the command to invoke + :param options: the list of command line options to pass to the command invocation + :param raises: optionally an exception class that is expected to be raised + """ + + def _inner(command, options=None, raises=None): + """Run the command and check the result.""" + from aiida_fleur.cmdline.util import utils + monkeypatch.setattr(utils, 'launch_process', mock_launch_process) + return run_cli_command(command, options, raises) + + return _inner diff --git a/tests/cmdline/data/__init__.py b/tests/cmdline/data/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/cmdline/launch/__init__.py b/tests/cmdline/launch/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/cmdline/launch/test_launch.py b/tests/cmdline/launch/test_launch.py new file mode 100644 index 000000000..c89cb4cc7 --- /dev/null +++ b/tests/cmdline/launch/test_launch.py @@ -0,0 +1,171 @@ +# -*- 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/ # +############################################################################### +''' +Module to test all CLI commands to launch for calcjob and workflows of aiida-fleur. + +Comment: while 'launch process' is mocked to do nothing these tests are still quite slow +but execute large parts of the workchain code base. +''' +import os +from aiida.orm import Dict +file_path1 = '../../files/inpxml/FePt/inp.xml' + +inpxmlfilefolder = os.path.dirname(os.path.abspath(__file__)) +FEPT_INPXML_FILE = os.path.abspath(os.path.join(inpxmlfilefolder, file_path1)) + + +def test_launch_inpgen_base(run_cli_process_launch_command, fixture_code): + """Test invoking the inpgen launch command with only required inputs.""" + from aiida_fleur.cmdline.launch import launch_inpgen + + code = fixture_code('fleur.inpgen').store() + options = ['--inpgen', code.uuid] + run_cli_process_launch_command(launch_inpgen, options=options) + + +def test_launch_fleur_base(run_cli_process_launch_command, fixture_code, create_fleurinp): + """Test invoking the fleur launch command with only required inputs.""" + from aiida_fleur.cmdline.launch import launch_fleur + + code = fixture_code('fleur.fleur').store() + fleurinp = create_fleurinp(FEPT_INPXML_FILE).store() + + #Calcjob + options = ['--fleur', code.uuid, '-inp', fleurinp.uuid, '--no-launch_base'] + run_cli_process_launch_command(launch_fleur, options=options) + + #Base_fleur + options = ['--fleur', code.uuid, '-inp', fleurinp.uuid] + run_cli_process_launch_command(launch_fleur, options=options) + + +def test_launch_scf_base(run_cli_process_launch_command, fixture_code, create_fleurinp): + """Test invoking the scf workchain launch command with only required inputs.""" + from aiida_fleur.cmdline.launch import launch_scf + + #Path 1 + inpgen = fixture_code('fleur.inpgen').store() + fleur = fixture_code('fleur.fleur').store() + options = ['--inpgen', inpgen.uuid, '--fleur', fleur.uuid] + run_cli_process_launch_command(launch_scf, options=options) + + #Path2 + fleurinp = create_fleurinp(FEPT_INPXML_FILE).store() + options = ['--fleur', fleur.uuid, '-inp', fleurinp.uuid] + run_cli_process_launch_command(launch_scf, options=options) + + +def test_launch_eos_base(run_cli_process_launch_command, fixture_code): + """Test invoking the eos workchain launch command with only required inputs.""" + from aiida_fleur.cmdline.launch import launch_eos + + inpgen = fixture_code('fleur.inpgen').store() + fleur = fixture_code('fleur.fleur').store() + options = ['--inpgen', inpgen.uuid, '--fleur', fleur.uuid] + run_cli_process_launch_command(launch_eos, options=options) + + +def test_launch_relax_base(run_cli_process_launch_command, fixture_code): + """Test invoking the relax workchain launch command with only required inputs.""" + from aiida_fleur.cmdline.launch import launch_relax + + inpgen = fixture_code('fleur.inpgen').store() + fleur = fixture_code('fleur.fleur').store() + options = ['--inpgen', inpgen.uuid, '--fleur', fleur.uuid] + run_cli_process_launch_command(launch_relax, options=options) + + +def test_launch_corehole_base(run_cli_process_launch_command, fixture_code): + """Test invoking the corehole workchain launch command with only required inputs.""" + from aiida_fleur.cmdline.launch import launch_corehole + + inpgen = fixture_code('fleur.inpgen').store() + fleur = fixture_code('fleur.fleur').store() + options = ['--inpgen', inpgen.uuid, '--fleur', fleur.uuid] + run_cli_process_launch_command(launch_corehole, options=options) + + +def test_launch_init_cls_base(run_cli_process_launch_command, fixture_code): + """Test invoking the initial_cls workchain launch command with only required inputs.""" + from aiida_fleur.cmdline.launch import launch_init_cls + + inpgen = fixture_code('fleur.inpgen').store() + fleur = fixture_code('fleur.fleur').store() + options = ['--inpgen', inpgen.uuid, '--fleur', fleur.uuid] + run_cli_process_launch_command(launch_init_cls, options=options) + + +''' +def test_launch_banddos_base(run_cli_process_launch_command, fixture_code, generate_remote_data, create_fleurinp): + """Test invoking the banddow workchain launch command with only required inputs.""" + from aiida_fleur.cmdline.launch import launch_banddos + + inpgen = fixture_code('fleur.inpgen').store() + fleur = fixture_code('fleur.fleur').store() + path = os.path.abspath(os.path.join(inpxmlfilefolder, '../../files/outxml/tmp')) + remote = generate_remote_data(fleur.computer, path).store() + fleurinp = create_fleurinp(FEPT_INPXML_FILE).store() + + options = ['--fleur', fleur.uuid, '-P', remote.uuid, '-inp', fleurinp.uuid] + run_cli_process_launch_command(launch_banddos, options=options) +''' + + +def test_launch_mae_base(run_cli_process_launch_command, fixture_code): + """Test invoking the mae workchain launch command with only required inputs.""" + from aiida_fleur.cmdline.launch import launch_mae + + inpgen = fixture_code('fleur.inpgen').store() + fleur = fixture_code('fleur.fleur').store() + + wf_para = { + 'lattice': 'fcc', + 'miller': [[-1, 1, 0], [0, 0, 1], [1, 1, 0]], + 'host_symbol': 'Pt', + 'latticeconstant': 4.0, + 'size': (1, 1, 5), + 'replacements': { + 0: 'Fe', + -1: 'Fe' + }, + 'decimals': 10, + 'pop_last_layers': 1, + 'total_number_layers': 8, + 'num_relaxed_layers': 3 + } + + wf_para = Dict(dict=wf_para).store() + options = ['--inpgen', inpgen.uuid, '--fleur', fleur.uuid, '-wf', wf_para.uuid] + run_cli_process_launch_command(launch_mae, options=options) + + +def test_launch_create_magnetic_base(run_cli_process_launch_command, fixture_code): + """Test invoking the mae workchain launch command with only required inputs.""" + from aiida_fleur.cmdline.launch import launch_create_magnetic + + inpgen = fixture_code('fleur.inpgen').store() + fleur = fixture_code('fleur.fleur').store() + + wf_para = { + 'sqa_ref': [0.7, 0.7], + 'use_soc_ref': False, + 'sqas_theta': [0.0, 1.57079, 1.57079], + 'sqas_phi': [0.0, 0.0, 1.57079], + 'serial': False, + 'soc_off': [], + 'inpxml_changes': [], + } + + wf_para = Dict(dict=wf_para).store() + + options = ['--inpgen', inpgen.uuid, '--fleur', fleur.uuid, '-wf', wf_para.uuid] + run_cli_process_launch_command(launch_create_magnetic, options=options) diff --git a/tests/cmdline/test_base_all_commands.py b/tests/cmdline/test_base_all_commands.py new file mode 100644 index 000000000..a3911bf0f --- /dev/null +++ b/tests/cmdline/test_base_all_commands.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +"""Tests if all available CLI commands can print help.""" +from click import Context, Group +from aiida_fleur.cmdline import cmd_root + + +def test_base_all_commands(): + """Test that all commands in ``cmd_root`` are reachable and can print the help message. + + This doesn't guarantee that the command works but at least that it can be successfully called and there are no + import errors or other basic problems. + """ + + def recursively_print_help(ctx): + assert isinstance(ctx.get_help(), str) + + if isinstance(ctx.command, Group): + for subcommand in ctx.command.commands.values(): + ctx.command = subcommand + recursively_print_help(ctx) + + ctx = Context(cmd_root) + recursively_print_help(ctx) diff --git a/tests/cmdline/visualization/__init__.py b/tests/cmdline/visualization/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/cmdline/visualization/test_plot.py b/tests/cmdline/visualization/test_plot.py new file mode 100644 index 000000000..10c0db58e --- /dev/null +++ b/tests/cmdline/visualization/test_plot.py @@ -0,0 +1,15 @@ +# -*- 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/ # +############################################################################### +''' +Module to test the plot cmd from the commandline +''' +from aiida_fleur.cmdline.visualization import cmd_plot diff --git a/tests/cmdline/workflows/__init__.py b/tests/cmdline/workflows/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/conftest.py b/tests/conftest.py index 08c6c6bfa..6aaf061e8 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -50,6 +50,7 @@ def fixture_code(fixture_localhost): """Return a `Code` instance configured to run calculations of given entry point on localhost `Computer`.""" def _fixture_code(entry_point_name): + return Code(input_plugin_name=entry_point_name, remote_computer_exec=[fixture_localhost, '/bin/ls']) return _fixture_code diff --git a/tests/workflows_regression_manually.py b/tests/workflows_regression_manually.py new file mode 100644 index 000000000..b8b73cc3c --- /dev/null +++ b/tests/workflows_regression_manually.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- + +# this should be executed at least once before a release +# It runs all launch commands form the aiida-fleur cmdline once +# with defaults for a given inpgen or fleur code or it use + +#aiida-fleur launch inpgen +#aiida-fleur launch fleur +#aiida-fleur launch scf +#aiida-fleur launch eos +#aiida-fleur launch banddos +#aiida-fleur launch relax +#aiida-fleur launch corehole +#aiida-fleur launch init_cls From 985cf826323a24faf8c6c014293de8dafb47abe6 Mon Sep 17 00:00:00 2001 From: broeder-j Date: Mon, 23 Nov 2020 09:13:10 +0100 Subject: [PATCH 08/53] Make cmdline work but no bash completion and cyclic imports --- aiida_fleur/cmdline/__init__.py | 10 ++++++---- aiida_fleur/cmdline/data/__init__.py | 8 +++++--- aiida_fleur/cmdline/workflows/__init__.py | 14 ++------------ 3 files changed, 13 insertions(+), 19 deletions(-) diff --git a/aiida_fleur/cmdline/__init__.py b/aiida_fleur/cmdline/__init__.py index 4a17e100f..057cdfd22 100755 --- a/aiida_fleur/cmdline/__init__.py +++ b/aiida_fleur/cmdline/__init__.py @@ -20,6 +20,8 @@ # Activate the completion of parameter types provided by the click_completion package click_completion.init() +# for bash use eval "$(_AIIDA_FLEUR_COMPLETE=source_bash aiida-fleur)" + # Instead of using entrypoints and directly injecting verdi commands into aiida-core # we created our own separete CLI because verdi will prob change and become # less material science specific @@ -31,7 +33,7 @@ def cmd_root(profile): # pylint: disable=unused-argument """CLI for the `aiida-fleur` plugin.""" -#from .launch import cmd_launch -#from .data import cmd_structure, cmd_fleurinp -#from .workflows import cmd_workflow -#from .visualization import cmd_plot +from .launch import cmd_launch +from .data import cmd_data +from .workflows import cmd_workflow +from .visualization import cmd_plot diff --git a/aiida_fleur/cmdline/data/__init__.py b/aiida_fleur/cmdline/data/__init__.py index 82b61a245..f7a44857d 100755 --- a/aiida_fleur/cmdline/data/__init__.py +++ b/aiida_fleur/cmdline/data/__init__.py @@ -9,6 +9,8 @@ # For further information please visit http://www.flapw.de or # # http://aiida-fleur.readthedocs.io/en/develop/ # ############################################################################### +# pylint: disable=cyclic-import +# ,reimported,unused-import,wrong-import-position """ Module with CLI commands for various data types. """ @@ -24,6 +26,6 @@ def cmd_data(): cmd_root.add_command(cmd_data) # Import the sub commands to register them with the CLI -#from .structure import cmd_structure -#from .parameters import cmd_parameter -#from .fleurinp import cmd_fleurinp +from .structure import cmd_structure +from .parameters import cmd_parameter +from .fleurinp import cmd_fleurinp diff --git a/aiida_fleur/cmdline/workflows/__init__.py b/aiida_fleur/cmdline/workflows/__init__.py index 6627948b3..e2ef6e4c6 100755 --- a/aiida_fleur/cmdline/workflows/__init__.py +++ b/aiida_fleur/cmdline/workflows/__init__.py @@ -9,6 +9,8 @@ # For further information please visit http://www.flapw.de or # # http://aiida-fleur.readthedocs.io/en/develop/ # ############################################################################### +# pylint: disable=cyclic-import +# ,reimported,unused-import,wrong-import-position """ Module with CLI commands to launch and inspect various aiida-fleur workchains. """ @@ -24,18 +26,6 @@ def cmd_workflow(): """Commands to inspect aiida-fleur workchains.""" -# Import the sub commands to register them with the CLI -#from .scf import cmd_scf -#from .relax import cmd_relax -#from .banddos import cmd_banddos -#from .eos import cmd_eos -#from .corehole import cmd_corehole -#from .initial_cls import cmd_initial_cls - -#from .dmi import cmd_dmi -#from .mae import cmd_mae -#from .ssdisp import cmd_ssdisp - # general further commands for fleur workchains From 84d5a8c736495c838953e467f966ba3a574a9d60 Mon Sep 17 00:00:00 2001 From: broeder-j Date: Mon, 23 Nov 2020 09:30:41 +0100 Subject: [PATCH 09/53] Get rid of cyclic imports in cmdline Bash completion is still not working --- aiida_fleur/cmdline/__init__.py | 18 +++++++---- aiida_fleur/cmdline/data/__init__.py | 3 +- aiida_fleur/cmdline/launch/__init__.py | 31 +++++++++++++++++-- aiida_fleur/cmdline/launch/launch.py | 29 +++++++++-------- aiida_fleur/cmdline/visualization/__init__.py | 7 ++--- aiida_fleur/cmdline/workflows/__init__.py | 5 +-- 6 files changed, 59 insertions(+), 34 deletions(-) diff --git a/aiida_fleur/cmdline/__init__.py b/aiida_fleur/cmdline/__init__.py index 057cdfd22..51b63b733 100755 --- a/aiida_fleur/cmdline/__init__.py +++ b/aiida_fleur/cmdline/__init__.py @@ -16,12 +16,15 @@ import click_completion 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 # Activate the completion of parameter types provided by the click_completion package +# for bash: eval "$(_AIIDA_FLEUR_COMPLETE=source_bash aiida-fleur)" click_completion.init() -# for bash use eval "$(_AIIDA_FLEUR_COMPLETE=source_bash aiida-fleur)" - # Instead of using entrypoints and directly injecting verdi commands into aiida-core # we created our own separete CLI because verdi will prob change and become # less material science specific @@ -33,7 +36,10 @@ def cmd_root(profile): # pylint: disable=unused-argument """CLI for the `aiida-fleur` plugin.""" -from .launch import cmd_launch -from .data import cmd_data -from .workflows import cmd_workflow -from .visualization import cmd_plot +# To avoid circular imports all commands are not yet connected to the root +# but they have to be here because of bash completion + +cmd_root.add_command(cmd_launch) +cmd_root.add_command(cmd_data) +cmd_root.add_command(cmd_workflow) +cmd_root.add_command(cmd_plot) diff --git a/aiida_fleur/cmdline/data/__init__.py b/aiida_fleur/cmdline/data/__init__.py index f7a44857d..6f44a8666 100755 --- a/aiida_fleur/cmdline/data/__init__.py +++ b/aiida_fleur/cmdline/data/__init__.py @@ -15,7 +15,6 @@ Module with CLI commands for various data types. """ import click -from .. import cmd_root @click.group('data') @@ -23,7 +22,7 @@ def cmd_data(): """Commands to create and inspect data nodes.""" -cmd_root.add_command(cmd_data) +#cmd_root.add_command(cmd_data) # Import the sub commands to register them with the CLI from .structure import cmd_structure diff --git a/aiida_fleur/cmdline/launch/__init__.py b/aiida_fleur/cmdline/launch/__init__.py index 740e86821..c8e32e054 100644 --- a/aiida_fleur/cmdline/launch/__init__.py +++ b/aiida_fleur/cmdline/launch/__init__.py @@ -12,12 +12,37 @@ ''' Module with CLI commands for calcjob types of aiida-fleur. ''' -from .. import cmd_root +import click +from .launch import launch_inpgen +from .launch import launch_fleur +from .launch import launch_scf +from .launch import launch_banddos +from .launch import launch_relax +from .launch import launch_eos +from .launch import launch_corehole +from .launch import launch_init_cls +from .launch import launch_mae +from .launch import launch_create_magnetic +from .launch import launch_dmi +from .launch import launch_ssdisp -@cmd_root.group('launch') +@click.group('launch') def cmd_launch(): """Commands to launch workflows and calcjobs of aiida-fleur.""" -from .launch import * +# we do it like this and not in short from to avoid cyclic imports +# and get the full bash completion working +cmd_launch.add_command(launch_inpgen) +cmd_launch.add_command(launch_fleur) +cmd_launch.add_command(launch_scf) +cmd_launch.add_command(launch_banddos) +cmd_launch.add_command(launch_relax) +cmd_launch.add_command(launch_eos) +cmd_launch.add_command(launch_corehole) +cmd_launch.add_command(launch_init_cls) +cmd_launch.add_command(launch_mae) +cmd_launch.add_command(launch_create_magnetic) +cmd_launch.add_command(launch_dmi) +cmd_launch.add_command(launch_ssdisp) diff --git a/aiida_fleur/cmdline/launch/launch.py b/aiida_fleur/cmdline/launch/launch.py index d9ae026e8..4320f89af 100755 --- a/aiida_fleur/cmdline/launch/launch.py +++ b/aiida_fleur/cmdline/launch/launch.py @@ -11,11 +11,10 @@ ############################################################################### ''' Module with CLI commands to launch for calcjob and workflows of aiida-fleur. - -# TODO: these launch commands should be put in seperate files, if this one becomes to large.. ''' +# TODO: these launch commands should be put in separate files, if this one becomes to large.. + import click -from . import cmd_launch from ..util import options from ..util.utils import launch_process from ..util import defaults @@ -25,7 +24,7 @@ from aiida.plugins import CalculationFactory -@cmd_launch.command('inpgen') +@click.command('inpgen') @options.STRUCTURE_OR_FILE(default=defaults.get_si_bulk_structure, show_default=True) @options.INPGEN() @options.CALC_PARAMETERS() @@ -65,7 +64,7 @@ def launch_inpgen(structure, inpgen, calc_parameters, settings, daemon): launch_process(builder, daemon) -@cmd_launch.command('fleur') +@click.command('fleur') @options.FLEURINP() @options.FLEUR() @options.REMOTE() @@ -140,7 +139,7 @@ def launch_fleur(fleurinp, fleur, parent_folder, settings, daemon, max_num_machi launch_process(builder, daemon) -@cmd_launch.command('scf') +@click.command('scf') @options.STRUCTURE_OR_FILE(default=defaults.get_si_bulk_structure, show_default=True) @options.INPGEN() @options.CALC_PARAMETERS() @@ -176,7 +175,7 @@ def launch_scf(structure, inpgen, calc_parameters, fleurinp, fleur, wf_parameter launch_process(builder, daemon) -@cmd_launch.command('relax') +@click.command('relax') @options.STRUCTURE_OR_FILE(default=defaults.get_si_bulk_structure, show_default=True) @options.INPGEN() @options.CALC_PARAMETERS() @@ -211,7 +210,7 @@ def launch_relax(structure, inpgen, calc_parameters, fleur, wf_parameters, scf_p launch_process(builder, daemon) -@cmd_launch.command('eos') +@click.command('eos') @options.STRUCTURE_OR_FILE(default=defaults.get_si_bulk_structure, show_default=True) @options.INPGEN() @options.CALC_PARAMETERS() @@ -243,7 +242,7 @@ def launch_eos(structure, inpgen, calc_parameters, fleur, wf_parameters, scf_par launch_process(builder, daemon) -@cmd_launch.command('banddos') +@click.command('banddos') @options.FLEURINP() @options.FLEUR() @options.WF_PARAMETERS() @@ -269,7 +268,7 @@ def launch_banddos(fleurinp, fleur, wf_parameters, parent_folder, daemon, settin launch_process(builder, daemon) -@cmd_launch.command('init_cls') +@click.command('init_cls') @options.STRUCTURE_OR_FILE(default=defaults.get_si_bulk_structure, show_default=True) @options.INPGEN() @options.CALC_PARAMETERS() @@ -298,7 +297,7 @@ def launch_init_cls(structure, inpgen, calc_parameters, fleurinp, fleur, wf_para launch_process(builder, daemon) -@cmd_launch.command('corehole') +@click.command('corehole') @options.STRUCTURE_OR_FILE(default=defaults.get_si_bulk_structure, show_default=True) @options.INPGEN() @options.CALC_PARAMETERS() @@ -327,7 +326,7 @@ def launch_corehole(structure, inpgen, calc_parameters, fleurinp, fleur, wf_para launch_process(builder, daemon) -@cmd_launch.command('mae') +@click.command('mae') @options.STRUCTURE_OR_FILE(default=defaults.get_fept_film_structure, show_default=True) @options.INPGEN() @options.CALC_PARAMETERS() @@ -367,7 +366,7 @@ def launch_mae(structure, inpgen, calc_parameters, fleurinp, fleur, wf_parameter launch_process(builder, daemon) -@cmd_launch.command('create_magnetic') +@click.command('create_magnetic') @options.INPGEN() @options.CALC_PARAMETERS() @options.FLEUR() @@ -414,7 +413,7 @@ def launch_create_magnetic(inpgen, calc_parameters, fleur, wf_parameters, eos_pa launch_process(builder, daemon) -@cmd_launch.command('ssdisp') +@click.command('ssdisp') def launch_ssdisp(): """ Launch a ssdisp workchain @@ -426,7 +425,7 @@ def launch_ssdisp(): #launch_process(builder, daemon) -@cmd_launch.command('dmi') +@click.command('dmi') def launch_dmi(): """ Launch a dmi workchain diff --git a/aiida_fleur/cmdline/visualization/__init__.py b/aiida_fleur/cmdline/visualization/__init__.py index 19df9087c..30654e4ca 100755 --- a/aiida_fleur/cmdline/visualization/__init__.py +++ b/aiida_fleur/cmdline/visualization/__init__.py @@ -9,18 +9,17 @@ # For further information please visit http://www.flapw.de or # # http://aiida-fleur.readthedocs.io/en/develop/ # ############################################################################### -# pylint: disable=cyclic-import -# ,reimported,unused-import,wrong-import-position """ Module with CLI commands for various visualizations of data types. """ +# if in the future there are futher sub commands of plot, the current plot +# command can become plot_fleur import click from aiida.cmdline.utils import decorators from aiida.cmdline.params import arguments -from .. import cmd_root -@cmd_root.command('plot') +@click.command('plot') @arguments.NODES('nodes') # help='The pks for the nodes to be parsed to plot_fleur') # type=click.Path(exists=True)) diff --git a/aiida_fleur/cmdline/workflows/__init__.py b/aiida_fleur/cmdline/workflows/__init__.py index e2ef6e4c6..eb0802bb9 100755 --- a/aiida_fleur/cmdline/workflows/__init__.py +++ b/aiida_fleur/cmdline/workflows/__init__.py @@ -9,19 +9,16 @@ # For further information please visit http://www.flapw.de or # # http://aiida-fleur.readthedocs.io/en/develop/ # ############################################################################### -# pylint: disable=cyclic-import -# ,reimported,unused-import,wrong-import-position """ Module with CLI commands to launch and inspect various aiida-fleur workchains. """ import click -from .. import cmd_root from aiida.cmdline.params.types import ProcessParamType from aiida.cmdline.params import arguments, options from aiida.cmdline.utils import decorators, echo -@cmd_root.group('workflow') +@click.group('workflow') def cmd_workflow(): """Commands to inspect aiida-fleur workchains.""" From 70b5afe2192c745b594ed4e4d2accca74e5fa10a Mon Sep 17 00:00:00 2001 From: broeder-j Date: Mon, 23 Nov 2020 09:51:42 +0100 Subject: [PATCH 10/53] Add launch cmds for wcs: dmi ssdisp banddos --- .gitignore | 3 +- aiida_fleur/cmdline/launch/launch.py | 65 ++++++++++++++++++++++----- aiida_fleur/workflows/banddos.py | 9 +++- tests/cmdline/launch/test_launch.py | 67 +++++++++++++++++++++++++++- 4 files changed, 128 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index f2abc28d4..6c5af7cf9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # Created by https://www.gitignore.io/api/python,linux,macos - +# +.aiida/ submit_test/ ### Python ### diff --git a/aiida_fleur/cmdline/launch/launch.py b/aiida_fleur/cmdline/launch/launch.py index 4320f89af..8e0720729 100755 --- a/aiida_fleur/cmdline/launch/launch.py +++ b/aiida_fleur/cmdline/launch/launch.py @@ -414,24 +414,67 @@ def launch_create_magnetic(inpgen, calc_parameters, fleur, wf_parameters, eos_pa @click.command('ssdisp') -def launch_ssdisp(): +@options.STRUCTURE_OR_FILE(default=defaults.get_fept_film_structure, show_default=True) +@options.INPGEN() +@options.CALC_PARAMETERS() +@options.FLEUR() +@options.WF_PARAMETERS(required=True) +@options.SCF_PARAMETERS() +@options.DAEMON() +@options.OPTION_NODE() +def launch_ssdisp(structure, inpgen, calc_parameters, fleur, wf_parameters, scf_parameters, daemon, option_node): """ Launch a ssdisp workchain """ - click.echo('Not implemented yet, sorry. Please implement me!') - #workchain_class = WorkflowFactory('fleur.ssdisp') - #inputs = {} - #builder = workchain_class.get_builder(code=load_node(fleur),**inputs) - #launch_process(builder, daemon) + workchain_class = WorkflowFactory('fleur.ssdisp') + inputs = { + 'scf': { + 'wf_parameters': scf_parameters, + 'structure': structure, + 'calc_parameters': calc_parameters, + 'options': option_node, + 'inpgen': inpgen, + 'fleur': fleur + }, + 'wf_parameters': wf_parameters, + 'fleur': fleur, + 'options': option_node + } + inputs = clean_nones(inputs) + builder = workchain_class.get_builder() + builder.update(inputs) + launch_process(builder, daemon) @click.command('dmi') -def launch_dmi(): +@options.STRUCTURE_OR_FILE(default=defaults.get_fept_film_structure, show_default=True) +@options.INPGEN() +@options.CALC_PARAMETERS() +@options.FLEUR() +@options.WF_PARAMETERS(required=True) +@options.SCF_PARAMETERS() +@options.DAEMON() +@options.OPTION_NODE() +def launch_dmi(structure, inpgen, calc_parameters, fleur, wf_parameters, scf_parameters, daemon, option_node): """ Launch a dmi workchain """ click.echo('Not implemented yet, sorry. Please implement me!') - #workchain_class = WorkflowFactory('fleur.dmi') - #inputs = {} - #builder = workchain_class.get_builder(code=load_node(fleur),**inputs) - #launch_process(builder, daemon) + workchain_class = WorkflowFactory('fleur.dmi') + inputs = { + 'scf': { + 'wf_parameters': scf_parameters, + 'structure': structure, + 'calc_parameters': calc_parameters, + 'options': option_node, + 'inpgen': inpgen, + 'fleur': fleur + }, + 'wf_parameters': wf_parameters, + 'fleur': fleur, + 'options': option_node + } + inputs = clean_nones(inputs) + builder = workchain_class.get_builder() + builder.update(inputs) + launch_process(builder, daemon) diff --git a/aiida_fleur/workflows/banddos.py b/aiida_fleur/workflows/banddos.py index 745f21dd1..8d098a86c 100644 --- a/aiida_fleur/workflows/banddos.py +++ b/aiida_fleur/workflows/banddos.py @@ -304,8 +304,13 @@ def return_results(self): efermi_band = last_calc_out_dict.get('fermi_energy', None) bandgap_band = last_calc_out_dict.get('bandgap', None) - diff_efermi = efermi_scf - efermi_band - diff_bandgap = bandgap_scf - bandgap_band + diff_efermi = None + if efermi_band is not None: + diff_efermi = efermi_scf - efermi_band + + diff_bandgap = None + if bandgap_band is not None: + diff_bandgap = bandgap_scf - bandgap_band outputnode_dict = {} diff --git a/tests/cmdline/launch/test_launch.py b/tests/cmdline/launch/test_launch.py index c89cb4cc7..8c3254702 100644 --- a/tests/cmdline/launch/test_launch.py +++ b/tests/cmdline/launch/test_launch.py @@ -104,7 +104,6 @@ def test_launch_init_cls_base(run_cli_process_launch_command, fixture_code): run_cli_process_launch_command(launch_init_cls, options=options) -''' def test_launch_banddos_base(run_cli_process_launch_command, fixture_code, generate_remote_data, create_fleurinp): """Test invoking the banddow workchain launch command with only required inputs.""" from aiida_fleur.cmdline.launch import launch_banddos @@ -117,7 +116,6 @@ def test_launch_banddos_base(run_cli_process_launch_command, fixture_code, gener options = ['--fleur', fleur.uuid, '-P', remote.uuid, '-inp', fleurinp.uuid] run_cli_process_launch_command(launch_banddos, options=options) -''' def test_launch_mae_base(run_cli_process_launch_command, fixture_code): @@ -169,3 +167,68 @@ def test_launch_create_magnetic_base(run_cli_process_launch_command, fixture_cod options = ['--inpgen', inpgen.uuid, '--fleur', fleur.uuid, '-wf', wf_para.uuid] run_cli_process_launch_command(launch_create_magnetic, options=options) + + +def test_launch_dmi_base(run_cli_process_launch_command, fixture_code): + """Test invoking the mae workchain launch command with only required inputs.""" + from aiida_fleur.cmdline.launch import launch_dmi + + inpgen = fixture_code('fleur.inpgen').store() + fleur = fixture_code('fleur.fleur').store() + + wf_para = { + 'serial': False, + 'beta': { + '123': 1.57079 + }, + 'sqas_theta': [0.0, 1.57079, 1.57079], + 'sqas_phi': [0.0, 0.0, 1.57079], + 'soc_off': [], + # 'prop_dir': [0.125, 0.15, 0.24], + 'q_vectors': [[0.0, 0.0, 0.0], [0.1, 0.1, 0.0]], + 'ref_qss': [0.0, 0.0, 0.0], + 'inpxml_changes': [] + } + + wf_para = Dict(dict=wf_para).store() + options = ['--inpgen', inpgen.uuid, '--fleur', fleur.uuid, '-wf', wf_para.uuid] + run_cli_process_launch_command(launch_dmi, options=options) + + +def test_launch_ssdisp_base(run_cli_process_launch_command, fixture_code): + """Test invoking the mae workchain launch command with only required inputs.""" + from aiida_fleur.cmdline.launch import launch_ssdisp + + inpgen = fixture_code('fleur.inpgen').store() + fleur = fixture_code('fleur.fleur').store() + + wf_para = { + 'lattice': 'fcc', + 'miller': [[-1, 1, 0], [0, 0, 1], [1, 1, 0]], + 'host_symbol': 'Pt', + 'latticeconstant': 4.0, + 'size': (1, 1, 5), + 'replacements': { + 0: 'Fe', + -1: 'Fe' + }, + 'decimals': 10, + 'pop_last_layers': 1, + 'total_number_layers': 8, + 'num_relaxed_layers': 3 + } + + wf_para = { + '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': [] + } + + wf_para = Dict(dict=wf_para).store() + options = ['--inpgen', inpgen.uuid, '--fleur', fleur.uuid, '-wf', wf_para.uuid] + run_cli_process_launch_command(launch_ssdisp, options=options) From c6b133d7f1b46b28f6019d00e0e6a1a7884ab7ec Mon Sep 17 00:00:00 2001 From: broeder-j Date: Mon, 23 Nov 2020 13:24:48 +0100 Subject: [PATCH 11/53] Update README badges, since were outdated --- README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 76f80fb5e..bbb3ec0a9 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,8 @@ [![MIT license](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![GitHub release](https://img.shields.io/github/release/JuDFTteam/aiida-fleur.svg)](https://github.com/JuDFTteam/aiida-fleur/releases) [![PyPI version](https://badge.fury.io/py/aiida-fleur.svg)](https://badge.fury.io/py/aiida-fleur) -[![Build develop](https://travis-ci.org/JuDFTteam/aiida-fleur.svg?branch=master)](https://travis-ci.org/JuDFTteam/aiida-fleur) -[![Coveralls github branch](https://github.com/JuDFTteam/aiida-fleur/blob/develop/aiida_fleur/tests/coverage.svg)](https://github.com/JuDFTteam/aiida-fleur/tree/develop) -[![Code quality pylint](https://github.com/JuDFTteam/aiida-fleur/blob/develop/aiida_fleur/tests/pylint.svg)](https://github.com/JuDFTteam/aiida-fleur/tree/develop) +[![PyPI pyversions](https://img.shields.io/pypi/pyversions/aiida-fleur.svg)](https://pypi.python.org/pypi/aiida-fleur) +[![Build status](https://github.com/JuDFTteam/aiida-fleur/workflows/aiida-fleur/badge.svg?branch=develop&event=push)](https://github.com/JuDFTteam/aiida-fleur/actions) [![Documentation Status](https://readthedocs.org/projects/aiida-fleur/badge/?version=develop)](https://aiida-fleur.readthedocs.io/en/develop/?badge=develop) @@ -14,6 +13,12 @@ DFT [FLEUR code](http://www.flapw.de) with the [AiiDA framework](http://www.aiid Developed at [Forschungszentrum Jülich GmbH](http://www.fz-juelich.de/pgi/pgi-1/DE/Home/home_node.html) +## Compatibility matrix + +| FLEUR Plugin | AiiDA CORE | Python | FLEUR | +|-|-|-|-| +| `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 From 0a2715aca9541ea43f2f3010a493f4735273378d Mon Sep 17 00:00:00 2001 From: broeder-j Date: Mon, 23 Nov 2020 14:43:36 +0100 Subject: [PATCH 12/53] Update README CLI, auto create and upload codecov report Also CLI bashcompletion works, evaluated wrong command --- .github/workflows/ci.yml | 5 ++++ README.md | 48 +++++++++++++++++++++++++++++++-- aiida_fleur/cmdline/__init__.py | 2 +- tests/codecov.yml | 26 ++++++++++++++++++ tests/coverage.svg | 21 --------------- tests/run_all_cov.sh | 2 +- tests/run_all_cov_show.sh | 14 ++++++++++ 7 files changed, 93 insertions(+), 25 deletions(-) create mode 100644 tests/codecov.yml delete mode 100644 tests/coverage.svg create mode 100755 tests/run_all_cov_show.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index da3c451f4..a404b15ee 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -120,3 +120,8 @@ jobs: touch local_exe/inpgen && chmod +x local_exe/inpgen touch local_exe/fleur && chmod +x local_exe/fleur ./run_all_cov.sh + + - name: Upload report to Codecov + uses: codecov/codecov-action@v1.0.15 + with: + file: ./coverage.xml diff --git a/README.md b/README.md index bbb3ec0a9..a445d85c1 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ [![PyPI pyversions](https://img.shields.io/pypi/pyversions/aiida-fleur.svg)](https://pypi.python.org/pypi/aiida-fleur) [![Build status](https://github.com/JuDFTteam/aiida-fleur/workflows/aiida-fleur/badge.svg?branch=develop&event=push)](https://github.com/JuDFTteam/aiida-fleur/actions) [![Documentation Status](https://readthedocs.org/projects/aiida-fleur/badge/?version=develop)](https://aiida-fleur.readthedocs.io/en/develop/?badge=develop) +[![codecov](https://codecov.io/gh/JuDFTteam/aiida-fleur/branch/develop/graph/badge.svg)](https://codecov.io/gh/JuDFTteam/aiida-fleur) This software contains a plugin that enables the usage of the all-electron @@ -42,7 +43,7 @@ In Extreme Data Workshop 2018 Proceedings, 2019, vol 40, p 43-48 ### Comments/Disclaimer: -The plug-in and the workflows will only work with a Fleur version using xml files as I/O. +The plug-in and the workflows will only work with a Fleur version using xml files as I/O, i.e >v0.27. ### Contents @@ -99,6 +100,49 @@ read_cif.py | This can be used as stand-alone to create StructureData nodes from Utility and tools, which are independend of AiiDA are moved to the [masci-tools](https://github.com/JuDFTteam/masci-tools) (material science tools) repository, which is a dependency of aiida-fleur. + +### Command line interface (CLI) + +Besides the python API, aiida-fleur comes with a builtin CLI: `aiida-fleur`. +This interface is built using the click library and supports tab-completion. + +To enable tab-completion, add the following to your shell loading script, e.g. the .bashrc or virtual environment activate script: + + eval "$(_AIIDA_FLEUR_COMPLETE=source aiida-fleur)" + +the main subcommands include: + + data: Commands to create and inspect data nodes + 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. + launch: Commands to launch workflows and calcjobs of aiida-fleur + + 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 + + plot: Invoke the plot_fleur command on given nodes + + workflow: Commands to inspect aiida-fleur workchains and prepare inputs + +for example to launch an scf workchain on a given structure execute: + + $ 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: + + $ aiida-fleur launch scf -i -f -S ./structure/Cu.cif + ## Installation Instructions From the aiida-fleur folder (after downloading the code, recommended) use: @@ -159,7 +203,7 @@ Mainly AiiDA: Easy plotting and other useful routines that do not depend on aiida_core are part of the [masci-tools](https://github.com/JuDFTteam/masci-tools) (material science tools) repository. -For easy ploting we recommend using 'plot_methods' from masci-tools, which are also deployed by the 'plot_fleur()' function. +For easy plotting we recommend using 'plot_methods' from masci-tools, which are also deployed by the 'plot_fleur()' function. ## Further Information diff --git a/aiida_fleur/cmdline/__init__.py b/aiida_fleur/cmdline/__init__.py index 51b63b733..05ed9c8e8 100755 --- a/aiida_fleur/cmdline/__init__.py +++ b/aiida_fleur/cmdline/__init__.py @@ -22,7 +22,7 @@ from .visualization import cmd_plot # Activate the completion of parameter types provided by the click_completion package -# for bash: eval "$(_AIIDA_FLEUR_COMPLETE=source_bash aiida-fleur)" +# for bash: eval "$(_AIIDA_FLEUR_COMPLETE=source aiida-fleur)" click_completion.init() # Instead of using entrypoints and directly injecting verdi commands into aiida-core diff --git a/tests/codecov.yml b/tests/codecov.yml new file mode 100644 index 000000000..488230c9e --- /dev/null +++ b/tests/codecov.yml @@ -0,0 +1,26 @@ +codecov: + notify: + require_ci_to_pass: yes + +coverage: + precision: 2 + round: down + range: "0...100" + + status: + project: yes + patch: yes + changes: yes + +parsers: + gcov: + branch_detection: + conditional: yes + loop: yes + method: no + macro: no + +comment: + layout: "header, diff" + behavior: default + require_changes: no diff --git a/tests/coverage.svg b/tests/coverage.svg deleted file mode 100644 index f86374a02..000000000 --- a/tests/coverage.svg +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - coverage - coverage - 43% - 43% - - diff --git a/tests/run_all_cov.sh b/tests/run_all_cov.sh index 59dcd37f3..9f40c5792 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=term-missing:skip-covered --cov=aiida_fleur +pytest --cov-report=xml --cov=aiida_fleur #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 new file mode 100755 index 000000000..59dcd37f3 --- /dev/null +++ b/tests/run_all_cov_show.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env sh +export AIIDA_PATH='.'; +mkdir -p '.aiida'; +#pytest -sv +#pytest -v +pytest --cov-report=term-missing:skip-covered --cov=aiida_fleur +#pytest --cov-report=html --cov=aiida_fleur +#pytest --cov-report=html --cov=aiida_fleur -vv -rXxs -x + +# to create badge (requires coverage-badge) +#coverage-badge -o coverage.svg + +# pylint (for shield create, by hand, or write script to write total into svg) +# pylint ../../aiida_fleur/ > outlint From 4978b8a9772f23dd147dc8e60b743aaede47e32d Mon Sep 17 00:00:00 2001 From: broeder-j Date: Mon, 23 Nov 2020 15:07:06 +0100 Subject: [PATCH 13/53] Correct cov report location delete yml and pylint badge --- .github/workflows/ci.yml | 2 +- tests/.coveragerc | 6 ------ tests/codecov.yml | 26 -------------------------- tests/pylint.svg | 20 -------------------- 4 files changed, 1 insertion(+), 53 deletions(-) delete mode 100644 tests/.coveragerc delete mode 100644 tests/codecov.yml delete mode 100644 tests/pylint.svg diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a404b15ee..b6ea4063a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -124,4 +124,4 @@ jobs: - name: Upload report to Codecov uses: codecov/codecov-action@v1.0.15 with: - file: ./coverage.xml + file: ./tests/coverage.xml diff --git a/tests/.coveragerc b/tests/.coveragerc deleted file mode 100644 index eb3500f32..000000000 --- a/tests/.coveragerc +++ /dev/null @@ -1,6 +0,0 @@ -[run] -source = aiida_fleur -omit = *test* - -[html] -directory = coverage/html diff --git a/tests/codecov.yml b/tests/codecov.yml deleted file mode 100644 index 488230c9e..000000000 --- a/tests/codecov.yml +++ /dev/null @@ -1,26 +0,0 @@ -codecov: - notify: - require_ci_to_pass: yes - -coverage: - precision: 2 - round: down - range: "0...100" - - status: - project: yes - patch: yes - changes: yes - -parsers: - gcov: - branch_detection: - conditional: yes - loop: yes - method: no - macro: no - -comment: - layout: "header, diff" - behavior: default - require_changes: no diff --git a/tests/pylint.svg b/tests/pylint.svg deleted file mode 100644 index 61836ac08..000000000 --- a/tests/pylint.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - pylint - pylint - 6.91 - 7.49 - - From 7fb7a99a3f8d37bbc37d4f7d64931f1129239c6d Mon Sep 17 00:00:00 2001 From: broeder-j Date: Tue, 24 Nov 2020 22:22:55 +0100 Subject: [PATCH 14/53] Cleanup old/legacy workchains, add further cmdline tests --- aiida_fleur/cmdline/data/fleurinp.py | 6 +- aiida_fleur/cmdline/util/types.py | 10 +- aiida_fleur/workflows/b_corehole.py.legacy | 276 --------- aiida_fleur/workflows/band.py | 346 ----------- aiida_fleur/workflows/band2.py.legacy | 270 --------- aiida_fleur/workflows/corelevel.py.legacy | 258 -------- aiida_fleur/workflows/delta.py | 614 -------------------- aiida_fleur/workflows/delta_split.py.legacy | 588 ------------------- tests/cmdline/conftest.py | 7 + tests/cmdline/data/test_fleurinp.py | 52 ++ tests/cmdline/data/test_parameters.py | 42 ++ tests/cmdline/data/test_structure.py | 40 ++ tests/cmdline/test_types.py | 45 ++ tests/conftest.py | 2 +- 14 files changed, 198 insertions(+), 2358 deletions(-) delete mode 100644 aiida_fleur/workflows/b_corehole.py.legacy delete mode 100644 aiida_fleur/workflows/band.py delete mode 100644 aiida_fleur/workflows/band2.py.legacy delete mode 100644 aiida_fleur/workflows/corelevel.py.legacy delete mode 100644 aiida_fleur/workflows/delta.py delete mode 100644 aiida_fleur/workflows/delta_split.py.legacy create mode 100644 tests/cmdline/data/test_fleurinp.py create mode 100644 tests/cmdline/data/test_parameters.py create mode 100644 tests/cmdline/data/test_structure.py create mode 100644 tests/cmdline/test_types.py diff --git a/aiida_fleur/cmdline/data/fleurinp.py b/aiida_fleur/cmdline/data/fleurinp.py index d6c871143..23ae0648d 100755 --- a/aiida_fleur/cmdline/data/fleurinp.py +++ b/aiida_fleur/cmdline/data/fleurinp.py @@ -125,13 +125,10 @@ def cat_file(node, filename): Dumb the content of a file contained in given fleurinpdata, per default dump inp.xml """ - #if filename is None: - # filename = 'inp.xml' echo.echo(node.get_content(filename=filename)) - #click.echo('Not implemented yet, sorry. Please implement me!') - +''' @cmd_fleurinp.command('info') def info(): """ @@ -182,3 +179,4 @@ def get_parameters(): parameter data uuid/pk """ click.echo('Not implemented yet, sorry. Please implement me!') +''' diff --git a/aiida_fleur/cmdline/util/types.py b/aiida_fleur/cmdline/util/types.py index cf57aaeb1..a4eeff1f4 100644 --- a/aiida_fleur/cmdline/util/types.py +++ b/aiida_fleur/cmdline/util/types.py @@ -17,6 +17,7 @@ from aiida.cmdline.utils import echo from aiida.common.exceptions import NotExistent from aiida.plugins import DataFactory +from aiida.cmdline.utils.decorators import with_dbenv class StructureNodeOrFileParamType(click.ParamType): @@ -30,10 +31,12 @@ class StructureNodeOrFileParamType(click.ParamType): name = 'StructureFile' + @with_dbenv() def convert(self, value, param, ctx): is_path = False # Alternative one could check if int or uuid # aiida allows also for shorten uuids + from aiida.orm import StructureData, QueryBuilder try: structure = types.DataParamType(sub_classes=('aiida.data:structure',)).convert(value, param, ctx) @@ -43,7 +46,6 @@ def convert(self, value, param, ctx): is_path = True if is_path: - StructureData = DataFactory('structure') # If it is a path to a file try to convert the structure pathtype = click.Path(exists=True, dir_okay=False, resolve_path=True) filename = pathtype.convert(value, param, ctx) @@ -59,4 +61,10 @@ def convert(self, value, param, ctx): echo.echo_critical(str(err)) # do not store structure, since this option is for calculation and workflow # input, which will store the structure anyway. + + # do not store again if structure is already there. + duplicate = QueryBuilder().append(StructureData, filters={'extras._aiida_hash': structure._get_hash()}).first() # pylint: disable=protected-access + + if duplicate: + return duplicate[0] return structure diff --git a/aiida_fleur/workflows/b_corehole.py.legacy b/aiida_fleur/workflows/b_corehole.py.legacy deleted file mode 100644 index 2171cc0b0..000000000 --- a/aiida_fleur/workflows/b_corehole.py.legacy +++ /dev/null @@ -1,276 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -""" -This is the worklfow 'corehole' using the Fleur code, which calculates Binding -energies and corelevel shifts with different methods. -'divide and conquer' -""" -# TODO alow certain kpoint path, or kpoint node, so far auto -from __future__ import absolute_import -from __future__ import print_function -from aiida_fleur.data.fleurinp import FleurinpData -from aiida import load_dbenv, is_dbenv_loaded -import six -if not is_dbenv_loaded(): - load_dbenv() - -import os.path -from aiida.plugins import Code, DataFactory -from aiida.engine import WorkChain -from aiida.engine import submit -from aiida.engine import ToContext -from aiida.engine.process_registry import ProcessRegistry - -from aiida_fleur.calculation.fleur import FleurCalculation -from aiida_fleur.data.fleurinpmodifier import FleurinpModifier -from aiida.engine import while_, if_ -from aiida_fleur.tools.create_corehole import create_corehole - -StructureData = DataFactory('structure') -Dict = DataFactory('dict') -RemoteData = DataFactory('remote') -FleurProcess = FleurCalculation.process() - - -class fleur_corehole_wc(WorkChain): - ''' - Turn key solution for the calculation of core level shift and Binding energies - - - ''' - # wf_Parameters: Dict, - ''' - 'method' : ['initial', 'full_valence ch', 'half_valence_ch', 'ch', ...] - 'Bes' : [W4f, Be1s] - 'CLS' : [W4f, Be1s] - 'atoms' : ['all', 'postions' : []] - 'references' : ['calculate', or - 'scf_para' : {...}, 'default' - 'relax' : True - 'relax_mode': ['Fleur', 'QE Fleur', 'QE'] - 'relax_para' : {...}, 'default' - 'calculate_doses' : False - 'dos_para' : {...}, 'default' - ''' - ''' - # defaults - default wf_Parameters:: - 'method' : 'initial' - 'atoms' : 'all - 'references' : 'calculate' - 'scf_para' : 'default' - 'relax' : True - 'relax_mode': 'QE Fleur' - 'relax_para' : 'default' - 'calculate_doses' : False - 'dos_para' : 'default' - ''' - - _workflowversion = "0.0.1" - - @classmethod - def define(cls, spec): - super(fleur_corehole_wc, cls).define(spec) - spec.input("wf_parameters", valid_type=Dict, required=False, - default=Dict(dict={ - 'method' : 'initial', - 'atoms' : 'all', - 'references' : 'calculate', - 'relax' : False, - 'relax_mode': 'Fleur', - 'relax_para' : 'default', - 'scf_para' : 'default', - })) - spec.input("fleurinp", valid_type=FleurinpData, required=True) - spec.input("fleur", valid_type=Code, required=True) - spec.input("structure", valid_type=StructureData, required=False) - spec.input("calc_parameters", valid_type=Dict, required=False) - spec.outline( - cls.check_input, - if_(cls.relaxation_needed)( - cls.relax), - if_(cls.supercell_needed)( - cls.create_supercell - ), - cls.create_new_fleurinp, - cls.create_coreholes, - cls.run_scfs, - cls.collect_results, - cls.return_results - ) - #spec.dynamic_output() - - - def check_input(self): - ''' - check parameters, what condictions? complete? - check input nodes - ''' - ### input check ### ? or done automaticly, how optional? - # check if fleuinp corresponds to fleur_calc - print(('started bands workflow version {}'.format(self._workflowversion))) - print(("Workchain node identifiers: {}" - "".format(ProcessRegistry().current_calc_node))) - - 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. - """ - print('In relaxation inital_state_CLS workflow') - if self.ctx.relax: - # TODO check all forces of calculations - forces_fine = True - if forces_fine: - return True - else: - return False - else: - return False - - - def relax(self): - """ - Do structural relaxation for certain structures. - """ - print('In relax inital_state_CLS workflow') - for calc in self.ctx.dos_to_calc: - pass - # TODO run relax workflow - - def create_new_fleurinp(self): - """ - create a new fleurinp from the old with certain parameters - """ - # TODO allow change of kpoint mesh?, tria? - wf_dict = self.inputs.wf_parameters.get_dict() - nkpts = wf_dict.get('nkpts', 500) - # how can the user say he want to use the given kpoint mesh, ZZ nkpts : False/0 - sigma = wf_dict.get('sigma', 0.005) - emin = wf_dict.get('emin', -0.30) - emax = wf_dict.get('emax', 0.80) - - fleurmode = FleurinpModifier(self.inputs.fleurinp) - - #change_dict = {'band': True, 'ndir' : -1, 'minEnergy' : self.inputs.wf_parameters.get_dict().get('minEnergy', -0.30000000), - #'maxEnergy' : self.inputs.wf_parameters.get_dict().get('manEnergy','0.80000000'), - #'sigma' : self.inputs.wf_parameters.get_dict().get('sigma', '0.00500000')} - change_dict = {'band': True, 'ndir' : 0, 'minEnergy' : emin, - 'maxEnergy' : emax, 'sigma' : sigma} #'ndir' : 1, 'pot8' : True - - fleurmode.set_inpchanges(change_dict) - - if nkpts: - fleurmode.set_nkpts(count=nkpts) - #fleurinp_new.replace_tag() - - fleurmode.show(validate=True, display=False) # needed? - fleurinp_new = fleurmode.freeze() - self.ctx.fleurinp1 = fleurinp_new - print(fleurinp_new) - #print(fleurinp_new.folder.get_subfolder('path').get_abs_path('')) - - def get_inputs_fleur(self): - ''' - get the input for a FLEUR calc - ''' - inputs = FleurProcess.get_inputs_template() - - fleurin = self.ctx.fleurinp1 - #print fleurin - remote = self.inputs.remote - inputs.parent_folder = remote - inputs.code = self.inputs.fleur - inputs.fleurinpdata = fleurin - - # TODO nkpoints decide n core - - core = 12 # get from computer nodes per machine - inputs._options.resources = {"num_machines": 1, "num_mpiprocs_per_machine" : core} - inputs._options.max_wallclock_seconds = 30 * 60 - - if self.ctx.serial: - inputs._options.withmpi = False # for now - inputs._options.resources = {"num_machines": 1} - - if self.ctx.queue: - inputs._options.queue_name = self.ctx.queue - print(self.ctx.queue) - # if code local use - #if self.inputs.fleur.is_local(): - # inputs._options.computer = computer - # #else use computer from code. - #else: - # inputs._options.queue_name = 'th1' - - if self.ctx.serial: - inputs._options.withmpi = False # for now - inputs._options.resources = {"num_machines": 1} - - return inputs - - def run_fleur(self): - ''' - run a fleur calculation - ''' - FleurProcess = FleurCalculation.process() - inputs = {} - inputs = self.get_inputs_fleur() - #print inputs - future = submit(FleurProcess, **inputs) - print('run Fleur in band workflow') - - return ToContext(last_calc=future) - - def return_results(self): - ''' - return the results of the calculations - ''' - # TODO more here - print('Band workflow Done') - print(('A bandstructure was calculated for fleurinpdata {} and is found under pk={}, ' - 'calculation {}'.format(self.inputs.fleurinp, self.ctx.last_calc.pk, self.ctx.last_calc))) - - #check if band file exists: if not succesful = False - #TODO be careful with general bands.X - - bandfilename = 'bands.1' # ['bands.1', 'bands.2', ...] - # TODO this should be easier... - last_calc_retrieved = self.ctx.last_calc.get_outputs_dict()['retrieved'].folder.get_subfolder('path') - bandfilepath = self.ctx.last_calc.get_outputs_dict()['retrieved'].folder.get_subfolder('path').get_abs_path(bandfilename) - print(bandfilepath) - #bandfilepath = "path to bandfile" # Array? - if os.path.isfile(bandfilepath): - self.ctx.successful = True - else: - bandfilepath = None - print('!NO bandstructure file was found, something went wrong!') - #TODO corret efermi: - # get efermi from last calculation - efermi1 = self.inputs.remote.get_inputs()[-1].res.fermi_energy - #get efermi from this caclulation - efermi2 = self.ctx.last_calc.res.fermi_energy - diff_efermi = efermi1 - efermi2 - # store difference in output node - # adjust difference in band.gnu - #filename = 'gnutest2' - - outputnode_dict ={} - - outputnode_dict['workflow_name'] = self.__class__.__name__ - outputnode_dict['Warnings'] = self.ctx.warnings - outputnode_dict['successful'] = self.ctx.successful - outputnode_dict['diff_efermi'] = diff_efermi - #outputnode_dict['last_calc_pk'] = self.ctx.last_calc.pk - #outputnode_dict['last_calc_uuid'] = self.ctx.last_calc.uuid - outputnode_dict['bandfile'] = bandfilepath - outputnode_dict['last_calc_uuid'] = self.ctx.last_calc.uuid - outputnode_dict['last_calc_retrieved'] = last_calc_retrieved - #print outputnode_dict - outputnode = Dict(dict=outputnode_dict) - outdict = {} - outdict['output_corehole_wc_para'] = outputnode - #print outdict - for k, v in six.iteritems(outdict): - self.out(k, v) diff --git a/aiida_fleur/workflows/band.py b/aiida_fleur/workflows/band.py deleted file mode 100644 index 98733db44..000000000 --- a/aiida_fleur/workflows/band.py +++ /dev/null @@ -1,346 +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 is the worklfow 'band' for the Fleur code, which calculates a -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 aiida.plugins import DataFactory -from aiida.orm import Code, StructureData, Dict, RemoteData -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_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 - - -class FleurBandWorkChain(WorkChain): - ''' - This workflow calculated a bandstructure from a Fleur calculation - - :Params: a Fleurcalculation node - :returns: Success, last result node, list with convergence behavior - ''' - # wf_parameters: { 'tria', 'nkpts', 'sigma', 'emin', 'emax'} - # defaults : tria = True, nkpts = 800, sigma=0.005, emin= , emax = - - _workflowversion = '0.3.4' - - _default_options = { - 'resources': { - 'num_machines': 1, - 'num_mpiprocs_per_machine': 1 - }, - 'max_wallclock_seconds': 60 * 60, - 'queue_name': '', - 'custom_scheduler_commands': '', - 'import_sys_environment': False, - 'environment_variables': {} - } - _wf_default = { - 'fleur_runmax': 4, - 'kpath': 'auto', - # 'nkpts' : 800, - 'sigma': 0.005, - 'emin': -0.50, - 'emax': 0.90 - } - - @classmethod - def define(cls, spec): - super(FleurBandWorkChain, cls).define(spec) - # spec.expose_inputs(FleurScfWorkChain, 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) - spec.input('fleurinp', valid_type=FleurinpData, 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.output('output_band_wc_para', valid_type=Dict) - - 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.') - - def start(self): - ''' - check parameters, what condictions? complete? - check input nodes - ''' - ### input check ### ? or done automaticly, how optional? - # check if fleuinp corresponds to fleur_calc - self.report('started bands workflow version {}'.format(self._workflowversion)) - #print("Workchain node identifiers: ")#'{}' - #"".format(ProcessRegistry().current_calc_node)) - - self.ctx.fleurinp_band = '' - self.ctx.last_calc = None - self.ctx.successful = False - self.ctx.info = [] - self.ctx.warnings = [] - self.ctx.errors = [] - self.ctx.calcs = [] - - inputs = self.inputs - - wf_default = copy.deepcopy(self._wf_default) - if 'wf_parameters' in inputs: - wf_dict = inputs.wf_parameters.get_dict() - else: - wf_dict = wf_default - - for key, val in six.iteritems(wf_default): - 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: - options = 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 - - # set values, or defaults - self.ctx.max_number_runs = self.ctx.wf_dict.get('fleur_runmax', 4) - - # Check if user gave valid fleur executable - # if 'fleur' in inputs.scf: - # try: - # test_and_get_codenode(inputs.scf.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 FT' - 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 for the FT' - self.control_end_wc(error) - return self.exit_codes.ERROR_INVALID_INPUT_CONFIG - else: - self.ctx.scf_needed = False - - def create_new_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 - # nkpts = wf_dict.get('nkpts', 500) - # how can the user say he want to use the given kpoint mesh, ZZ nkpts : False/0 - sigma = wf_dict.get('sigma', 0.005) - emin = wf_dict.get('emin', -0.30) - emax = wf_dict.get('emax', 0.80) - - fleurmode = FleurinpModifier(self.inputs.fleurinp) - - change_dict = {'band': True, 'ndir': 0, 'minEnergy': emin, 'maxEnergy': emax, 'sigma': sigma} #'ndir' : 1, - - fleurmode.set_inpchanges(change_dict) - - # 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_band = fleurinp_new - - def scf_needed(self): - """ - Returns True if SCF WC is needed. - """ - return self.ctx.scf_needed - - def converge_scf(self): - """ - Converge charge density. - """ - return 0 - - def run_fleur(self): - """ - run a FLEUR calculation - """ - self.report('INFO: run FLEUR') - # inputs = self.get_inputs_scf() - fleurin = self.ctx.fleurinp_band - remote = self.inputs.remote - code = self.inputs.fleur - options = self.ctx.options.copy() - label = 'bansdtructure_calculation' - description = 'Bandstructure 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) - - return ToContext(last_calc=future) - - def get_inputs_scf(self): - """ - Initialize inputs for scf workflow: - wf_param, options, calculation parameters, codes, structure - """ - input_scf = AttributeDict(self.exposed_inputs(FleurScfWorkChain, namespace='scf')) - input_scf.fleurinp = self.ctx.fleurinp_band - - return input_scf - - 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.inputs.fleurinp, self.ctx.last_calc.pk, self.ctx.last_calc)) - - from aiida_fleur.tools.common_fleur_wf import find_last_submitted_calcjob - if self.ctx.last_calc: - try: - last_calc_uuid = find_last_submitted_calcjob(self.ctx.last_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_dict = last_calc_out.get_dict() - except (NotExistent, AttributeError): - last_calc_out = None - last_calc_out_dict = {} - retrieved = None - - #check if band file exists: if not succesful = False - #TODO be careful with general bands.X - # bandfilename = 'bands.1' # ['bands.1', 'bands.2', ...] - - # last_calc_retrieved = self.ctx.last_calc.get_outputs_dict()['retrieved'].folder.get_subfolder('path').get_abs_path('') - # bandfilepath = self.ctx.last_calc.get_outputs_dict()['retrieved'].folder.get_subfolder('path').get_abs_path(bandfilename) - # print(bandfilepath) - # #bandfilepath = "path to bandfile" # Array? - # if os.path.isfile(bandfilepath): - # self.ctx.successful = True - # else: - # bandfilepath = None - # self.report('!NO bandstructure file was found, something went wrong!') - - # #TODO corret efermi: - # # get efermi from last calculation - scf_results = self.inputs.remote_data.get_incoming().all()[-1].node.res - efermi_scf = scf_results.fermi_energy - bandgap_scf = scf_results.bandgap - # efermi_band = last_calc_out_dict['fermi_energy'] - # bandgap_band = last_calc_out_dict['bandgap'] - - # diff_efermi = efermi_scf - efermi_band - # diff_bandgap = bandgap_scf - bandgap_band - - outputnode_dict = {} - - outputnode_dict['workflow_name'] = self.__class__.__name__ - 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['remote_dir'] = self.ctx.last_calc.get_remote_workdir() - # outputnode_dict['fermi_energy_band'] = efermi_band - # outputnode_dict['bandgap_band'] = bandgap_band - outputnode_dict['fermi_energy_scf'] = efermi_scf - outputnode_dict['bandgap_scf'] = bandgap_scf - # outputnode_dict['diff_efermi'] = diff_efermi - # outputnode_dict['diff_bandgap'] = diff_bandgap - - # outputnode_dict['diff_efermi'] = diff_efermi - # outputnode_dict['bandfile'] = bandfilepath - - outputnode_t = Dict(dict=outputnode_dict) - if last_calc_out: - outdict = create_band_result_node(outpara=outputnode_t, - last_calc_out=last_calc_out, - last_calc_retrieved=retrieved) - else: - outdict = create_band_result_node(outpara=outputnode_t) - - #TODO parse Bandstructure - for link_name, node in six.iteritems(outdict): - self.out(link_name, node) - - def control_end_wc(self, errormsg): - """ - Controlled way to shutdown the workchain. will initialize the output nodes - The shutdown of the workchain will has to be done afterwards - """ - self.report(errormsg) # because return_results still fails somewhen - self.ctx.errors.append(errormsg) - self.return_results() - - -@cf -def create_band_result_node(**kwargs): - """ - This is a pseudo wf, to create the right graph structure of AiiDA. - This wokfunction will create the output node in the database. - It also connects the output_node to all nodes the information commes from. - 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): - if key == 'outpara': # should be always there - outpara = val - outdict = {} - outputnode = outpara.clone() - outputnode.label = 'output_band_wc_para' - outputnode.description = ('Contains band calculation results') - - outdict['output_band_wc_para'] = outputnode - - return outdict diff --git a/aiida_fleur/workflows/band2.py.legacy b/aiida_fleur/workflows/band2.py.legacy deleted file mode 100644 index b50d92b51..000000000 --- a/aiida_fleur/workflows/band2.py.legacy +++ /dev/null @@ -1,270 +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 is the worklfow 'band2' for the Fleur code, which calculates a -electron bandstructure from a given structure data node with seekpath. - -""" -# 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 -from aiida.plugins import Code, DataFactory -#from aiida.tools.codespecific.fleur.queue_defaults import queue_defaults -from aiida.engine import WorkChain -from aiida.engine import submit -from aiida.engine import if_ -from aiida.engine import ToContext -from aiida.engine import workfunction as wf -#from aiida.work.process_registry import ProcessRegistry -from aiida_fleur.calculation.fleur import FleurCalculation -from aiida_fleur.data.fleurinpmodifier import FleurinpModifier -from aiida_fleur.tools.common_fleur_wf import get_inputs_fleur -from seekpath.aiidawrappers import get_path, get_explicit_k_path -import six - -StructureData = DataFactory('structure') -Dict = DataFactory('dict') -RemoteData = DataFactory('remote') -KpointsData = DataFactory('array.kpoints') -FleurinpData = DataFactory('fleur.fleurinp') -FleurProcess = FleurCalculation.process() - -''' -We want to run fleur with a certain kpath, which we will get from the seek path method -''' - - -class fleur_band2_wc(WorkChain): - ''' - This workflow calculated a bandstructure from a Fleur calculation - - :Params: a Fleurcalculation node - :returns: - ''' - - _workflowversion = "0.1.0" - - @classmethod - def define(cls, spec): - super(fleur_band2_wc, cls).define(spec) - spec.input("wf_parameters", valid_type=Dict, required=False, - default=Dict(dict={ - 'kpath' : 'auto', - 'nkpts' : 800, - 'sigma' : 0.005, - 'emin' : -0.50, - 'emax' : 0.90})) - - spec.input("fleur", valid_type=Code, required=True) - spec.input("structure", valid_type=StructureData, required=False) - spec.input("calc_parameters", valid_type=Dict, required=False) - spec.input("settings", valid_type=Dict, required=False) - spec.input("inpgen", valid_type=Code, required=False) - spec.outline( - cls.start, - cls.setup_structure, - cls.setup_kpoints, - cls.setup_parameters, - cls.create_new_fleurinp, - cls.run_fleur, - cls.return_results - ) - #spec.dynamic_output() - - - def start(self): - ''' - check parameters, what condictions? complete? - check input nodes - ''' - ### input check ### ? or done automaticly, how optional? - # check if fleuinp corresponds to fleur_calc - print(('started bands workflow version {}'.format(self._workflowversion))) - print("Workchain node identifiers: ")#{}" - #"".format(ProcessRegistry().current_calc_node)) - - self.ctx.fleurinp1 = "" - self.ctx.last_calc = None - self.ctx.successful = False - self.ctx.warnings = [] - - wf_dict = self.inputs.wf_parameters.get_dict() - - # if MPI in code name, execute parallel - self.ctx.serial = wf_dict.get('serial', False) - - # set values, or defaults - self.ctx.max_number_runs = wf_dict.get('fleur_runmax', 4) - self.ctx.resources = wf_dict.get('resources', {"num_machines": 1}) - self.ctx.walltime_sec = wf_dict.get('walltime_sec', 10*30) - self.ctx.queue = wf_dict.get('queue_name', None) - - - - def setup_structure(self): - """ - We use SeeKPath to determine the primitive structure for the given input structure, if it - wasn't yet the case. - """ - seekpath_result = seekpath_structure(self.inputs.structure) - self.ctx.structure_initial_primitive = seekpath_result['primitive_structure'] - - def setup_kpoints(self): - """ - Define the k-point mesh for the relax and scf calculations. Also get the k-point path for - the bands calculation for the initial input structure from SeeKpath - """ - kpoints_mesh = KpointsData() - kpoints_mesh.set_cell_from_structure(self.inputs.structure) - kpoints_mesh.set_kpoints_mesh_from_density( - distance=self.ctx.protocol['kpoints_mesh_density'], - offset=self.ctx.protocol['kpoints_mesh_offset'] - ) - - self.ctx.kpoints_mesh = kpoints_mesh - - - def create_new_fleurinp(self): - """ - create a new fleurinp from the old with certain parameters - """ - # TODO allow change of kpoint mesh?, tria? - wf_dict = self.inputs.wf_parameters.get_dict() - nkpts = wf_dict.get('nkpts', 500) - # how can the user say he want to use the given kpoint mesh, ZZ nkpts : False/0 - sigma = wf_dict.get('sigma', 0.005) - emin = wf_dict.get('emin', -0.30) - emax = wf_dict.get('emax', 0.80) - - fleurmode = FleurinpModifier(self.inputs.fleurinp) - - #change_dict = {'band': True, 'ndir' : -1, 'minEnergy' : self.inputs.wf_parameters.get_dict().get('minEnergy', -0.30000000), - #'maxEnergy' : self.inputs.wf_parameters.get_dict().get('manEnergy','0.80000000'), - #'sigma' : self.inputs.wf_parameters.get_dict().get('sigma', '0.00500000')} - change_dict = {'band': True, 'ndir' : 0, 'minEnergy' : emin, - 'maxEnergy' : emax, 'sigma' : sigma} #'ndir' : 1, 'pot8' : True - - fleurmode.set_inpchanges(change_dict) - - if nkpts: - fleurmode.set_nkpts(count=nkpts) - #fleurinp_new.replace_tag() - - fleurmode.show(validate=True, display=False) # needed? - fleurinp_new = fleurmode.freeze() - self.ctx.fleurinp1 = fleurinp_new - #print(fleurinp_new) - #print(fleurinp_new.folder.get_subfolder('path').get_abs_path('')) - - def run_fleur(self): - """ - run a FLEUR calculation - """ - fleurin = self.ctx.fleurinp1 - remote = self.inputs.remote - code = self.inputs.fleur - - options = {"max_wallclock_seconds": self.ctx.walltime_sec, - "resources": self.ctx.resources, - "queue_name" : self.ctx.queue} - - inputs = get_inputs_fleur(code, remote, fleurin, options, serial=self.ctx.serial) - future = submit(FleurProcess, **inputs) - - return ToContext(last_calc=future) #calcs.append(future), - - - - def return_results(self): - ''' - return the results of the calculations - ''' - # TODO more here - print('Band workflow Done') - print(('A bandstructure was calculated for fleurinpdata {} and is found under pk={}, ' - 'calculation {}'.format(self.inputs.fleurinp, self.ctx.last_calc.pk, self.ctx.last_calc))) - - #check if band file exists: if not succesful = False - #TODO be careful with general bands.X - - bandfilename = 'bands.1' # ['bands.1', 'bands.2', ...] - # TODO this should be easier... - last_calc_retrieved = self.ctx.last_calc.get_outputs_dict()['retrieved'].folder.get_subfolder('path').get_abs_path('') - bandfilepath = self.ctx.last_calc.get_outputs_dict()['retrieved'].folder.get_subfolder('path').get_abs_path(bandfilename) - print(bandfilepath) - #bandfilepath = "path to bandfile" # Array? - if os.path.isfile(bandfilepath): - self.ctx.successful = True - else: - bandfilepath = None - print('!NO bandstructure file was found, something went wrong!') - #TODO corret efermi: - # get efermi from last calculation - efermi1 = self.inputs.remote.get_inputs()[-1].res.fermi_energy - #get efermi from this caclulation - efermi2 = self.ctx.last_calc.res.fermi_energy - diff_efermi = efermi1 - efermi2 - # store difference in output node - # adjust difference in band.gnu - #filename = 'gnutest2' - - outputnode_dict ={} - - outputnode_dict['workflow_name'] = self.__class__.__name__ - outputnode_dict['Warnings'] = self.ctx.warnings - outputnode_dict['successful'] = self.ctx.successful - outputnode_dict['diff_efermi'] = diff_efermi - #outputnode_dict['last_calc_pk'] = self.ctx.last_calc.pk - #outputnode_dict['last_calc_uuid'] = self.ctx.last_calc.uuid - outputnode_dict['bandfile'] = bandfilepath - outputnode_dict['last_calc_uuid'] = self.ctx.last_calc.uuid - outputnode_dict['last_calc_retrieved'] = last_calc_retrieved - #print outputnode_dict - outputnode = Dict(dict=outputnode_dict) - outdict = {} - #TODO parse Bandstructure - #bandstructurenode = '' - #outdict['output_band'] = bandstructurenode - #or if spin =2 - #outdict['output_band1'] = bandstructurenode1 - #outdict['output_band2'] = bandstructurenode1 - outdict['output_band_wf_para'] = outputnode - #print outdict - for k, v in six.iteritems(outdict): - self.out(k, v) - - - - -#TODO import from somewhere? -@wf -def seekpath_structure(structure): - - seekpath_info = get_path(structure) - explicit_path = get_explicit_k_path(structure) - - primitive_structure = seekpath_info.pop('primitive_structure') - conv_structure = seekpath_info.pop('conv_structure') - parameters = Dict(dict=seekpath_info) - - result = { - 'parameters': parameters, - 'conv_structure': conv_structure, - 'primitive_structure': primitive_structure, - 'explicit_kpoints_path': explicit_path['explicit_kpoints'], - } - - return result diff --git a/aiida_fleur/workflows/corelevel.py.legacy b/aiida_fleur/workflows/corelevel.py.legacy deleted file mode 100644 index 1f3dff4c9..000000000 --- a/aiida_fleur/workflows/corelevel.py.legacy +++ /dev/null @@ -1,258 +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 is the worklfow 'corelevel' using the Fleur code, which calculates Binding -energies and corelevel shifts with different methods. -'divide and conquer' -""" -# TODO alow certain kpoint path, or kpoint node, so far auto - - -from __future__ import absolute_import -from __future__ import print_function -import os.path -from aiida.plugins import Code, DataFactory -from aiida.engine import WorkChain -from aiida.engine import submit -from aiida.engine import ToContext -from aiida.engine import while_, if_ -#from aiida.work.process_registry import ProcessRegistry - -from aiida_fleur.calculation.fleur import FleurCalculation -from aiida_fleur.data.fleurinpmodifier import FleurinpModifier -from aiida_fleur.tools.create_corehole import create_corehole -import six - -StructureData = DataFactory('structure') -Dict = DataFactory('dict') -RemoteData = DataFactory('remote') -FleurinpData = DataFactory('fleur.fleurinp') -FleurProcess = FleurCalculation.process() - - -class fleur_corelevel_wc(WorkChain): - ''' - Turn key solution for the calculation of core level shift and Binding energies - - - ''' - # wf_Parameters: Dict, - ''' - 'method' : ['initial', 'full_valence ch', 'half_valence_ch', 'ch', ...] - 'Bes' : [W4f, Be1s] - 'CLS' : [W4f, Be1s] - 'atoms' : ['all', 'postions' : []] - 'references' : ['calculate', or - 'scf_para' : {...}, 'default' - 'relax' : True - 'relax_mode': ['Fleur', 'QE Fleur', 'QE'] - 'relax_para' : {...}, 'default' - 'calculate_doses' : False - 'dos_para' : {...}, 'default' - ''' - ''' - # defaults - default wf_Parameters:: - 'method' : 'initial' - 'atoms' : 'all - 'references' : 'calculate' - 'scf_para' : 'default' - 'relax' : True - 'relax_mode': 'QE Fleur' - 'relax_para' : 'default' - 'calculate_doses' : False - 'dos_para' : 'default' - ''' - - _workflowversion = "0.0.1" - - @classmethod - def define(cls, spec): - super(corelevel, cls).define(spec) - spec.input("wf_parameters", valid_type=Dict, required=False, - default=Dict(dict={ - 'method' : 'initial', - 'atoms' : 'all', - 'references' : 'calculate', - 'calculate_doses' : False, - 'relax' : True, - 'relax_mode': 'QE Fleur', - 'relax_para' : 'default', - 'scf_para' : 'default', - 'dos_para' : 'default'})) - spec.input("fleurinp", valid_type=FleurinpData, required=True) - spec.input("fleur", valid_type=Code, required=True) - spec.input("structure", valid_type=StructureData, required=False) - spec.input("calc_parameters", valid_type=Dict, required=False) - spec.outline( - cls.check_input, - if_(cls.initalstate)( - cls.calculate_inital), - cls.create_new_fleurinp, - cls.run_fleur, - cls.run_scfs, - cls.collect_results, - cls.return_results - ) - #spec.dynamic_output() - - - def start(self): - ''' - check parameters, what condictions? complete? - check input nodes - ''' - ### input check ### ? or done automaticly, how optional? - # check if fleuinp corresponds to fleur_calc - print(('started bands workflow version {}'.format(self._workflowversion))) - print("Workchain node identifiers: ")#{}" - #"".format(ProcessRegistry().current_calc_node)) - - - - def create_new_fleurinp(self): - """ - create a new fleurinp from the old with certain parameters - """ - # TODO allow change of kpoint mesh?, tria? - wf_dict = self.inputs.wf_parameters.get_dict() - nkpts = wf_dict.get('nkpts', 500) - # how can the user say he want to use the given kpoint mesh, ZZ nkpts : False/0 - sigma = wf_dict.get('sigma', 0.005) - emin = wf_dict.get('emin', -0.30) - emax = wf_dict.get('emax', 0.80) - - fleurmode = FleurinpModifier(self.inputs.fleurinp) - - #change_dict = {'band': True, 'ndir' : -1, 'minEnergy' : self.inputs.wf_parameters.get_dict().get('minEnergy', -0.30000000), - #'maxEnergy' : self.inputs.wf_parameters.get_dict().get('manEnergy','0.80000000'), - #'sigma' : self.inputs.wf_parameters.get_dict().get('sigma', '0.00500000')} - change_dict = {'band': True, 'ndir' : 0, 'minEnergy' : emin, - 'maxEnergy' : emax, 'sigma' : sigma} #'ndir' : 1, 'pot8' : True - - fleurmode.set_inpchanges(change_dict) - - if nkpts: - fleurmode.set_nkpts(count=nkpts) - #fleurinp_new.replace_tag() - - fleurmode.show(validate=True, display=False) # needed? - fleurinp_new = fleurmode.freeze() - self.ctx.fleurinp1 = fleurinp_new - print(fleurinp_new) - #print(fleurinp_new.folder.get_subfolder('path').get_abs_path('')) - - def get_inputs_fleur(self): - ''' - get the input for a FLEUR calc - ''' - inputs = FleurProcess.get_inputs_template() - - fleurin = self.ctx.fleurinp1 - #print fleurin - remote = self.inputs.remote - inputs.parent_folder = remote - inputs.code = self.inputs.fleur - inputs.fleurinpdata = fleurin - - # TODO nkpoints decide n core - - core = 12 # get from computer nodes per machine - inputs._options.resources = {"num_machines": 1, "num_mpiprocs_per_machine" : core} - inputs._options.max_wallclock_seconds = 30 * 60 - - if self.ctx.serial: - inputs._options.withmpi = False # for now - inputs._options.resources = {"num_machines": 1} - - if self.ctx.queue: - inputs._options.queue_name = self.ctx.queue - print((self.ctx.queue)) - # if code local use - #if self.inputs.fleur.is_local(): - # inputs._options.computer = computer - # #else use computer from code. - #else: - # inputs._options.queue_name = 'th1' - - if self.ctx.serial: - inputs._options.withmpi = False # for now - inputs._options.resources = {"num_machines": 1} - - return inputs - - def run_fleur(self): - ''' - run a fleur calculation - ''' - FleurProcess = FleurCalculation.process() - inputs = {} - inputs = self.get_inputs_fleur() - #print inputs - future = submit(FleurProcess, **inputs) - print('run Fleur in band workflow') - - return ToContext(last_calc=future) - - def return_results(self): - ''' - return the results of the calculations - ''' - # TODO more here - print('Band workflow Done') - print(('A bandstructure was calculated for fleurinpdata {} and is found under pk={}, ' - 'calculation {}'.format(self.inputs.fleurinp, self.ctx.last_calc.pk, self.ctx.last_calc))) - - #check if band file exists: if not succesful = False - #TODO be careful with general bands.X - - bandfilename = 'bands.1' # ['bands.1', 'bands.2', ...] - # TODO this should be easier... - last_calc_retrieved = self.ctx.last_calc.get_outputs_dict()['retrieved'].folder.get_subfolder('path') - bandfilepath = self.ctx.last_calc.get_outputs_dict()['retrieved'].folder.get_subfolder('path').get_abs_path(bandfilename) - print(bandfilepath) - #bandfilepath = "path to bandfile" # Array? - if os.path.isfile(bandfilepath): - self.ctx.successful = True - else: - bandfilepath = None - print('!NO bandstructure file was found, something went wrong!') - #TODO corret efermi: - # get efermi from last calculation - efermi1 = self.inputs.remote.get_inputs()[-1].res.fermi_energy - #get efermi from this caclulation - efermi2 = self.ctx.last_calc.res.fermi_energy - diff_efermi = efermi1 - efermi2 - # store difference in output node - # adjust difference in band.gnu - #filename = 'gnutest2' - - outputnode_dict ={} - - outputnode_dict['workflow_name'] = self.__class__.__name__ - outputnode_dict['Warnings'] = self.ctx.warnings - outputnode_dict['successful'] = self.ctx.successful - outputnode_dict['diff_efermi'] = diff_efermi - #outputnode_dict['last_calc_pk'] = self.ctx.last_calc.pk - #outputnode_dict['last_calc_uuid'] = self.ctx.last_calc.uuid - outputnode_dict['bandfile'] = bandfilepath - outputnode_dict['last_calc_uuid'] = self.ctx.last_calc.uuid - outputnode_dict['last_calc_retrieved'] = last_calc_retrieved - #print outputnode_dict - outputnode = Dict(dict=outputnode_dict) - outdict = {} - outdict['band_out'] = outputnode - #print outdict - for k, v in six.iteritems(outdict): - self.out(k, v) diff --git a/aiida_fleur/workflows/delta.py b/aiida_fleur/workflows/delta.py deleted file mode 100644 index 5df0bf867..000000000 --- a/aiida_fleur/workflows/delta.py +++ /dev/null @@ -1,614 +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 you find the worklfow 'fleur_delta_wc' which is a turnkey solution to calculate a delta for a given code with AiiDA. -""" -#TODO: calculation of delta value from the files -# submit everything if subworkchaining works in Aiida -# parameter node finding is not optimal. - -# TODO several eos starts wich only 20 structures to limit jobs throughput -from __future__ import absolute_import -from __future__ import print_function -import os -import six -from string import digits -#from pprint import pprint - -from aiida.plugins import DataFactory -from aiida.orm import Code, Group -from aiida.orm import RemoteData, StructureData, Dict, SinglefileData -from aiida.engine import WorkChain, ToContext #, while_ -#from aiida.work.process_registry import ProcessRegistry -from aiida.engine import calcfunction as cf -from aiida.engine import submit -from aiida.common.exceptions import NotExistent - -from aiida_fleur.workflows.eos import FleurEosWorkChain -from aiida_fleur.data.fleurinp import FleurinpData - - -class fleur_delta_wc(WorkChain): - """ - This workflow calculates a equation of states and from a given - group of structures in the database using a group of given parameter nodes in the database - """ - - _workflowversion = '0.3.2' - _wf_default = {} - - @classmethod - def define(cls, spec): - super(fleur_delta_wc, cls).define(spec) - spec.input( - 'wf_parameters', - valid_type=Dict, - required=False, - default=Dict( - dict={ - 'struc_group': 'delta', - 'para_group': 'delta', - 'add_extra': { - 'type': 'delta run' - }, - #'group_label' : 'delta_eos', - 'joblimit': 100, - 'part': [1, 2, 3, 4], - 'points': 7, - 'step': 0.02 - })) - spec.input('options', - valid_type=Dict, - required=False, - default=Dict( - dict={ - 'resources': { - 'num_machines': 1 - }, - 'walltime_sec': 60 * 60, - 'queue_name': '', - 'custom_scheduler_commands': '', - 'import_sys_environment': False, - 'environment_variables': {} - })) - spec.input('inpgen', valid_type=Code, required=True) - spec.input('fleur', valid_type=Code, required=True) - spec.outline( - cls.start_up, - #while_(cls.calculations_left_torun)( - cls.run_eos, #), - cls.extract_results_eos, - cls.calculate_delta, - cls.return_results, - ) - #spec.dynamic_output() - - def start_up(self): - """ - init context and some parameters - """ - - #print('started delta workflow version {}'.format(self._workflowversion)) - #print("Workchain node identifiers: {}".format(ProcessRegistry().current_calc_node)) - #identifier = 0#ProcessRegistry().current_calc_node - #self.ctx.own_uuid = identifier.uuid - #self.ctx.own_pk = identifier.pk - - self.report('started delta workflow version {} with identifier: ' #{}' - ''.format(self._workflowversion)) #, identifier)) - - # init - self.ctx.calcs_to_run = [] - # input check - - # check if right codes - wf_dict = self.inputs.wf_parameters.get_dict() - options_dict = self.inputs.get('options', - Dict(dict={ - 'resources': { - 'num_machines': 1 - }, - 'walltime_sec': int(5.5 * 3600) - })) - self.ctx.inputs_eos = { - 'fleur': self.inputs.fleur, - 'inpgen': self.inputs.inpgen, - 'wf_parameters': { - 'points': wf_dict.get('points', 7), - 'step': wf_dict.get('step', 0.02), - 'guess': 1.0 - }, - 'options': options_dict - } - self.ctx.wc_eos_para = Dict(dict=self.ctx.inputs_eos.get('wf_parameters')) - self.ctx.ncalc = 1 # init - self.get_calcs_from_groups() - self.ctx.successful = True - self.ctx.warnings = [] - self.ctx.labels = [] - #self.ctx.calcs_to_run = calcs - self.ctx.ncalcs = len(self.ctx.calcs_to_run) - print((self.ctx.ncalcs)) - print((self.ctx.ncalc)) - estimated_jobs = self.ctx.ncalc * wf_dict.get('points', 5) - joblimit = wf_dict.get('joblimit', 90) - self.ctx.eos_run_steps = 1 - self.ctx.eos_steps_done = 0 - self.ctx.minindex = 0 - self.ctx.maxindex = self.ctx.ncalc - 1 - self.ctx.eos_max_perstep = 10000 # init - - if estimated_jobs >= joblimit: - self.ctx.eos_run_steps = estimated_jobs / joblimit + 1 - self.ctx.eos_max_perstep = joblimit / wf_dict.get('points', 5) - # TODO be carefull if is not a divisor... of joblimit - self.ctx.maxindex = 0 # will be set later self.ctx.eos_max_perstep - - self.report('{} {}'.format(self.ctx.ncalc, self.ctx.eos_max_perstep)) - self.report('Estimated fleur scfs to run {}, running in {} steps.' - ''.format(estimated_jobs, self.ctx.eos_run_steps)) - - def get_calcs_from_groups(self): - """ - Extract the crystal structures and parameter data nodes from the given - groups and create calculation 'pairs' (stru, para). - """ - wf_dict = self.inputs.wf_parameters.get_dict() - #get all delta structure - str_gr = wf_dict.get('struc_group', 'delta') - - try: - group_pk = int(str_gr) - except ValueError: - group_pk = None - group_name = str_gr - - if group_pk is not None: - try: - str_group = Group(label=group_pk) - except NotExistent: - str_group = None - message = ('You have to provide a valid pk for a Group of' - 'structures or a Group name. Wf_para key: "struc_group".' - 'given pk= {} is not a valid group' - '(or is your group name integer?)'.format(group_pk)) - #print(message) - self.report(message) - self.abort_nowait('I abort, because I have no structures to calculate ...') - else: - try: - str_group = Group.get_from_string(group_name) - except NotExistent: - str_group = None - message = ('You have to provide a valid pk for a Group of' - 'structures or a Group name. Wf_para key: "struc_group".' - 'given group name= {} is not a valid group' - '(or is your group name integer?)'.format(group_name)) - #print(message) - self.report(message) - self.abort_nowait('I abort, because I have no structures to calculate ...') - - #get all delta parameters - para_gr = wf_dict.get('para_group', 'delta') - - if not para_gr: - #waring use defauls - message = 'COMMENT: I did recieve "para_group=None" as input. I will use inpgen defaults' - self.report(message) - - try: - group_pk = int(para_gr) - except ValueError: - group_pk = None - group_name = para_gr - - if group_pk is not None: - try: - para_group = Group(label=group_pk) - except NotExistent: - para_group = None - message = ('ERROR: You have to provide a valid pk for a Group of' - 'parameters or a Group name (or use None for inpgen defaults). Wf_para key: "para_group".' - 'given pk= {} is not a valid group' - '(or is your group name integer?)'.format(group_pk)) - #print(message) - self.report(message) - self.abort_nowait('ERROR: I abort, because I have no paremeters to calculate and ' - 'I guess you did not want to use the inpgen default...') - else: - try: - para_group = Group.get_from_string(group_name) - except NotExistent: - para_group = None - message = ('ERROR: You have to provide a valid pk for a Group of' - 'parameters or a Group name (or use None for inpgen defaults). Wf_para key: "struc_group".' - 'given group name= {} is not a valid group' - '(or is your group name integer?)'.format(group_name)) - #print(message) - self.report(message) - self.abort_nowait('ERROR: I abort, because I have no paremeters to calculate and ' - 'I guess you did not want to use the inpgen default...') - - # creating calculation pairs (structure, parameters) - - para_nodesi = para_group.nodes - para_nodes = [] - - for para in para_nodesi: - para_nodes.append(para) - #print para_nodes - n_para = len(para_nodes) - stru_nodes = str_group.nodes - n_stru = len(stru_nodes) - if n_para != n_stru: - message = ('COMMENT: You did not provide the same number of parameter' - 'nodes as structure nodes. Is this wanted? npara={} nstru={}'.format(n_para, n_stru)) - self.report(message) - calcs = [] - for struc in stru_nodes: - para = get_paranode(struc, para_nodes) - #if para: - calcs.append((struc, para)) - #else: - # calcs.append((struc)) - #pprint(calcs[:20]) - self.ctx.calcs_to_run = calcs - self.ctx.ncalc = len(calcs) - - #def calculations_left_torun(self): - # """ - # Checks if there are still some equations of states to run - # """ - # calculations_left = True - # self.ctx.last_step = False - # - # if self.ctx.eos_steps_done == self.ctx.eos_run_steps: - # calculations_left = False - # if (self.ctx.eos_steps_done + 1) == self.ctx.eos_run_steps: - # self.ctx.last_step = True - # - # return calculations_left - - def run_eos(self): - """ - Run the equation of states for all delta structures with their parameters - """ - #if self.ctx.last_step: - # self.ctx.maxindex = None - #else: - # self.ctx.maxindex = self.ctx.maxindex + self.ctx.eos_max_perstep - - #self.report('Submitting eqaution of states part {} out of {}, from {} to {}' - # ''.format(self.ctx.eos_steps_done, self.ctx.eos_run_steps, - # self.ctx.minindex, self.ctx.maxindex)) - - eos_results = {} - inputs = self.get_inputs_eos() - - #print(self.ctx.minindex) - #print(self.ctx.maxindex) - - for struc, para in self.ctx.calcs_to_run: #[:4]self.ctx.minindex:self.ctx.maxindex]:#0:0]:# - #print para - formula = struc.get_formula() - label = '|delta_wc|eos|{}'.format(formula) - description = '|delta| fleur_eos_wc on {}'.format(formula) - if para: - eos_future = submit(FleurEosWorkChain, - wf_parameters=inputs['wc_eos_para'], - structure=struc, - options=inputs['options'], - calc_parameters=para, - inpgen=inputs['inpgen'], - fleur=inputs['fleur'], - label=label, - description=description) - else: # TODO: run eos_wc_simple - eos_future = submit(FleurEosWorkChain, - wf_parameters=inputs['wc_eos_para'], - structure=struc, - options=inputs['options'], - inpgen=inputs['inpgen'], - fleur=inputs['fleur'], - label=label, - description=description) - self.report('launching fleur_eos_wc<{}> on structure {} with parameter {}' - ''.format(eos_future.pid, struc.pk, para.pk)) - label = formula - self.ctx.labels.append(label) - eos_results[label] = eos_future - - #self.ctx.eos_steps_done = self.ctx.eos_steps_done + 1 - #self.ctx.minindex = self.ctx.maxindex - ''' - # with run - eos_results = {} - inputs = self.get_inputs_eos() - - - for struc, para in self.ctx.calcs_to_run[:]: - print para - formula = struc.get_formula() - if para: - #print('here') - eos_future = fleur_eos_wc.run( - wf_parameters=inputs['wc_eos_para'], structure=struc, - calc_parameters=para, inpgen=inputs['inpgen'], fleur=inputs['fleur']) - #fleur_eos_wc.run(# - else: - self.report('INFO: default parameters for structure {}'.format(formula)) - eos_future = fleur_eos_wc.run( - wf_parameters=inputs['wc_eos_para'], structure=struc, - inpgen=inputs['inpgen'], fleur=inputs['fleur']) - #fleur_eos_wc.run(#a - #self.report('launching fleur_eos_wc<{}> on structure {} with parameter {}' - # ''.format(eos_future.pid, struc.pk, para.pk)) - label = formula - self.ctx.labels.append(label) - eos_results[label] = eos_future - - return ToContext(**eos_results) - ''' - return ToContext(**eos_results) - - # To limit the troughput of 100 jobs, we create several run eos steps - def get_inputs_eos(self): - """ - get the inputs for a scf-cycle - """ - inputs = {} - # produce the inputs for a eos worklfow (collect here...) - - inputs['wc_eos_para'] = self.ctx.wc_eos_para - #inputs['calc_parameters'] = self.inputs.calc_parameters - inputs['inpgen'] = self.ctx.inputs_eos.get('inpgen') - inputs['fleur'] = self.ctx.inputs_eos.get('fleur') - inputs['options'] = self.ctx.inputs_eos.get('options') - return inputs - - def extract_results_eos(self): - """ - extract information out of the result nodes of the the eos workchains - ran in the step before - """ - - self.ctx.all_results = {} - self.ctx.all_succ = {} - self.ctx.eos_uuids = {} - outstr = ('''\ - Delta calculation FLEUR {} (AiiDA wc). - - Crystal \t V0 \t \t B0 \t \t BP [A^3/at] \t [GPa] \t \t [--] \n - '''.format(self.ctx.inputs_eos.get('fleur'))) - filename = 'delta_wc_{}.out'.format(self.ctx.own_pk) - outfile = open(filename, 'w') - outfile.write(outstr) - outfile.close() - outstr = '' - for label in self.ctx.labels: - eos_res = self.ctx[label] - #print(calc) - outpara1 = eos_res.get_outputs_dict() - #print outpara1 - try: - outpara = outpara1['output_eos_wc_para'].get_dict() - except KeyError: - self.report('ERROR: Eos wc for element: {} failed. I retrieved {} ' - 'I skip the results retrieval for that element.'.format(label, eos_res)) - continue - eos_succ = outpara.get('successful', False) - if not eos_succ: - #maybe do something else here (exclude point and write a warning or so, or error treatment) - self.ctx.successful = False - - natoms = outpara.get('natoms', None) - gs_vol = outpara.get('volume_gs', None) - bm = outpara.get('bulk_modulus', None) - #bm_u = outpara.get('bulk_modulus_units', 'GPa') - dbm = outpara.get('bulk_deriv', None) - if natoms: - gs_vol_pera = gs_vol / natoms - else: - gs_vol_pera = gs_vol - - element = label.translate(None, digits) # remove all numbers from string - self.ctx.all_results[element] = [gs_vol_pera, bm, dbm] - self.ctx.all_succ[element] = eos_succ - self.ctx.eos_uuids[element] = eos_res.get_inputs()[0].uuid - - outstr = outstr + '{} \t {:.5f} \t {:.5f} \t {:.5f} \n'.format(element, gs_vol_pera, bm, dbm) - #write inside the loop to have at least partially results... - #outfile = open('delta_wc.out', 'a') - #outstr = '{} \t {:.5f} \t {:.5f} \t {:.5f} \n'.format(element, gs_vol_pera, bm, dbm) - #outfile.write(outstr) - #outfile.close() - # produce a single file - # maybe put in try(or write in a certain place where is sure that you have the permissions) - #outfile = open('delta_wc.out', 'w') - outfile = open(filename, 'a') # for testing purposes - outfile.write(outstr) - - outfile.close() - - self.ctx.outfilepath = os.path.abspath(outfile.name) - - def calculate_delta(self): - """ - Execute here the script to calculate a delta factor - """ - pass - - def return_results(self): - """ - return the results of the calculations - """ - - # log some stuff in report - - # a text file should be written and stored as single file data and - #parameter data node in the database - - #produce a single file data with all the numbers - - all_res = self.ctx.all_results - self.report('all_res : {}'.format(all_res)) - #print all_res - bm_dic = {} - bmd_dic = {} - vol_dic = {} - - for elem, val in six.iteritems(all_res): - #print elem - vol_dic[elem] = val[0] - bm_dic[elem] = val[1] - bmd_dic[elem] = val[2] - - outputnode_dict = {} - - outputnode_dict['workflow_name'] = self.__class__.__name__ - outputnode_dict['warnings'] = self.ctx.warnings - outputnode_dict['successful'] = self.ctx.successful - outputnode_dict['eos_uuids'] = self.ctx.eos_uuids - outputnode_dict['eos_success'] = self.ctx.all_succ - outputnode_dict['bulk_modulus'] = bm_dic - outputnode_dict['bulk_modulus_units'] = 'GPa' - outputnode_dict['bulk_modulus_dev'] = bmd_dic - outputnode_dict['volumes'] = vol_dic - outputnode_dict['volumes_units'] = 'A^3/per atom' - outputnode_dict['delta_factor'] = {'Wien2K': '', 'Fleur_026': ''} - - #outputnode = Dict(dict=outputnode_dict) - - if self.ctx.successful: - self.report('INFO: Done, delta worklfow complete') - #print 'Done, delta worklfow complete' - else: - self.report('INFO: Done, but something went wrong.... Properly some ' - 'individual eos workchain failed. Check the log.') - #print('Done, but something went wrong.... Properly some ' - # 'individual eos workchain failed. Check the log.') - - delta_file = SinglefileData.filename = self.ctx.outfilepath - - print(delta_file) - - # output must be aiida Data types. - outnodedict = {} - outnode = Dict(dict=outputnode_dict) - outnodedict['results_node'] = outnode - for label in self.ctx.labels: - eos_res = self.ctx[label] - #print(calc) - outpara1 = eos_res.get_outputs_dict() - #print outpara1 - try: - outpara = outpara1['output_eos_wc_para'] - except KeyError: - #self.report('ERROR: Eos wc for element: {} failed. I retrieved {} ' - # 'I skip the results retrieval for that element.'.format(label, eos_res)) - continue - outnodedict[label] = outpara - - outputnode = create_delta_result_node(**outnodedict) - - outdict = {} - outdict['output_delta_wc_para'] = outputnode.get('output_delta_wc_para') - #outdict['delta_file'] = delta_file - #print outdict - for link_name, node in six.iteritems(outdict): - self.out(link_name, node) - - -''' -if __name__ == "__main__": - import argparse - - parser = argparse.ArgumentParser(description='SCF with FLEUR. workflow to' - ' converge the chargedensity and optional the total energy.') - parser.add_argument('--wf_para', type=Dict, dest='wf_parameters', - help='The pseudopotential family', required=False) - parser.add_argument('--structure', type=StructureData, dest='structure', - help='The crystal structure node', required=False) - parser.add_argument('--calc_para', type=Dict, dest='calc_parameters', - help='Parameters for the FLEUR calculation', required=False) - parser.add_argument('--fleurinp', type=FleurinpData, dest='fleurinp', - help='FleurinpData from which to run the FLEUR calculation', required=False) - parser.add_argument('--remote', type=RemoteData, dest='remote_data', - help=('Remote Data of older FLEUR calculation, ' - 'from which files will be copied (mixing_history ...)'), required=False) - parser.add_argument('--inpgen', type=Code, dest='inpgen', - help='The inpgen code node to use', required=False) - parser.add_argument('--fleur', type=Code, dest='fleur', - help='The FLEUR code node to use', required=True) - - args = parser.parse_args() - res = fleur_scf_wc.run(wf_parameters=args.wf_parameters, - structure=args.structure, - calc_parameters=args.calc_parameters, - fleurinp=args.fleurinp, - remote_data=args.remote_data, - inpgen = args.inpgen, - fleur=args.fleur) -''' - - -@cf -def create_delta_result_node(**kwargs): #*args): - """ - This is a pseudo wf, to create the rigth graph structure of AiiDA. - This wokfunction will create the output node in the database. - It also connects the output_node to all nodes the information commes from. - 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. - - """ - outdict = {} - outpara = kwargs.get('results_node', {}) - outdict['output_delta_wc_para'] = outpara.clone() - # copy, because we rather produce the same node twice then have a circle in the database for now... - #output_para = args[0] - #return {'output_eos_wc_para'} - return outdict - - -def get_paranode(struc, para_nodes): - """ - find out if a parameter node for a structure is in para_nodes - (currently very creedy, but lists are small (100x100) but maybe reduce database accesses) - """ - - suuid = struc.uuid - formula = struc.get_formula() - element = formula.translate(None, digits) - #print para_nodes - for para in para_nodes: - struc_uuid = para.get_extra('struc_uuid', None) - para_form = para.get_extra('formula', None) - para_ele = para.get_extra('element', None) - if suuid == struc_uuid: - return para - elif formula == para_form: - return para - elif element == para_ele: - return para - elif element == para_form: - return para - else: - pass - #Do something else (test if parameters for a certain element are there) - #.... - # we found no parameter node for the given structure therefore return none - return None - - -def write_delta_file(result_dict): - pass diff --git a/aiida_fleur/workflows/delta_split.py.legacy b/aiida_fleur/workflows/delta_split.py.legacy deleted file mode 100644 index a06c9fe37..000000000 --- a/aiida_fleur/workflows/delta_split.py.legacy +++ /dev/null @@ -1,588 +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 you find the worklfow 'fleur_delta_wc' which is a turnkey solution to calculate a delta for a given code with AiiDA. -""" -#TODO: calculation of delta value from the files -# submit everything if subworkchaining works in Aiida -# parameter node finding is not optimal. - -# TODO several eos starts wich only 20 structures to limit jobs throughput -from __future__ import absolute_import -from __future__ import print_function -import os -from string import digits -from pprint import pprint - -from aiida.plugins import Code, DataFactory, Group -from aiida.engine import WorkChain, ToContext, while_ -#from aiida.work.process_registry import ProcessRegistry -from aiida.engine import workfunction as wf -from aiida.engine import submit -from aiida.common.exceptions import NotExistent -from aiida_fleur.workflows.eos import fleur_eos_wc -import six - -#from aiida_fleur.tools.xml_util import eval_xpath2 -#from lxml import etree - - -RemoteData = DataFactory('remote') -StructureData = DataFactory('structure') -Dict = DataFactory('dict') -FleurinpData = DataFactory('fleur.fleurinp') -SingleData = DataFactory('singlefile') - -class fleur_delta_wc(WorkChain): - """ - This workflow calculates a equation of states and from a given - group of structures in the database using a group of given parameter nodes in the database - """ - - _workflowversion = "0.0.1" - _wf_default = {} - - def __init__(self, *args, **kwargs): - super(fleur_delta_wc, self).__init__(*args, **kwargs) - - @classmethod - def define(cls, spec): - super(fleur_delta_wc, cls).define(spec) - spec.input("wf_parameters", valid_type=Dict, required=False, - default=Dict(dict={'struc_group': 'delta', - 'para_group' : 'delta', - 'add_extra' : {'type' : 'delta run'}, - #'group_label' : 'delta_eos', - 'joblimit' : 100, - 'part' : [1,2,3,4], - 'points' : 5, - 'step' : 0.02, - 'queue_name' : '', - 'options' : {'resources' : {"num_machines": 1}, - 'walltime_sec' : int(5.5*3600)}})) - spec.input("inpgen", valid_type=Code, required=True) - spec.input("fleur", valid_type=Code, required=True) - spec.outline( - cls.start_up, - while_(cls.calculations_left_torun)( - cls.run_eos), - cls.extract_results_eos, - cls.calculate_delta, - cls.return_results, - - ) - #spec.dynamic_output() - - def start_up(self): - """ - init context and some parameters - """ - - #print('started delta workflow version {}'.format(self._workflowversion)) - #print("Workchain node identifiers: {}".format(ProcessRegistry().current_calc_node)) - self.report('started delta workflow version {} with idenifier: '#{}' - ''.format(self._workflowversion))#, ProcessRegistry().current_calc_node)) - - # init - self.ctx.calcs_to_run = [] - # input check - - # check if right codes - wf_dict = self.inputs.wf_parameters.get_dict() - self.ctx.inputs_eos = { - 'fleur': self.inputs.fleur, - 'inpgen': self.inputs.inpgen, - 'wf_parameters': - {'points' : wf_dict.get('points', 5), - 'step' : wf_dict.get('step', 0.02), - 'guess' : 1.0, - 'resources' : wf_dict.get('resources', {"num_machines": 1}), - 'walltime_sec': wf_dict.get('walltime_sec', int(5.5*3600)), - 'queue_name' : wf_dict.get('queue_name', ''), - 'serial' : wf_dict.get('serial', False) - }} - self.ctx.wc_eos_para = Dict(dict=self.ctx.inputs_eos.get('wf_parameters')) - self.ctx.ncalc = 1 # init - self.get_calcs_from_groups() - self.ctx.successful = True - self.ctx.warnings = [] - self.ctx.labels = [] - #self.ctx.calcs_to_run = calcs - self.ctx.ncalcs = len(self.ctx.calcs_to_run) - print(self.ctx.ncalcs) - print(self.ctx.ncalc) - estimated_jobs = self.ctx.ncalc*wf_dict.get('points', 5) - joblimit = wf_dict.get('joblimit', 90) - self.ctx.eos_run_steps = 1 - self.ctx.eos_steps_done = 0 - self.ctx.minindex = 0 - self.ctx.maxindex = self.ctx.ncalc -1 - self.ctx.eos_max_perstep = 10000 # init - - if estimated_jobs >= joblimit: - self.ctx.eos_run_steps = estimated_jobs/joblimit + 1 - self.ctx.eos_max_perstep = joblimit/wf_dict.get('points', 5) - # TODO be carefull if is not a divisor... of joblimit - self.ctx.maxindex = 0 # will be set later self.ctx.eos_max_perstep - - self.report('{} {}'.format(self.ctx.ncalc, self.ctx.eos_max_perstep)) - self.report('Estimated fleur scfs to run {}, running in {} steps.' - ''.format(estimated_jobs, self.ctx.eos_run_steps)) - - def get_calcs_from_groups(self): - """ - Extract the crystal structures and parameter data nodes from the given - groups and create calculation 'pairs' (stru, para). - """ - wf_dict = self.inputs.wf_parameters.get_dict() - #get all delta structure - str_gr = wf_dict.get('struc_group', 'delta') - - try: - group_pk = int(str_gr) - except ValueError: - group_pk = None - group_name = str_gr - - if group_pk is not None: - try: - str_group = Group(dbgroup=group_pk) - except NotExistent: - str_group = None - message = ('You have to provide a valid pk for a Group of' - 'structures or a Group name. Wf_para key: "struc_group".' - 'given pk= {} is not a valid group' - '(or is your group name integer?)'.format(group_pk)) - #print(message) - self.report(message) - self.abort_nowait('I abort, because I have no structures to calculate ...') - else: - try: - str_group = Group.get_from_string(group_name) - except NotExistent: - str_group = None - message = ('You have to provide a valid pk for a Group of' - 'structures or a Group name. Wf_para key: "struc_group".' - 'given group name= {} is not a valid group' - '(or is your group name integer?)'.format(group_name)) - #print(message) - self.report(message) - self.abort_nowait('I abort, because I have no structures to calculate ...') - - - #get all delta parameters - para_gr = wf_dict.get('para_group', 'delta') - - if not para_gr: - #waring use defauls - message = 'COMMENT: I did recieve "para_group=None" as input. I will use inpgen defaults' - self.report(message) - - try: - group_pk = int(para_gr ) - except ValueError: - group_pk = None - group_name = para_gr - - if group_pk is not None: - try: - para_group = Group(dbgroup=group_pk) - except NotExistent: - para_group = None - message = ('ERROR: You have to provide a valid pk for a Group of' - 'parameters or a Group name (or use None for inpgen defaults). Wf_para key: "para_group".' - 'given pk= {} is not a valid group' - '(or is your group name integer?)'.format(group_pk)) - #print(message) - self.report(message) - self.abort_nowait('ERROR: I abort, because I have no paremeters to calculate and ' - 'I guess you did not want to use the inpgen default...') - else: - try: - para_group = Group.get_from_string(group_name) - except NotExistent: - para_group = None - message = ('ERROR: You have to provide a valid pk for a Group of' - 'parameters or a Group name (or use None for inpgen defaults). Wf_para key: "struc_group".' - 'given group name= {} is not a valid group' - '(or is your group name integer?)'.format(group_name)) - #print(message) - self.report(message) - self.abort_nowait('ERROR: I abort, because I have no paremeters to calculate and ' - 'I guess you did not want to use the inpgen default...') - - # creating calculation pairs (structure, parameters) - - para_nodesi = para_group.nodes - para_nodes = [] - - for para in para_nodesi: - para_nodes.append(para) - #print para_nodes - n_para = len(para_nodes) - stru_nodes = str_group.nodes - n_stru = len(stru_nodes) - if n_para != n_stru: - message = ('COMMENT: You did not provide the same number of parameter' - 'nodes as structure nodes. Is this wanted? npara={} nstru={}'.format(n_para, n_stru)) - self.report(message) - calcs = [] - for struc in stru_nodes: - para = get_paranode(struc, para_nodes) - #if para: - calcs.append((struc, para)) - #else: - # calcs.append((struc)) - #pprint(calcs[:20]) - self.ctx.calcs_to_run = calcs - self.ctx.ncalc = len(calcs) - return - - def calculations_left_torun(self): - """ - Checks if there are still some equations of states to run - """ - calculations_left = True - self.ctx.last_step = False - - if self.ctx.eos_steps_done == self.ctx.eos_run_steps: - calculations_left = False - if (self.ctx.eos_steps_done + 1) == self.ctx.eos_run_steps: - self.ctx.last_step = True - - return calculations_left - - - - def run_eos(self): - """ - Run the equation of states for all delta structures with their parameters - """ - if self.ctx.last_step: - self.ctx.maxindex = None - else: - self.ctx.maxindex = self.ctx.maxindex + self.ctx.eos_max_perstep - - self.report('Submitting eqaution of states part {} out of {}, from {} to {}' - ''.format(self.ctx.eos_steps_done, self.ctx.eos_run_steps, - self.ctx.minindex, self.ctx.maxindex)) - - eos_results = {} - inputs = self.get_inputs_eos() - - - print((self.ctx.minindex)) - print((self.ctx.maxindex)) - - for struc, para in self.ctx.calcs_to_run[self.ctx.minindex:self.ctx.maxindex]:#0:0]:# - #print para - formula = struc.get_formula() - label = '|delta_wc|eos|{}'.format(formula) - description = '|delta| fleur_eos_wc on {}'.format(formula) - if para: - eos_future = submit(fleur_eos_wc, - wf_parameters=inputs['wc_eos_para'], structure=struc, - calc_parameters=para, inpgen=inputs['inpgen'], fleur=inputs['fleur'], - _label=label, _description=description) - else: # TODO: run eos_wc_simple - eos_future = submit(fleur_eos_wc, - wf_parameters=inputs['wc_eos_para'], structure=struc, - inpgen=inputs['inpgen'], fleur=inputs['fleur'], - _label=label, _description=description) - self.report('launching fleur_eos_wc<{}> on structure {} with parameter {}' - ''.format(eos_future.pid, struc.pk, para.pk)) - label = formula - self.ctx.labels.append(label) - eos_results[label] = eos_future - - self.ctx.eos_steps_done = self.ctx.eos_steps_done + 1 - self.ctx.minindex = self.ctx.maxindex - - - return ToContext(**eos_results) - - ''' - # with run - eos_results = {} - inputs = self.get_inputs_eos() - - - for struc, para in self.ctx.calcs_to_run[:]: - print para - formula = struc.get_formula() - if para: - #print('here') - eos_future = fleur_eos_wc.run( - wf_parameters=inputs['wc_eos_para'], structure=struc, - calc_parameters=para, inpgen=inputs['inpgen'], fleur=inputs['fleur']) - #fleur_eos_wc.run(# - else: - self.report('INFO: default parameters for structure {}'.format(formula)) - eos_future = fleur_eos_wc.run( - wf_parameters=inputs['wc_eos_para'], structure=struc, - inpgen=inputs['inpgen'], fleur=inputs['fleur']) - #fleur_eos_wc.run(#a - #self.report('launching fleur_eos_wc<{}> on structure {} with parameter {}' - # ''.format(eos_future.pid, struc.pk, para.pk)) - label = formula - self.ctx.labels.append(label) - eos_results[label] = eos_future - - return ToContext(**eos_results) - ''' - - # To limit the troughput of 100 jobs, we create several run eos steps - def get_inputs_eos(self): - """ - get the inputs for a scf-cycle - """ - inputs = {} - # produce the inputs for a eos worklfow (collect here...) - - inputs['wc_eos_para'] = self.ctx.wc_eos_para - #inputs['calc_parameters'] = self.inputs.calc_parameters - inputs['inpgen'] = self.ctx.inputs_eos.get('inpgen') - inputs['fleur'] = self.ctx.inputs_eos.get('fleur') - - return inputs - - def extract_results_eos(self): - """ - extract information out of the result nodes of the the eos workchains - ran in the step before - """ - - self.ctx.all_results = {} - self.ctx.all_succ = {} - self.ctx.eos_uuids ={} - outstr = ('''\ - Delta calculation FLEUR {} (AiiDA wc). - - Crystal \t V0 \t \t B0 \t \t BP [A^3/at] \t [GPa] \t \t [--] \n - '''.format(self.ctx.inputs_eos.get('fleur'))) - outfile = open('delta_wc.out', 'w') - outfile.write(outstr) - outfile.close() - outstr = '' - for label in self.ctx.labels: - eos_res = self.ctx[label] - #print(calc) - outpara1 = eos_res.get_outputs_dict() - #print outpara1 - try: - outpara= outpara1['output_eos_wc_para'].get_dict() - except KeyError: - self.report('ERROR: Eos wc for element: {} failed. I retrieved {} ' - 'I skip the results retrieval for that element.'.format(label, eos_res)) - continue - eos_succ = outpara.get('successful', False) - if not eos_succ: - #maybe do something else here (exclude point and write a warning or so, or error treatment) - self.ctx.successful = False - - natoms = outpara.get('natoms', None) - gs_vol = outpara.get('volume_gs', None) - bm = outpara.get('bulk_modulus', None) - #bm_u = outpara.get('bulk_modulus_units', 'GPa') - dbm = outpara.get('bulk_deriv', None) - if natoms: - gs_vol_pera = gs_vol/natoms - else: - gs_vol_pera = gs_vol - - element = label.translate(None, digits) # remove all numbers from string - self.ctx.all_results[element] = [gs_vol_pera, bm, dbm] - self.ctx.all_succ[element] = eos_succ - self.ctx.eos_uuids[element] = eos_res.get_inputs()[0].uuid - - outstr = outstr + '{} \t {:.5f} \t {:.5f} \t {:.5f} \n'.format(element, gs_vol_pera, bm, dbm) - #write inside the loop to have at least partially results... - #outfile = open('delta_wc.out', 'a') - #outstr = '{} \t {:.5f} \t {:.5f} \t {:.5f} \n'.format(element, gs_vol_pera, bm, dbm) - #outfile.write(outstr) - #outfile.close() - # produce a single file - # maybe put in try(or write in a certain place where is sure that you have the permissions) - #outfile = open('delta_wc.out', 'w') - outfile = open('delta_wc.out', 'a') # for testing purposes - outfile.write(outstr) - - outfile.close() - - self.ctx.outfilepath = os.path.abspath(outfile.name) - - - def calculate_delta(self): - """ - Execute here the script to calculate a delta factor - """ - pass - - def return_results(self): - """ - return the results of the calculations - """ - - # log some stuff in report - - # a text file should be written and stored as single file data and - #parameter data node in the database - - #produce a single file data with all the numbers - - all_res = self.ctx.all_results - bm_dic = {} - bmd_dic = {} - vol_dic = {} - - for elem,val in all_res: - vol_dic[elem] = val[0] - bm_dic[elem] = val[1] - bmd_dic[elem] = val[2] - - outputnode_dict ={} - - - outputnode_dict['workflow_name'] = self.__class__.__name__ - outputnode_dict['warnings'] = self.ctx.warnings - outputnode_dict['successful'] = self.ctx.successful - outputnode_dict['eos_uuids'] = self.ctx.eos_uuids - outputnode_dict['eos_success'] = self.ctx.all_succ - outputnode_dict['bulk_modulus'] = bm_dic - outputnode_dict['bulk_modulus_units'] = 'GPa' - outputnode_dict['bulk_modulus_dev'] = bmd_dic - outputnode_dict['volumes'] = vol_dic - outputnode_dict['volumes_units'] = 'A^3/per atom' - outputnode_dict['delta_factor'] = {'Wien2K' : '', 'Fleur_026' : ''} - - #outputnode = Dict(dict=outputnode_dict) - - if self.ctx.successful: - self.report('INFO: Done, delta worklfow complete') - #print 'Done, delta worklfow complete' - else: - self.report('INFO: Done, but something went wrong.... Properly some ' - 'individual eos workchain failed. Check the log.') - #print('Done, but something went wrong.... Properly some ' - # 'individual eos workchain failed. Check the log.') - - delta_file = SingleData.filename = self.ctx.outfilepath - - print(delta_file) - - # output must be aiida Data types. - outnodedict = {} - outnode = Dict(dict=outputnode_dict) - outnodedict['results_node'] = outnode - for label in self.ctx.labels: - eos_res = self.ctx[label] - #print(calc) - outpara1 = eos_res.get_outputs_dict() - #print outpara1 - try: - outpara = outpara1['output_eos_wc_para'] - except KeyError: - #self.report('ERROR: Eos wc for element: {} failed. I retrieved {} ' - # 'I skip the results retrieval for that element.'.format(label, eos_res)) - continue - outnodedict[label] = outpara - - outputnode = create_delta_result_node(**outnodedict) - - outdict = {} - outdict['output_delta_wc_para'] = outputnode.get('output_delta_wc_para') - #outdict['delta_file'] = delta_file - #print outdict - for link_name, node in six.iteritems(outdict): - self.out(link_name, node) -''' -if __name__ == "__main__": - import argparse - - parser = argparse.ArgumentParser(description='SCF with FLEUR. workflow to' - ' converge the chargedensity and optional the total energy.') - parser.add_argument('--wf_para', type=Dict, dest='wf_parameters', - help='The pseudopotential family', required=False) - parser.add_argument('--structure', type=StructureData, dest='structure', - help='The crystal structure node', required=False) - parser.add_argument('--calc_para', type=Dict, dest='calc_parameters', - help='Parameters for the FLEUR calculation', required=False) - parser.add_argument('--fleurinp', type=FleurinpData, dest='fleurinp', - help='FleurinpData from which to run the FLEUR calculation', required=False) - parser.add_argument('--remote', type=RemoteData, dest='remote_data', - help=('Remote Data of older FLEUR calculation, ' - 'from which files will be copied (mixing_history ...)'), required=False) - parser.add_argument('--inpgen', type=Code, dest='inpgen', - help='The inpgen code node to use', required=False) - parser.add_argument('--fleur', type=Code, dest='fleur', - help='The FLEUR code node to use', required=True) - - args = parser.parse_args() - res = fleur_scf_wc.run(wf_parameters=args.wf_parameters, - structure=args.structure, - calc_parameters=args.calc_parameters, - fleurinp=args.fleurinp, - remote_data=args.remote_data, - inpgen = args.inpgen, - fleur=args.fleur) -''' -@wf -def create_delta_result_node(**kwargs):#*args): - """ - This is a pseudo wf, to create the rigth graph structure of AiiDA. - This wokfunction will create the output node in the database. - It also connects the output_node to all nodes the information commes from. - 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. - - """ - outdict = {} - outpara = kwargs.get('results_node', {}) - outdict['output_delta_wc_para'] = outpara.copy() - # copy, because we rather produce the same node twice then have a circle in the database for now... - #output_para = args[0] - #return {'output_eos_wc_para'} - return outdict - - -def get_paranode(struc, para_nodes): - """ - find out if a parameter node for a structure is in para_nodes - (currently very creedy, but lists are small (100x100) but maybe reduce database accesses) - """ - - suuid = struc.uuid - formula = struc.get_formula() - element = formula.translate(None, digits) - #print para_nodes - for para in para_nodes: - struc_uuid = para.get_extra('struc_uuid', None) - para_form = para.get_extra('formula', None) - para_ele = para.get_extra('element', None) - if suuid == struc_uuid: - return para - elif formula == para_form: - return para - elif element == para_ele: - return para - elif element == para_form: - return para - else: - pass - #Do something else (test if parameters for a certain element are there) - #.... - # we found no parameter node for the given structure therefore return none - return None - -def write_delta_file(result_dict): - pass diff --git a/tests/cmdline/conftest.py b/tests/cmdline/conftest.py index a60e7aa3a..33ac21e59 100644 --- a/tests/cmdline/conftest.py +++ b/tests/cmdline/conftest.py @@ -12,6 +12,13 @@ def mock_launch_process(*_, **__): return +@pytest.fixture +def struct_file_type(): + """Return instance of ``StructureNodeOrFileParamType``.""" + from aiida_fleur.cmdline.util.types import StructureNodeOrFileParamType + return StructureNodeOrFileParamType() + + @pytest.fixture def run_cli_command(): """Run a `click` command with the given options. diff --git a/tests/cmdline/data/test_fleurinp.py b/tests/cmdline/data/test_fleurinp.py new file mode 100644 index 000000000..cefa74d9a --- /dev/null +++ b/tests/cmdline/data/test_fleurinp.py @@ -0,0 +1,52 @@ +# -*- 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/ # +############################################################################### +''' +Module to test all CLI data fleurinp commands. +''' +import os +from aiida.orm import Dict +file_path1 = '../../files/inpxml/FePt/inp.xml' +file_path2 = '../../files/inpxml/Si/inp.xml' + +inpxmlfilefolder = os.path.dirname(os.path.abspath(__file__)) +FEPT_INPXML_FILE = os.path.abspath(os.path.join(inpxmlfilefolder, file_path1)) +SI_INPXML_FILE = os.path.abspath(os.path.join(inpxmlfilefolder, file_path2)) + + +def test_cmd_fleurinp_list(run_cli_command, create_fleurinp): + """Test invoking the data fleurinp list command.""" + from aiida_fleur.cmdline.data.fleurinp import list_fleurinp + + fleurinp = create_fleurinp(FEPT_INPXML_FILE) + fleurinp.store() + options = ['--uuid', '--ctime', '--strucinfo'] + results = run_cli_command(list_fleurinp, options=options) + print(results.output_lines) + assert fleurinp.uuid in results.output + + fleurinp2 = create_fleurinp(SI_INPXML_FILE) + fleurinp2.store() + options = ['--uuid', '--ctime', '--strucinfo', '--raw'] + results = run_cli_command(list_fleurinp, options=options) + assert fleurinp.uuid in results.output + assert fleurinp2.uuid in results.output + + +def test_cmd_fleurinp_cat(run_cli_command, create_fleurinp): + """Test invoking the data fleurinp cat command.""" + from aiida_fleur.cmdline.data.fleurinp import cat_file + + fleurinp = create_fleurinp(FEPT_INPXML_FILE) + fleurinp.store() + options = [fleurinp.uuid] + result = run_cli_command(cat_file, options=options) + # printed contents will also put in line breaks \n which makes comparisson hard. diff --git a/tests/cmdline/data/test_parameters.py b/tests/cmdline/data/test_parameters.py new file mode 100644 index 000000000..910e05c8e --- /dev/null +++ b/tests/cmdline/data/test_parameters.py @@ -0,0 +1,42 @@ +# -*- 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/ # +############################################################################### +''' +Module to test all CLI data parameter commands. +''' +import os +from aiida.orm import Dict +file_path1 = '../../files/inpxml/Si/inp.xml' + +inpxmlfilefolder = os.path.dirname(os.path.abspath(__file__)) +SI_INPXML_FILE = os.path.abspath(os.path.join(inpxmlfilefolder, file_path1)) + + +def test_cmd_param_import(run_cli_command): + """Test invoking the import parameter command in all variants.""" + from shutil import copyfile + from aiida_fleur.cmdline.data.parameters import cmd_param_import + + options = [SI_INPXML_FILE, '--fleurinp'] + run_cli_command(cmd_param_import, options=options) + + options = [SI_INPXML_FILE, '--fleurinp', '--dry-run'] + run_cli_command(cmd_param_import, options=options) + + options = [SI_INPXML_FILE, '--show', '--dry-run'] + run_cli_command(cmd_param_import, options=options) + + dest_path = SI_INPXML_FILE.strip('.xml') + copyfile(SI_INPXML_FILE, dest_path) + options = [dest_path, '--fleurinp', '--dry-run'] + result = run_cli_command(cmd_param_import, options=options) + os.remove(dest_path) + assert 'Error: Currently, we can only extract information from an inp.xml file.' in result.output_lines diff --git a/tests/cmdline/data/test_structure.py b/tests/cmdline/data/test_structure.py new file mode 100644 index 000000000..5d593ba9f --- /dev/null +++ b/tests/cmdline/data/test_structure.py @@ -0,0 +1,40 @@ +# -*- 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/ # +############################################################################### +''' +Module to test all CLI data structure commands. +''' +import os +from aiida.orm import Dict +file_path1 = '../../files/inpxml/FePt/inp.xml' + +inpxmlfilefolder = os.path.dirname(os.path.abspath(__file__)) +FEPT_INPXML_FILE = os.path.abspath(os.path.join(inpxmlfilefolder, file_path1)) + + +def test_import_structure(run_cli_command): + """Test invoking the import structure command in all variants.""" + from shutil import copyfile + from aiida_fleur.cmdline.data.structure import cmd_import + + options = [FEPT_INPXML_FILE, '--fleurinp'] + run_cli_command(cmd_import, options=options) + + options = [FEPT_INPXML_FILE, '--fleurinp', '--dry-run'] + run_cli_command(cmd_import, options=options) + + dest_path = FEPT_INPXML_FILE.strip('.xml') + copyfile(FEPT_INPXML_FILE, dest_path) + options = [dest_path, '--fleurinp', '--dry-run'] + result = run_cli_command(cmd_import, options=options) + os.remove(dest_path) + assert 'Error: Currently, only StructureData from a inp.xml file can be '\ + 'extracted.' in result.output_lines diff --git a/tests/cmdline/test_types.py b/tests/cmdline/test_types.py new file mode 100644 index 000000000..407766326 --- /dev/null +++ b/tests/cmdline/test_types.py @@ -0,0 +1,45 @@ +# -*- 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/ # +############################################################################### +''' +Module to test all CLI types of the package. +''' +import pytest +import os +import click +from aiida.orm import StructureData + +file_path1 = '../files/cif/AlB.cif' + +inpxmlfilefolder = os.path.dirname(os.path.abspath(__file__)) +CIF_FILE = os.path.abspath(os.path.join(inpxmlfilefolder, file_path1)) + + +class TestStructureNodeOrFileParamType: + """Test the ``StructureNodeOrFileParamType``""" + + def test_not_existent_structure(self, struct_file_type): + """Test failure if identifier given but NotExistent""" + with pytest.raises(click.BadParameter): + struct_file_type.convert('7000', None, None) + + def test_path_give(self, struct_file_type): + """Test if it can take a cif file""" + result = struct_file_type.convert(CIF_FILE, None, None) + assert isinstance(result, StructureData) + + def test_parse_duplicate(self, struct_file_type): + """Test if duplicates are not stored again""" + result = struct_file_type.convert(CIF_FILE, None, None) + structure = result.store() + + result = struct_file_type.convert(CIF_FILE, None, None) + assert result.uuid == structure.uuid diff --git a/tests/conftest.py b/tests/conftest.py index 6aaf061e8..d98cb10ba 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -206,7 +206,7 @@ def generate_remote_data(): """Return a `RemoteData` node.""" def _generate_remote_data(computer, remote_path, entry_point_name=None): - """Return a `KpointsData` with a mesh of npoints in each direction.""" + """Return a `RemoteData` node pointing to given path.""" from aiida.common.links import LinkType from aiida.plugins.entry_point import format_entry_point_string From 60959c2b80f8c3656c8ffa6de60e79e4a5911412 Mon Sep 17 00:00:00 2001 From: broeder-j Date: Tue, 24 Nov 2020 22:46:24 +0100 Subject: [PATCH 15/53] Fix tests, old wc had to be removed --- aiida_fleur/cmdline/data/parameters.py | 2 +- aiida_fleur/cmdline/data/structure.py | 2 +- tests/cmdline/data/test_parameters.py | 4 ++-- tests/cmdline/data/test_structure.py | 4 ++-- tests/test_entrypoints.py | 7 ------- tests/test_workflows_builder_init.py | 24 ------------------------ 6 files changed, 6 insertions(+), 37 deletions(-) diff --git a/aiida_fleur/cmdline/data/parameters.py b/aiida_fleur/cmdline/data/parameters.py index 6c2753f13..cffbb0d74 100644 --- a/aiida_fleur/cmdline/data/parameters.py +++ b/aiida_fleur/cmdline/data/parameters.py @@ -42,7 +42,7 @@ def cmd_param_import(filename, dry_run, fleurinp, show): from aiida_fleur.data.fleurinp import FleurinpData if not filename.endswith('.xml'): - echo.echo_failure('Error: Currently, we can only extract information from an inp.xml file.') + echo.echo_critical('Error: Currently, we can only extract information from an inp.xml file.') fleurinpd = FleurinpData(files=[filename]) if not fleurinp or dry_run: parameters = fleurinpd.get_parameterdata_ncf() diff --git a/aiida_fleur/cmdline/data/structure.py b/aiida_fleur/cmdline/data/structure.py index 28760374d..bded4de20 100755 --- a/aiida_fleur/cmdline/data/structure.py +++ b/aiida_fleur/cmdline/data/structure.py @@ -47,7 +47,7 @@ def cmd_import(filename, dry_run, fleurinp): from aiida_fleur.data.fleurinp import FleurinpData if not filename.endswith('.xml'): - echo.echo_failure('Error: Currently, only StructureData from a inp.xml file can be extracted.') + echo.echo_critical('Error: Currently, only StructureData from a inp.xml file can be extracted.') fleurinpd = FleurinpData(files=[filename]) if not fleurinp or dry_run: structure = fleurinpd.get_structuredata_ncf() diff --git a/tests/cmdline/data/test_parameters.py b/tests/cmdline/data/test_parameters.py index 910e05c8e..aac6ae6b7 100644 --- a/tests/cmdline/data/test_parameters.py +++ b/tests/cmdline/data/test_parameters.py @@ -37,6 +37,6 @@ def test_cmd_param_import(run_cli_command): dest_path = SI_INPXML_FILE.strip('.xml') copyfile(SI_INPXML_FILE, dest_path) options = [dest_path, '--fleurinp', '--dry-run'] - result = run_cli_command(cmd_param_import, options=options) + result = run_cli_command(cmd_param_import, options=options, raises=True) os.remove(dest_path) - assert 'Error: Currently, we can only extract information from an inp.xml file.' in result.output_lines + assert 'Critical: Error: Currently, we can only extract information from an inp.xml file.' in result.output_lines diff --git a/tests/cmdline/data/test_structure.py b/tests/cmdline/data/test_structure.py index 5d593ba9f..251998077 100644 --- a/tests/cmdline/data/test_structure.py +++ b/tests/cmdline/data/test_structure.py @@ -34,7 +34,7 @@ def test_import_structure(run_cli_command): dest_path = FEPT_INPXML_FILE.strip('.xml') copyfile(FEPT_INPXML_FILE, dest_path) options = [dest_path, '--fleurinp', '--dry-run'] - result = run_cli_command(cmd_import, options=options) + result = run_cli_command(cmd_import, options=options, raises=True) os.remove(dest_path) - assert 'Error: Currently, only StructureData from a inp.xml file can be '\ + assert 'Critical: Error: Currently, only StructureData from a inp.xml file can be '\ 'extracted.' in result.output_lines diff --git a/tests/test_entrypoints.py b/tests/test_entrypoints.py index 763a7ec30..00ceae9c3 100644 --- a/tests/test_entrypoints.py +++ b/tests/test_entrypoints.py @@ -75,13 +75,6 @@ def test_fleur_dos_wc_entry_point(self): workflow = WorkflowFactory('fleur.dos') assert workflow == fleur_dos_wc - def test_fleur_band_wc_entry_point(self): - from aiida.plugins import WorkflowFactory - from aiida_fleur.workflows.band import FleurBandWorkChain - - workflow = WorkflowFactory('fleur.band') - assert workflow == FleurBandWorkChain - def test_fleur_banddos_wc_entry_point(self): from aiida.plugins import WorkflowFactory from aiida_fleur.workflows.banddos import FleurBandDosWorkChain diff --git a/tests/test_workflows_builder_init.py b/tests/test_workflows_builder_init.py index 5e1cc7e46..15252f411 100644 --- a/tests/test_workflows_builder_init.py +++ b/tests/test_workflows_builder_init.py @@ -55,22 +55,6 @@ def test_fleur_dos_wc_init(self): builder = fleur_dos_wc.get_builder() - def test_fleur_band_wc_init(self): - """ - Test the interface of the band workchain - """ - from aiida_fleur.workflows.band import FleurBandWorkChain - - builder = FleurBandWorkChain.get_builder() - - # def test_fleur_band2_wc_init(self): - # """ - # Test the interface of the band2 workchain - # """ - # from aiida_fleur.workflows.band2 import fleur_band2_wc - # - # builder = fleur_band2_wc.get_builder() - def test_fleur_corehole_wc_init(self): """ Test the interface of the corehole workchain @@ -87,14 +71,6 @@ def test_fleur_initial_cls_wc_init(self): builder = fleur_initial_cls_wc.get_builder() - def test_fleur_delta_wc_init(self): - """ - Test the interface of the delta workchain - """ - from aiida_fleur.workflows.delta import fleur_delta_wc - - builder = fleur_delta_wc.get_builder() - def test_fleur_relax_wc_init(self): """ Test the interface of the relax workchain From 2010aca7c8a700bec73562e9f1e090fab7ae2072 Mon Sep 17 00:00:00 2001 From: broeder-j Date: Thu, 26 Nov 2020 14:52:24 +0100 Subject: [PATCH 16/53] Fix docs build after wc remove --- docs/source/module_guide/code.rst | 12 +++--------- docs/source/user_guide/workflows/dos_band_wc.rst | 12 ++++++------ docs/source/user_guide/workflows/wc_index.rst | 2 +- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/docs/source/module_guide/code.rst b/docs/source/module_guide/code.rst index 0d2c27519..cc1d9e841 100644 --- a/docs/source/module_guide/code.rst +++ b/docs/source/module_guide/code.rst @@ -59,10 +59,10 @@ SCF: Fleur-Scf WorkChain .. automodule:: aiida_fleur.workflows.scf :members: -Band: Bandstructure WorkChain ------------------------------- +BandDos: Bandstructure WorkChain +-------------------------------- -.. automodule:: aiida_fleur.workflows.band +.. automodule:: aiida_fleur.workflows.banddos :members: DOS: Density of states WorkChain @@ -95,12 +95,6 @@ corehole: Performance of coreholes calculations .. automodule:: aiida_fleur.workflows.corehole :members: -delta: Calculates a Delta Factor --------------------------------- - -.. automodule:: aiida_fleur.workflows.delta - :members: - MAE: Force-theorem calculation of magnetic anisotropy energies ---------------------------------------------------------------- diff --git a/docs/source/user_guide/workflows/dos_band_wc.rst b/docs/source/user_guide/workflows/dos_band_wc.rst index 290d85573..9aee76c80 100644 --- a/docs/source/user_guide/workflows/dos_band_wc.rst +++ b/docs/source/user_guide/workflows/dos_band_wc.rst @@ -9,8 +9,8 @@ Fleur dos/band workflows 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.band.FleurBandWorkChain` -* **String to pass to the** :py:func:`~aiida.plugins.WorkflowFactory`: ``fleur.dos``, ``fleur.band`` +* **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`` @@ -28,9 +28,9 @@ Import Example: #or WorkflowFactory('fleur.dos') - from aiida_fleur.workflows.band import FleurBandWorkChain + from aiida_fleur.workflows.banddos import FleurBandDosWorkChain #or - WorkflowFactory('fleur.band') + WorkflowFactory('fleur.banddos') Description/Purpose ^^^^^^^^^^^^^^^^^^^ @@ -38,11 +38,11 @@ Description/Purpose Calculates an Density of states (DOS) ontop of a given Fleur calculation (converged or not). - Band: + BandDos: Calculates an electronic band structure ontop of a given Fleur calculation (converged or not). - In the future we plan to add the posibility to converge a calculation before, and choose the kpaths automatic. + 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. Each of these workflows prepares/chances the Fleur input and manages one Fleur calculation. diff --git a/docs/source/user_guide/workflows/wc_index.rst b/docs/source/user_guide/workflows/wc_index.rst index df1da81f6..54f136214 100644 --- a/docs/source/user_guide/workflows/wc_index.rst +++ b/docs/source/user_guide/workflows/wc_index.rst @@ -10,7 +10,7 @@ Inputs '''''' There is always a ``wf_parameters``: -:py:class:`~aiida.orm.Dict` node for controlling the workflow behaviour. +:py:class:`~aiida.orm.Dict` node for controlling the workflow behavior. It contains all the parameters related to physical aspects of the workchain and its content vary between different workchains. From 7ef7d34c938222449f711884f0c46cbd6da56279 Mon Sep 17 00:00:00 2001 From: broeder-j Date: Thu, 26 Nov 2020 19:17:03 +0100 Subject: [PATCH 17/53] Add further cmdline tests --- tests/cmdline/launch/test_launch.py | 3 + tests/cmdline/visualization/test_plot.py | 31 +++++++- tests/cmdline/workflows/test_worklow_cmd.py | 73 ++++++++++++++++++ .../exports/fleur_scf_fleurinp_Si.tar.gz | Bin 0 -> 86766 bytes 4 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 tests/cmdline/workflows/test_worklow_cmd.py create mode 100644 tests/files/exports/fleur_scf_fleurinp_Si.tar.gz diff --git a/tests/cmdline/launch/test_launch.py b/tests/cmdline/launch/test_launch.py index 8c3254702..583d70f55 100644 --- a/tests/cmdline/launch/test_launch.py +++ b/tests/cmdline/launch/test_launch.py @@ -31,6 +31,9 @@ def test_launch_inpgen_base(run_cli_process_launch_command, fixture_code): options = ['--inpgen', code.uuid] run_cli_process_launch_command(launch_inpgen, options=options) + options = ['--inpgen', code.uuid, '--daemon'] + run_cli_process_launch_command(launch_inpgen, options=options) + def test_launch_fleur_base(run_cli_process_launch_command, fixture_code, create_fleurinp): """Test invoking the fleur launch command with only required inputs.""" diff --git a/tests/cmdline/visualization/test_plot.py b/tests/cmdline/visualization/test_plot.py index 10c0db58e..06b6f4eff 100644 --- a/tests/cmdline/visualization/test_plot.py +++ b/tests/cmdline/visualization/test_plot.py @@ -12,4 +12,33 @@ ''' Module to test the plot cmd from the commandline ''' -from aiida_fleur.cmdline.visualization import cmd_plot + +import os +from aiida.tools.importexport import import_data + +file_path = '../../files/exports/fleur_scf_fleurinp_Si.tar.gz' +thisfilefolder = os.path.dirname(os.path.abspath(__file__)) +EXPORTFILE_FILE = os.path.abspath(os.path.join(thisfilefolder, file_path)) + + +def test_cmd_plot(run_cli_command): + """Test invoking the plot command in all variants. + + If this test hangs, --no-show is not working + """ + from aiida_fleur.cmdline.visualization import cmd_plot + + # import an an aiida export, this does not migrate + import_data(EXPORTFILE_FILE) + process_uuid = '7f9f4cfb-4170-48ea-801d-4269f88792e0' + + options = [process_uuid, '--no-show'] + result = run_cli_command(cmd_plot, options=options) + + #provide a file with ids + tempfile_name = 'test_uuids.txt' + with open(tempfile_name, 'w') as file1: + file1.write('7f9f4cfb-4170-48ea-801d-4269f88792e0\n7f9f4cfb-4170-48ea-801d-4269f88792e0') + options = [process_uuid, '--no-show', '-f', tempfile_name] + result = run_cli_command(cmd_plot, options=options) + os.remove(tempfile_name) diff --git a/tests/cmdline/workflows/test_worklow_cmd.py b/tests/cmdline/workflows/test_worklow_cmd.py new file mode 100644 index 000000000..504f6ff1c --- /dev/null +++ b/tests/cmdline/workflows/test_worklow_cmd.py @@ -0,0 +1,73 @@ +# -*- 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/ # +############################################################################### +''' +Module to test all CLI workflow commands. +''' + +import os +from aiida.tools.importexport import import_data + +file_path = '../../files/exports/fleur_scf_fleurinp_Si.tar.gz' +thisfilefolder = os.path.dirname(os.path.abspath(__file__)) +EXPORTFILE_FILE = os.path.abspath(os.path.join(thisfilefolder, file_path)) + + +def test_workchain_res(run_cli_command): + """Test invoking the workchain res command in all variants.""" + from aiida_fleur.cmdline.workflows import workchain_res + + EXPECTED1 = '"total_energy": -580.0719889044,' + EXPECTED2 = '"energy_core_electrons": -316.8117066016,' + # import an an aiida export, this does not migrate + import_data(EXPORTFILE_FILE) + process_uuid = '7f9f4cfb-4170-48ea-801d-4269f88792e0' + + options = [process_uuid] + result = run_cli_command(workchain_res, options=options) + assert EXPECTED1 in result.output_lines + assert EXPECTED2 in result.output_lines + + # only one node + options = [process_uuid, '-l', 'output_scf_wc_para', '--info', '--keys', 'total_energy', 'total_wall_time'] + result = run_cli_command(workchain_res, options=options) + assert EXPECTED1 in result.output_lines + assert EXPECTED2 not in result.output_lines + assert 'Info:' in result.output_lines[0] + + options = [process_uuid, '--keys', 'nothere'] + run_cli_command(workchain_res, options=options, raises=KeyError) + + +def test_workchain_inputdict(run_cli_command): + """Test invoking the workchain inputdict command in all variants.""" + from aiida_fleur.cmdline.workflows import workchain_inputdict + + # import an an aiida export, this does not migrate + import_data(EXPORTFILE_FILE) + + EXPECTED = '"max_wallclock_seconds": 300,' + EXPECTED2 = '"num_machines": 1,' + process_uuid = '7f9f4cfb-4170-48ea-801d-4269f88792e0' + + options = [process_uuid] + result = run_cli_command(workchain_inputdict, options=options) + assert EXPECTED in result.output_lines + assert EXPECTED2 in result.output_lines + + options = [process_uuid, '--info', '-l', 'options', '--keys', 'max_wallclock_seconds', 'withmpi'] + result = run_cli_command(workchain_inputdict, options=options) + assert EXPECTED in result.output_lines + assert EXPECTED2 not in result.output_lines + assert 'Info:' in result.output_lines[0] + + options = [process_uuid, '--keys', 'nothere'] + run_cli_command(workchain_inputdict, options=options, raises=KeyError) diff --git a/tests/files/exports/fleur_scf_fleurinp_Si.tar.gz b/tests/files/exports/fleur_scf_fleurinp_Si.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..a2dd394489c4a50f66a572ed6e8d8639ab896e3d GIT binary patch literal 86766 zcmcG#1#lg)nl)(V*fBG+9Wz`r#LUdh%*@Qp%#I;uW@g5inVA{aZ{GYfZ)SIM{Dk2V;!X=WwS~T_f8!=DG^I6Hus=x*-hJL&50UBC{a(W zn5-!-&5H7TITzA}OUJ!TGB8w39{xh02qis-oQtR)R~)!j(N%dloD$fbBVK?T%z7$< zpDSO~0yH8;rC6!87x2dFyDcJbpdci1wJ=aI2`Qa|dh~4}kJOevMa6XnkmNS^PM7Nwibh5p8B|9t74?o>ci{T@L zsjX9DTPc7-CP=rz?2jn22LMH?q@{+`Jd zP+sGc?W=2{cpEs#D~hqmYvHLug6^*}{H({b{O3n~ENdwMR%+J&J@&;=Yk<0=h)@FUmRv%}( z_;PZs{j^x_)vT>)6W9jAH6oWHhAhK+8q#n6=q5SYi-6-7l)+ zlzf612#ALY7zp~y{rD=I1!Pfli{X;0&gJsLG6 zV1fK3OGJIY`8r8Te0p(#v||E^#r_(dr8gQCN5U)i@Vvm=-hLXw7rtVm9i5k!np1RZ zx3uK;=6MP6*}~uHi1{dM&z>CZAKwx)Hg0%7+p?=sWt|(ae2zuuW1KX0R+DDGOUkjfUQ>Il>LKq6=47he zoH}eQ-SBCuPBOcxEs>#reSd2k0N3OvW)8oyew@m7pg0iMQJtTnz-##=^L%o|^}t=lOnzYWXXkeG=e zMutC_UK0_6JemBE>epEs-X-|)e&Lw{_aEi08#!_*ZU|KO{hBD&jM9iKHe)AF3#_h~ zTQrvO8Otv35{n6gw+E3ejYbR}yrQ1TLdui6?A)F+l{Y*~S|B`}Uhn7#HeN3d0xy?` z2_Ibvg;qK$xZv%rrn#jwC+%*ZS`WV&LY&tT?cxspl>Uk59hVyO8230jI9SJr)!=S_ ztnWy4tI3P>z02PwjVW@k)@cmz1h@4>>I%=VE>`Zb|8Td1*+nyEEt^7=2OvKO&}_IG zwD`oewk#ENm@*z5G z+grW*@o2OZ$DThBoivsB%6;?Bl9Sy|k#OUo zkmkkgtQxtVRZcTWi*s=UWr(OMN0--5T=04x8 z!L|NRORWB^=dp39WHpBX5Z-UD_f9a-g`_yJ9OBIVJ{2MP3dpnij%Ju(poX5n(p#oQ ze4t#w+Rzyew9C9rmJo;XVeDeJSQi76@z!1~B6|&nG~5I`C-!N07J;(`flQ~?q`|2q zjVIoFgpq>ra*G4tvBW;wa2$p&c%a~P#OciH95|-B(d<2v$^8UQ-CY<^H-~(BjApWM z*eb#c0@Nv?O&if~b)TBO-o@fcqrch!*k%?<=o+ywIZskDO1S@!TSA3V*QBNfh3TxF zsP!;0(@YWT0&j60mq~BD?`>xv^2%A9`QqZOR=<++xHO_gJBexG^H%IUvHKzYX(1ux@y?*~M@JhdEj!kmeB?a#dBT6p z894szwDxRs80YZT*_@Y7o-d*HC}SJ9uFOW$tuDQQC!ghZgc5Krq3Tg|CGGo|>Yn)f z7N`qE_AQ@kle2%Wb*dt;K@;s**f95ItlwxKEZhF&r0ISqbN}cpmFdXa8TrsK&j!y} z!6X=ArHXyZ6JzR(^DXf_B=aX-DoW>h+jJz?18?$`H%(F58Bxw@8Q9UzoI?Y8l-Msq& zeK?>Z-DdP|eqph1RRLRv)va!wqO%0~+Q)OBHD*;H`@?!Xuh=>@28hl};bvv^R_D;^ zljnD_)aWn3&HyE3am8JG1qidAqCe|xjz9inP5(@wzI}jtb*qq0Y_pKggSlBu&jSw} z2I6MftPci#TY$y!!QECT*b^R-Pyk24+=`tS?D9=D9mbnEgi*lw6>DLoC27V)d|v);|h_*(K~*%P(}>d~0a z6+Bo!+C1ycy@x%mATI>2D)SL#dz^%K##JfSGtYL37G{9x2c$H$C zOMKH7S#MYio(r%V?+yWevd>TlA#y$EXf#-0)kxg7&uou&La7B5d z7u1d}erbjB6;kU`58`d|gFP${xz@ZL*SK>2$@mCqWM&?#%!vm^G&RGrjqO-CU$OB!sw**8Au#$r;sT?kuu%RVeG=LlJ1T4{kumwK0MOpqg2gY{J%i_b zL!>~Yo5dT}E0yksK01b4BSnI5ihif1@8x)nB*smT6W&ZCUZ}HVv@o02vZ2Ujuj4zo zxaG^jxbaB2q&6r{%dQ2!ZUDTlQ|6D;qRiJYu@t7$%6&iJT8@ZsGGNI=OmXOGoPr;x zguGTtNjx`4MjIgJ+d2;iz4=AqAz_nb+ww3i_VKZ{m~h< z40WaJflUtJgOUB}lu=9kf(m6HMNH_^+JHRQ^qMYL;1knUc*Gl|d#aHSIxg31)0;lF zudj8!0R{S}-sSlu%^IS&Eb@OgO~;7LZk)NK7c8RAs3Q11;0f1e{vfE6AJ` zW!#NF&OcEn55d?CfR{Gt%7aCwumBtTQ{aXctX|1cm4=>?$@;?tfe@NyV5JVSRpT_p z=TpXcX~|~Fqe4Zm#oQ`WE_14F?%V7EHsnabt~_5UGO;0-Zth zgYQh+R{!V?N6t@#3xsVc77Xx&!QF~*Nu|hF9o22SN+gJV^PR7+k&M^Ffk7_PJ+U3_ zBl|={a)0(5PWlFU_@JiAqx_kZ?Hg+~+KZ0Pbnz zN_v`9-zu$!A1vvksZCeRH5h(aKKastkeQhKJM}BAO%eTvF=lfr_oOm94C__4A{sL~ zo7yr;X77G^TI)P*$nEYLm&T%RQ_W^odv)q*v<=NzM7uXv@Ii<5AK1CzfeW^gg!`2W z@@ZoeI~V-*eG=Q52WR#2stHm4UQ0N(N#2y}dL@JOF=BoR`%#uKBSWSAL)R=UdKU>~7(IZH_l1ojJdE4eK zt#4PJxJT|sp{5>O+oBrv%+%3DVml-b1`m#UK`*FPUjlJWq$t_AFC52;+9WDeCyhhk z7l=ZP7=jwq(~kAM61JBFs5kbM)S4qo<^T{}wapV__c7bAvCp1z%D$49r^nLgS)65M z`_rl_@5cMj)m#ppe?#^2*DVg_CGh?#~AlyJWrCrzc&j&*XiX9y2IQ zj7^1>b%mCojJ9I?Qwn#PeYD(`ud|}B%dZ66WGA!lytL>+)g-(9m31Uvl&*cH=&v6& z|ET{5WNt&D1w3L+p~cGQpOwKJ#zYK248x!p_l$oW?)N&W&vcYf7n z$^E0Q=pWa*|F|J#!TyV3SN$Swt(Umf#Qux?p9wap%fue`;rO2f|CU?L4U%V$(8Q{2 z&9$Gt*IeqsHzQ&WgMtN*&t3$JtHwKzmiY~i1UQh7IAPqyLUl}53pasxx>8Wwp7yJ5(L$))R?TgEBHZc{C{Nv*oH zkJ)#i_efEy8AX{*k`Kbk?ZbdHc6EyL23-NQnF;aej&c94T2{qLR5~cNF|OP*1ZvF? z*@W3npAz1JYZb(d3|X4f{`sVfw!tl*2|HfRF^Qh=P=e=ylva-<2bY4;~_Fwku`7X7hQ>L6L@wqKR6ar8DeD~8XZ!``o6ot z;KtW{&BeIP8&k$ie%f&BmhJQWJX|$B^Y}4y^ld5YBfghkEMe#VA+TBr_$k!&D?B@q++0KFB`99Nh8wH#;9~rg@%#4I{EbJ2wu|($21_x zDd21>wJCW$D^ZpjdEdAC5BeNqMr*efegVF*l@4pS73u0cOUMi-r{F3!a)=tROK}$< zK<(QB{=A`z%T#h_W2im@A{;d7)~5j2$_RUHj#w1V$lQnFqqCvUN72OBS3_5$)~dGC zjzDU2lB6N!eAP{#YE7Wnx7gX_a9GB#SCxqHI}b|!J%?H5JiC?#4YI7A_e3NInO*~B z<04GDZWu5(8!2Q>-sx!uEeJf>a}PRq*07}t>rF3aM?`IP1p%(~zVn3SCh12`dz7++ zGJpS-Amd=+d?*e)Z-3xLWEf<^zB5hkl5yw9-H;9E9m9u~nyOCfiZ=yv1P^ax#Ygqr zbTtvhflD+EB2GX1|~*rp07+4Rqg{ zX>ej_&7i+7_~&nWhmjHIs94NC^+Gr*;oQ;KFv(cc^U`%|&>4B?^-F;jAn zREJyz(nb)lj6-20^pc&DDFxZkB8@O3WmWmV7Z3s8Q_{e(ZHv~b_IBHuI}WCaDUD9b z*m#@FtXpCKahM{po%7Re7X7NoKBuI}`FH%(?3 z%_6WzD~2A_w6Lm=C7^&NN+ZE)9)%i}-zUJu#)^O=#0YE1Jc$mLk${z=*bcT8R)uIV zDvhHgo|iSFKtz)mjU*wOki?;m(^sV=5KmPdtrgsXH-jcI;V@NkATszN3%6s2%E%>! zAps7c{4s+vlsTt52sc1stQ3 zY*9-2HNZK;>V_>lS6SQR{y=qSXEZqh&I)P$Fbb)HCDA2IMY-i>Mfy*?1xRL+z-}0g z43qk&e8h4;9PohSNF+E0u~*nREHMt`hr}#P8z2@!8;RyXh!gvvgYO~FN5K4`!oY}K z#Tf#~<6G>zL*bub+3G{!(1y(oG3ZeO!;475>Qc!FxS@v=jVM#EsX+C};c6nOsJd-g z)3#u|Q@gjFK`Y<#_)G+`q~aPG57duP*q_A`P;7Atpoh|w(a=yw69==u`apFuCUaEM z+0U*ycVUa-5uj^JxFicaCNYKZickX*#3Ha%nLJRCdE7&K`ED#hBvlFF(q?35nHbe> zS2dV3)N13pMDwB`1y`iR-^oyDs4^5PYy})fa0CM6j0vfBQA~Oi=c@xx7{oj@8g}VO zvTJ7JHBxDfR0^}=H~|I8qPjmsvlTi?$S9RDqY+io`DIufq?jB#vs_EIyL}M@Eo5ht z&AKJOZ9&ZzqJmEY2||b+AVHK`Lb$`+dB#(!+RT=WleC#Qy&5NZ9X1udvmRut-CKEN z?dGZs;oj?zXPJGLWSZ(W!H|_}a=s+?_A-YGmP#u1qY%c$3k#<1upx$qNrK1dA9PH> zKx9Ip-WC-)OmT^~;4Xl5rnTF4{k+4bUa}zrgOqOql$sB_^ zVkAa?9T_t^%JFpn%8G2EjoKdjgRj$S(&5<_Y%~cgf>750lv%xwEcIBP0rwNcVOG^qm|FWx|PoRrW3yGF%VIk5^=BG27X(Lc#C0l;AOe6KmrBI%Iun zVA3>Ccvz7{Ua6j&DVIj^5Y*C;a?UYgWt6FBhQ(JtRZ}M97rq8rLc6O83xjGl6sC}$ z8V*gb0}u((+K*$s1HWVPpjAZWH^^Z@Cu2OHz15~QAqa{As?MUrQxnv1yCfHhEN9oqN=ueJZ z$&E2|lD_Lf{~QZYO_)KT{q-`8ay%Ro@TY(gnq`5xJXVjo7(cA;u}7G_6n>3r}spwm)z-E+0C|G#_pzwLeXq_8{)3Y5~)G zcDpH=4r&)HJh=1T`(v?czY|>q30W?#0SYFFGD`$ym_D(YVX2YY=r^>p*$(z0B9r#4 zq5S3_CJA(FRhz&G(Jra)i{x&yguaUgI$oqi7!yI zc6Tv8ovMy>iwe(BacyInk`3+ps|67iwuphf{hI^>C)Yes!@eb7qT_bo>*Cc_AC=>Z zev0h)OOR#IC(I$uL40<)vRhfMOX}<^p#OgHZN)eQfU zF*6*qgj@;%G6PC+By-yOV$C|F=~vCo`pjRsKH4un-gG7=egz(%UUYROoTwK1LOLs6gN8dW1lxiz=wIJNZA!E3GzJ(@p@Qs zKb*MYN7&?7O#n|A=C_{a%sPq=r0LIq~AtwEM7` z#W0D!>c(qRE@c$HBC=FY?yl@#hIq1MpawXNb0>NfnrC5x{IP+8Q6XfYxN2Ol8M*UV zVaEXoa)WA9Sfwt)J{u{67csHMc0l=2FEVo*zQh+~^|g60mD1*-g}!fj5b#`fu?QVx zEo{~J!T>oWG)=*(DYCVdN@0xEj$`2@i4>%S)X38Z$0oqsNgJcHF;9w$egCtD0i`Y! zNtP;77un3Z7K&QRz@QtmuE*MW%|BBSeR)tr|b-|PN->@qv%jr^*|r7|XRAq%_0 zTD-Y1Vted+@xG%r?;9HTwN={aW-`{NgK5{Af+tX8xv&|sj}??4=pG0cbQr@<`dBo` z#fj~9rQucOqertq_`apeyA#U>m+}Ie80453l^;f4W@x4PYJ!1Cbc;TSSmol*THLq~ zscU6Z;qE$S1}zW1*>BF-j>z*i!*`_nYWBTS=S{hegR%V6U-ci#4^7l3qa3)?LdO>% zUrVi7{XOfT>RvXm(^(J8zv&viom*AR&d$+ec(o$gY}h^IRk21+FI=>3j?~3eWrhF* z^}O}9GgA%gAe)b4D<=&^4+GICez`LBf%sH`K>QpMLG?^~Ma7G$=S!B$XX&cul2n3% zLBKk6(N-uLhD4Ld=r6QjO#HDI>rle-GXyK#Zl*4DJdb4i5QNM3)Eo zWufDiNxGh^GB%$A|@}$rXO9kh^PSUMYpq`<6iu$B@kn84$Wj09~?C zXn;2BX_{clAu}7g9lhW{4+}!jZDM8s!6i2oVJNbJEJ-b$eKD$Yq@ExUqRZ z1*ENXOuU5i@|c45k;5=d{`wi}Sgw)a<;IXsZ# z{dvd=4(38h)x+*C3%v)(8W}-56toa8Ft)~4L*$0l8KneALNP-z#X zZVp>63L{x*Aq5iFZYU*b0mkW%QAR009>*o&N33d#+91`8OJ)5KoKnZk?NLjrR zK1^i@^D(s-i0;HQn5D%?;oeV&`!L-G%mNe+=p8I>b(c?RdS|4N&G|! zKHv|}{p7@e)UV@VL)uWZs;}KMIUm+u!+I(7a|fa;#I1ePEPb!}L{76+5ua7#mXB4} z`c>QMJHrP&hM9H}{`_M}8N7+3@mJ@Z)^sA$#Qd+RcaVSpvHXp~|^di0GCI zLSxKMh2ECC#hY;i5JIGWhf4^Po&Oc@U}kD##_y;pvi9Tyt%s=!9hWq??jN2YYQz+!YY2bT-bvM_`f7+{@ zCYAolYTr@1ZuENwu<5hkh2N6bOxOAPwKtE67kOoPSHb(oS@n~{eA9M^3Q0x#r|WNl ztE{)D-(X-fzk0UfO{GK6+^tR%aEkN+f`(wlh);b~jDaJfbBXenD9Ti+az%i{07H(*gdAU1J3nd8 zN{FrN@op?P#|-!Ew}FT4&;C0ftEkTzUM)#&b*a}hC-i_*qaF8bKf@ry7xVJZ?^!n!-k$A{ zdTGskf<-Og{arRmJbt>ys4z}mhWd!S5w;1YjzZ5}ysKyG(%4umyBN-`lMDm=bqDL9 z_EcJ%Fq#v~P~f~Kbib`cZrOg6^7KQJ2L)|1UqcgoJS6ru@Z3yXb|1Zv=p7nW;P<9? z@soQp;p5NnV~|NAo^RMfR{wq~u2u_J{OKuj$C;17?^sF>CP5pd9D14o;x2M>J|Wbc zY82kj3gSdW6l{uvIck|m6sI4im@j(eE@%@;Yk!IVE(qN5qv)KnbQhk$JR{wH{(!q& z#O8L{(knZCyqq#xTdv*wkVCLbjLwr2AAgn~hs-w-gj$N^XO8HPRCIl(rkTr7o}-l@ zS>#`oda9HlpPG=zK1-h#?+;R{3{|JcFe9I>gdD>}hnwBoE*7*tRU^%stYx#HH->nJ zMvt+seD#x9N3ZNo7|5OzW{V{d?Qhm5ilEw04Y)~VgNo!3d(K+4Sku0lCr$cu5mQ|s ztrpDm(aZa5n6p{ux)6q(8}LQ7Qc0lMICAA8^Mh$x%$dMdeQTJL-zTkutPpS3Dz&&5 zuT8qCXKBATMyO22UYtR#?u$*k1WhcP%q{So;v}hj?;^y2b3NSSY4sCrxOB*P0ABYE zV&2M{Xo_#!TQclGs|_)Y+su>zEwt&*&~qMS>EBpVleLyO4K~2MA_~_K)3nMUqr;Qd zeH@SnY@r%8F{1E9GPc5>K^U%X!VjCE+UV#WKhbL`d2n{OO+IyszfA%<>Ck#k$3x24 z8bNqMTpiFCHz*@PQ(3mO?%VpXeA8F;l-QdWMA$c~oT+Gi5LPRydoN|A7TwluA}|DS z0BZq7Iy9-7oNK~b)qiZ-3{U1#swR6(ll*m+6J_Ib`zP@75ktJ#}(>RIZ)8Y_%bPp5+45Ep?zOPEE9#nY8x~CAF2k ztte2N0i{5+r+z3CEIztFTTJ8-LX1yXwbiad!@-Yz-p`$Q9^_G_L`(or@Or=ZReeJL zpO~Njdsx~(Vt)P^md2p>ACYPQ8;lX~S1=}~zk)IUolzEszhYxy`70PJ{a?WVe`ge6 z{8wyjjDH7X{yW&;y*Kv1YXZCBUl}#{yJs_C{W~`Hzk(V59h))R|H20MuTF0Kudi-S z%Hh96Y0~^RB)cI48w~>!s{swOA;6eMk6xdVhJ^vZM9<1NT11wD16}0?shsb}(|Nn+h$HBnV$k55s$ezZ*(a^}=p4QRT5fteEe5wBbF=ON8`2S>v z$538uA{+>)HW~IE;CAK89jVhwVODW(f+?a8L8W;YLLDEg18>>XaBcb&H`u1z1qtv6nI z048)8q%a`$SUMd4p!`v@F)Zk_PU5y}A@OtUbBZ$lbAW;tDL#ihwV^Vq(SH^H2lHy# zDp1%eam8xd`PO~lqNuJVBn3cOa7xG#WWc0e0+evGhwpe~r|h?ut{hjANy9H#u&l)J zG=?*Xn=6F60Y$(OI=0#E%_*?xgP1q+g7#K@QJhn2HTuyWJN50+qu__Aw1MeVlGk8Cgy)64OJh14)2)c@GXw(&mvsIW(Ij2{Fz zs7C=8?F|&vFEdO(Sg=x^O|s?spk{Xp^8j1g3>(fCN&{)bm;`-m5u1j{ka={G0l7B_ z%*@$P(Cxp^N@f0J*2PPESUpdLm`?N>XLp1^sYhB0pH}nDwrh2@WrOujxHbd(GvQ|i z%HwjlW$Dh>LH6f4T(?j2){i9^?3erx1{S(p@&Q>Co| zEFgu6*kDn{7h-rRA-IL5N?-BXE?xs4DFZL>6QKcVZq|6_--}J~o2XZ((jN3qgrqcdP1l z>`_y{EmvSK;QZ)@_NnfYz~yZW?@3D2i6mkbQp02U2TdiUZ~fb#+(VK;Y$p%kP2!j? zcTPUVgZLT2GD;$dDijZxDaho~rZ%BFTVcu#M_x47X#Gq$n}Bj29+#$;iX_My0`qiv z)KE5>ik|rq64Q|Fq4&53o!xWW*?uM@0ptaPy^vf{j&Cp~YceuAvD%{c2^h~tY0NsxX#y)&jS9i}=cX~fuh!6!Al$>2(ZnzZhh0h(|F4_h> zqM6Lt^hbU_s$THJ)DuWEKnp>g_Sn=3k#(wF#+y?)Izqi|^f(1@yR5s>;S~oH{@FJ6 z^)qW9BznvFjvID#Pl(9Ol+GSrNZZtDIB|ju-z3h3UGxySUa7`$=`e-&6HJV0=*)Lt zmM-3ECP%35LHz8hAxW^jPVM(&raJ$tp8e^)UErUwJ5P7s`mN=$WE!2uU*Yo-Kk8><|5N{;l31+R2CT$pUXFT8FRitP~uXeHP=} zj0+LUey6)^8tbc&wwfRXfU~i(U6V!~;-H9D2~%TF@jFo-?btnXy7?Wp!A`pnwo;>SJI}r%P?h}VoF-jy`v`Q!+gc=lJg?6~&BKJhJ zzx(Sz4X)DziOJ^>;A@EXZAX&$`ykZ1uGUTWcI<}jQ6D$Xfe({OM5t`=yNYIC44abf zz+(AIf*aMej+tkouomivnN4hFwqlTeAi8}QUP+I(%9Qg(-HB}oqjI^mBCdU_pLDC> zH;hR(4JXYRGZEJY`xU{TpO8Xa;p zx{;%95cXLW$+eVi;z{8ENh&Q0dQ=VWR~2-;teNt?R}XL^=T7`){(_d@HCe3>oxE|LV$i_B6kCQVhvO=ZzPPNzlIy{if(_1c95g|3MPHfom z7fWPENo&Ab(P{#yytXenkfj!=?v;#M?)`NtM-#=*V#c@o%)F9 z2yx+h3FaZPT3pFawU5al0zqs;wGZP@;&XZ(tE|@E2&5kwwrNX)?k{Vr*BmX`B*ZXW zW{jW5nybeC z7OWcV{e6hwd+q|i5zat9{*S9rwPvLQgq$>pCC$iIST7dHf|Cl6+@nr43Vb}MU7PGf zI5b%QKQhD;zt}JMpPd(;PU|1@+y&fHDBS4bxRiOHxQH-1L(8d>jG_FLYU%In-TcauU;p;SGX|_Ev25lG9s0uvH8UFvwHe zi7Ak90NT&R1Il44rW#>&_{tOyz`_b0ML%@60$i3Pnu|-k zq6RKe$H!FPCk0%;U7k&H6}{hJ4Z{6UxS;Ct+xfFYYIts=+X%WDUgQSRUgTlmrbh|7 zQXBFx6KvAkX80+d{hf&4EUS589d4G;gv zN1GWhUl5{#%h5%i?kZyVDqLY0U6G_e=|5cvEi0;J5*;c0=`s~I0>{z#kM}f!Q7Z@N z#i*|PXPp@wHMkSW0=)uXr9gmLaB_5AyY#f&FUU=~DKWb$l|e#yWJ=%DfB3m?7N=hD zQUvX?Z*BIZF;bl7iSIDULp+8^PgeIQ_l5lNn>=@~GLh&U-_rm{puQ?^XQvXEvdnNTqBWyfn3|Hb954EnCdm9u!d@ zp7vOK(PFD<`p;!cA4{xatU39OJ54|5lo+(sLhrx@a~#Q{&2&Dd=-c`$1awnn)}5y! zaEUNlbo# zSbART4c39m0-Ts@rY62eeYcOIO1pQCRZs*;v)5UUa*6O`c;U7NHV%G4rF+see2{f# zxw6Bt@5G4iLx?c>e8>zgA=2jd)bHa>`+BYE)5FWn_a3dQ2mF`8lK@)LCFrA zX*1+LY)8ReF=weF0Uo-bnO=!ftFRLmHpcQBUDXOUb0^?yW}?S z-W1q6+TR;z5zLP2z$0?t`2KJj^Rn+l>SglvZ6}!NP9$HX~;5U>0*bIdT*z2o_h1o96?DQ@#xAm5f@7GzJ*#tcdIXZ*Z~+e zu@mB-bHlVy{j`$K#0w}Yy{&?x8`Rx$;|!WB-<>8_^R?kM$D#7BsryZkMG7wmTC-qLo^@cfz{RD?2kGBLWh#{Zq80L}r)}qEdTLspWhNcQUMhE z0T;tqf3u>o=}u0nO!i!rTDl&+@qW1?I~%`|vte2Bw|aRroqV+HnW5uwPjFwQm+T3i zUhMd$Wgymm6zNPLw^P^~ki!{!zun@n!s z=S6dOPA#MXDZ8ZYh!@4h#>ctS1W!9w<9uWqUUzm2`O6tLB)$01^YJx#$g~Qh)d1fK z9W(cF&wJ@Ron|9iYOKR6B<9=YaYGG0Kla1uFlSE87PP=h*is#dYr6Vo$gwCAR~^Tk zs&yb((?0k6yp|tIOr>q8w3lS~>pMqR8q2#*6-H|lBIt0AuFaf z6@mr(-N-eZAa8a?v87n4d+nwS#26?A9wkiVX`X#dX!DS7lCg zQU9T<=f(4s!Vcp=N0p3Les78;HkHtA$yFd=jLR8ro-IQq%wUSfT(jPkeD#Kt1+Qx= zzn)GaAVX{B*b_@8po$gsmC1T$E2?s3Ia#=#?e#7<)C7~}g0ZuYdQ2*$uJ*w$@O5?@ znrD!?D8TV4=!3As8vBkdyVGv}5EVi*lw{=E0}u3qSKF>B zFdVTeSL)cdPQRZrDHzp+8sqxU(Bi(RYO1Z??3qU(>T2(xzNmBJ!uQvoH?Mni43c6u`i}WDN^pd)*3zn5Acj+Hb4u!g@&xEiS zcEZzd{IsyrC3FWTYlBgn&;L2R`E1jHs zFx1*epY*q-+|=6Ith3EKQ{i-SooOUOSam8B7%%bOkVO$D4tsVnDR zQFcvr2V%6H*&tm$LY4tK7msy2HOEOBzk0=;2Tf93-M>)FgE)W3TCFJ0`!SNuU^5WoHEpq5ljfAh_X+_h$$wI?Na^f)Z zNuO)r z@aR91h#EUorgtKkcNJsF@Ztq$>Ab)O7;XpzZhx{HQoFlMS|t`zqB-1e{e)C-5n#HKVO z*vRg94fSC`JW|@B@;>YnU{P^(U(93#8z{Qo{i)KbLM^c^b$~=bxGS~&aQ|;Mv$wk@-&#_ zeEsd)?XFii2_hu#5!9OdSI?uJQcmP1vui`sQk18fI*!r|HZL}R1b${J&INwaz|ik0 zR8863K6Fj@c!0r??L+Oi%5ioMu9xKc$xtfpjkmzTgI;R)w<`3@$`u}N>lBmkDf+{{ zV_sasYM*|<86$eXSCFRF?K$f}jTBTTovU@hBOI(}N z8wLyPq)Uk7RCEM5g8GE&Ji2}lG~ID4woKht#C*Atnu+R_Uo%}>K<$s_+aOU78cejz zT7Zbn2MX-PfmvLNhd=n?u+;(7C!NnF^yUf1y2)VBTsHXIk>TKgsP>r+ugRbe%Vei) zgW+ITvfr)OQ0nzGd5T%56T|jA_wP%?3x@5_t&B@5B1tyrYQ?xxgMJyt{g9m2!BG6w zuc@~~7;u-%^E~jun6FCEVZ#L8^sWvUu^9Em?C$j&iIt&-<5R=XS45_?qiaR)D*q$D zx~7#F7C`p-lGmwtGI=tu2f^Jp0vYt%$K;;PR}`W7nvJ2xsQ@f$ClL;29Kp|^VvW&^ zL2$X9AyC8o=2yzAKnEGKcv4E+s%_rR9>P7g)52|wKm3fEMr%$gD(P^=G0QvyIMU%U zUN}CrjFSFCpAx5xTq6D=Fv@^qD7>BnIH8Ub9iO~_VV`rS7ecOI*SarR=fF7f_CHc% z*F(-4#`dBUJ+6*5@8ou$@SE}4p11XO;T13C#^NcLgNyoH{cJbTB0a5;X49IW9mrM^ z@Dr{029dxQ>KmOXE@iY!TACrU`a7fN?J9dnZFXUX3Q^he=dM=cJTM{XKoTCoBd$c4 zCa5tZz(Y6bYt(jgLj0>d-_;+D^#+#A;vsLDu8g$YPKHOBGsnrQZt}B&wJC6?3U^S- z2}loV;WGoT4T^prcp=A*^mg%~OY>39An$1C3=+2aW$k!m*)^A`Z)i4fL3GqtK7n;$ z1weAFOiihNs>o1Ak1>u$@vqYcf6YS~C_W6WGB0gWYTZ{L-n1}-bf_L(ydH$}iZ3@N z${W?NMu*=>fX6kz*D zOqI81fI^GY=>@DX#w16`?Dw`=L)b~%v}L#vFXzxGY%tvW6kKPQ+7R>aB6 zZjQbZ!h#JeCm0u1oY=dT>1ts-hG9DwlTF;@-mt+8;`bp+^IhF|uVlyF9$|u-t6AYS zjn>PLweN~uc#N#DG=aA{>d+HC1tV7@JQ?gyymd#PoXa2hZpFPWKUYmK!$KLux{_UIP`nW!D-`+O2$lG*d{ z8+i;_c+ z_)a_Wz^RcEEpg~IOB)J+O~rFPv=fBNi3Q-_OD)^xb1*SKqJYvosu>7Fc*b2T$@VUB zclu7}zSff}Bz=fMHIRqogvIWMmmPkhI=?Lvfk3HUxd@Y6WCbo3eCluNU9-=6?50=G zm9VnRDGip?5_>HWB@f3v=t)77XbXc1)d4CZ3(b6M&?n;oBgX9E?@?zU2eVErvpl~KS_yfAF1z1_ z1+!?3DrdESmE7+F=GeA)Xv&Tw#56Adh8F^w7;W68+Yco287pM-@ z80Dv8*Y!rrbQCu~)%l>xN*+%$^!?DLlUvW99{Qs^l2AjlE&!e4Jx*Pb9fYpSb3R%q zxQ|NAr%q8V1*3ayetZP>P&Ah)`%qgq3{{W#8T9yNIQqR?R?&_!5}kP>dhKfX12k@* zXt#Jg8Z~o%t~%BA5H)mmrJ&1rjKXtn972AQ+Z(9Mq#WDj2^p|iG?S4Blq(30BgAD-2vqR!>pYti@8(5Vlc zKJCWo=%-WZt!lcj&?twhn!r!5QR$p==IHJ>=s?+IyenBIn#s$5((>Y4G?4!s$bXZC z?jMS~m$1D@J7UFeuB2t7fip_?=0Gkw_g~F->2DsYnxs43_u~V~z^0|{*jtENQXMAU z`%{D#sw686aeYLi)2Gi*XnsO_KYu#c`lJN?eZl*4-+U>m;67n*cC8$>@EQ_pn5sap zt^|dpJ@|}j2xM&gs((ShM#WuY7Wj&0eUl@mFw~%_$P*KL-deQe&F^Z@3*XS!Z`Q$p zV;$-@$|6*jSC7`g@w`hEjp#51Dm&`ggfiV8lJDJZMn54%FVm7+(S+^2qdL8I^uF%U z)T(GFI+JKDc$=XM{dAHupop^@4V9FWsyx$!exMKbj=R>2dMUU*(SP2Det)BsD7M^> z9!L#GY8efp#`H2=4ZJaI}Z7x4it^DwZRL6dybrwmVG-pOo?FgzG#m-T*F}RgU zdtnTPb#Ls_|Bj>6TgP3QgeK9-WETBWSfBO9YYuh# zYTlB0G>?Wpwn(1LSVVW0Dt~WSE~7uYob7HXuA*PBYqL!!t)VWdqxyTV*HO6?9KTr%i6va z7(0K{^jrlccCMbEvxAWut0{i0#Oq6g6&c-6{I^Yu1+`QEAU>kUm>69JKPoX|VGoK) z0Vy+P&fsxr&4UG_8h0~H@?gc#y9WFA!EBh2w2v(QiXBsTk1A&S&Vfx>d@lXL!i8B~ z5-drx=f*<5Ez5Xy@?h~-nIApW;lt#8mo~Y_k73}_sJ3Iw2`v2P+0z61{8-_ef=!oH z0j!iu-ohtY5WBL+cI(<(Aq=&wp4qGs#;jJ}_lT~DVCD_=@>5b`SaSSM&6#_ru%egT zx!jZD*rJe}(`Sp*7`I1Us>YHeR=ItB;UHcb>kJH`lhQwf=hjQUV>n1mZ{jNK8EG%c6b%gK9KV1(QDJe?`OZ1&;wXZ^0W# zCufKmiwfn0T{FTipkx;|o*85FYc7kICrmIdTA%qB%4XOq*}s^r5_4?yQ;*qQ%mT|B zDO<4nbscjE6{h$bZ;9O(b2q$FY=w2*)Dph_&KjGb1N$yHHW-DnsqMuoTdYeUF;uSW|m=`Chv?387r`e@3*V-o>pRqn!%lPl2w={?})>b z@h=!tCmT6$=~oOnQ^5PfpazQ(;(JZJUW=JI#FF0f|AyJVnLFUmuEQ=o+pj;fSdTGR zU+iH}YsBIgHtSSMny{W{-QIVx7VOQ9drE=ht=PJ2Gb)$Sj&*1b@aKheVwF}-mQ2cB z7;^A9byL3^bD_FG)9%}YSuN?U?-cc7Y9M-d`KS+zhm zOkoEuH-+W%A^FA6rm^NYhV!Ltvsj**46{Ps9Om0|Lld=L!1_#3O^ORk*tegpE6Xw~ z*oV5!gL=_5%wndstK;H27S;OsGSmGH41SBr5a`&%G)Svnf0f_H@)S1Y>PvPo(;*Hj zIsIL%j>_LrmT4agoJ+T0TRgxXxwY?_F8;+5ORMe<(~y9oW(}=}vZUaliR6UVZ8EU; z-NU@KjvO%8H(eQ#qXe6}&5?f9RKQs(wz|Te25i{ml}$<0fq{w2V0Rt{z{hm&h(w$T zEKq(NDL=yk=6n6`&04VnHG7H^7joD@n_cWJH#!b*wewqHU?eBt-sryL!Osm+6h9?J z5AuNP{1N`r#eCpiUX9;&!EtchhBNy^?@90^k!MLNP5`JGGwvM!E(nync~l%lg~6Uz z0om#^5x_oLN?I)>283Jr9+7pP0+*#)V~;I#t>ZMo&HMp#R$j?Ub&j@WCHkeTh+YJn}J6~bD9gMu7R_4-R&*# zI`Fg|`%E2T3Ap8?tDXK>f#(;6Pikq{fXjYn?FR+6z{ZhDIY8bXm@P(Ln(KD}mM-U} zAE!70(f&fM(I6Kv2Sw#0qFh1Ou8NIG)oq~Id%fu6V|So2yH$VK;{hfFatd_i?tmdR zMwX}fUO;&J)_k+74~X?{#bx$=K?BF~9$$|?xKlJe&fOUR?hIdFA6W|m7X_-O@=peX zex}FeCf7m$koFYeFA4=gvz#A$PKE=7p_|MuCjx|T*r?B6eE{yIWOlvciv}i5WXmg* zj{v1kTc-(A2r+<_P&S>ij zFD55}8z(!qB(9_Yqr&B1S57?#-r4OEO+qh#E^UssnA%H__0a1!@5R@EtxWg!M#vig z|Ee*c>&pNvAsc&%@GaQ0Vvm|?$O1EmaHVj5 z0M`}#etl9Y1fS;F>P*^;z{(K?7sB`va292YCfI)hp_wkRbb}?pB>U`fXWKI1x#N)f zjG+Pu$MjDm_f>)_%ZY>M)m4C5wm^5ar5d#A)zUK_62YXWpRu-NEvPeo*(rVd8)zP= z43loI10)~6I!qE8KqMuf5qWbX2+W)&UiWAQvGxuwclvnF3=86S^m)ccDfT- zl~4EgNOgf=>DJrhCfz_?x_-ZJB+Az;`oZ`mb>EfA0pNX5%l0Dj z2Uw;zjFHj!37-1p6CaEG006?GT8y?R^*x{|6M3t#Z9INZ{L@b>B{NQuz5wQuXa% zGAR4bS<0Y-916yD2}Y7r!pt=xA%0sbsHXDHQ1u5j{B}EnM9hvB?ijex(bCYvXdMGj ztbqZJT~g>4PG^GY2b(TQnk-Pa#cRy|IS%m#Hc9{u|g&kl*1q@I)Q95DCz z#Mn7rF8H-OrTvW`H{2GUH8{D%1J@f!ZS%eOptS@2m@)ftXy@f*L`Xs8~1q{kss1;;Nr{yetgEl94wrFthpApxr=dFyn&PeaAakGstFB;gYgy+-ypX?Un>d+QQ*2Kqg*;&Zw$3oV`! zj+$ z{@JO8L`ex|38XaH#}FV@5KZ>V8UmwArgquvFz6J2aY$llg!Fc zN0a~N{(>@mP-(tS`dtN{$XL#o{h|sr#g%QUKB~cL+$f>%qdMf%xOp=Fiv~36eLi@* z>mr=K@$`hoiYB}?o$hSHbqP|NXbsnCYD4`*Y2y0`9atSI&dS$u8OELq+>_+L0>$2H z3TVD9zldon3narv3Gc->tTUJq(X$x362lq)B+| z-iQ^H(X$V{eBB!MI_u|hj95cMAxZSUg$+F0q&snT*aqg+m3y|D+QNILrWvDsw(#xd z{c4gcb}(t&$g;H24hpdPiU%R~kZz>G>wCUEeDpJbq*#Bl)DY?bPjUtJNXw!4cShRJwmYR zgOd|1N|X`I;c$XEJhZr3o+I>>H=({N=?IO}pa0}YaDaaV>@M<++rx~bz^W#8d-z+U zhTtV(2mdhWa5xIsLW$Z}NrOAq5OERzwv=lH&mOz7>F<35-tJGeOw+suReNKF#M(@t zS0Rhh=&k{jy>eQx>-iN(+Tp9|AEg1sRy|fbZImF(9bVr-OBU!eVUGAr$`R+5+h6W? zs}r|9PWj|Ly-HMJiAk~eXiDVIeP3fLX+`{ABc_Ea+Y#N&&c7T?cOqueRW~ZD-6C#n zZhQ3~^C0%%tVspccZhtpm2EeoeTWl5L0*S%g&iohm?h~h<2ERW? z3?VKt^9EX`g%Jl6UEIavBZxP%T&G^YeLyUmpKP4yjV5}?1nRH}J|+?~T!(JNJRuUw z&v28)#1eOprnW_=;)uEijl?FM1mZ%%Q;qbdM51G9^MZp>67lakesO&#ndrFgv&Z!z zmAEuyXYkE0jp*TS_kP7Zop{^!pgG&{74iP;`!f$dy(XSJdGB{t%NrsWX@;oIat4u7 zFr1c`|1GiP^W3|9%PiuQ^gs7^h3|;eDqc_CGG`MFH7G39f^&#*MGl9;WO>BU`!$JE z&+>`2c2;##XaUi`M07HDyO8*YMK{!c`6 zpNd%8xf0@?SBWH@RAoepJ*7bC11IlvG2vke!u~zz6Jbj?>cv|^DZwQ75NkeJ zPS6_|M~b8?323LPkHF0;!u8rB*|WpdgeP~17bKNy2%er1`=37665ic(-R)DWBYm7@M$^{by@BNBESA!|S(-yY1pc6BMlEiDOADHI|{>P$}{iPQO zCVtMsgaB>o$JoLw8(nR+^gVA^oV=9!=tPDjEJ@5Bl{>p7Uar(y8QRi ztVk%*B02)Kq71lO|7H25X1eAkN5BKAX!fbDcD}~A^K-tf9qR2jQAzvMO5tkx^KvK8rv#(^4>SMeHfLRU+xa6U+_);F}Wo?jO)H{cmc$f)F zieEr@RF7;2Pbnd#r>z#P3=rhLVCcWl0t_L)(OdEMB#xY+D=(xERYn$vw=N8lt0LP{ zDHdt()R1>68yUj4G?02M>AkPHCSvqi=lGb%C1k~XLT0H<2g&_`Tl-31L89!BmIs&g z5Rbf!3??gmWYf!(y>G(+sazS_>3VO3h}PV0nF%pLY@5sPDukOMpAUWn9{YF=DPHM7 z)tPS~$!_A2eOIiI-3~d`mQU6QtUQJGowG$WD85SF{b7f^-5i(ke(8XWFY$9%K6FCj zw6;(Cy>>xlD#aAVCtZ=bfhLY9V>d*~=0vmpkvo#sM7_H1=81T4%BqAM-9eCNM>pe( zyb&J7E}G>CUnKeWU-E=df5c&Z@p3~>0Fr)cXPxFK2r)c4t;MApjQkzpmwEpt1TmtO zIpc963<){D_VrgqI5PIQ!adw461laN@>kI;3bA-lol0r<5HZ(3@kZhKV?>!%Im7Vi z334(2?Ci_7SVZ9IjYWF-c!cEVM}FCg1Z3m1bBf>fXGp-#pi{VLGQzvukSfleiac{` zyzM2ChKL2^kd)m_M64U{W#zpdj1t* zRLqRGh^avs-_p`@%+?~^_k@1Fw5UTELJG&uuGJ$1h|tTtZA6}Z|4}U*(2RTn(%u8U zt%ynR^Flnm9ck~V7ED>`M5fV&6FKEwh?NLwSR!i=a>%Ukc zO~-3P;wBMKk!00O!D&SB;^Zmi?iu83;Jb$FlxPb)yDajHE*hH?EjPyOE+(rz=a$>U!c98exc-9ZUUE~F$ zg;}PU-_oLu8I^E=#cDAJThA?DkeH2|DRUqlFxow~D2F(Ks%X`}ZgO(6=Ro-(D4W(S>7Ow5vsdsJ8XJM&HhRsClDR$W(MN zszAzt&eevXzYS?xhgiZ;n&kBXXTNatqU~Kx=luwDpv$qSE#(0kz!kTeV-$^Q*rqY6 z%RfTv@S0Uo2{?IbtTs7LP_$4L8j@B%rxB$0a)Z64AxX zuC?;(Noe+s_oKPwDd>l4^55Ofsc41wJBrZ4G}MHzX%73Gj@}#hldN2Pg*F*EOWYQI zgTCC0@V9f!K%3}Ax!vkA(NcQFmK#c0Xpznx8S%DvD9ww|q?xBE-6Ke zS>X*u{c<#=FZYBGb0s>zU%+m^@)`X;oHYJ={R?`nEZlMb7!m#R^@syh)SzcsRvum0 zt3@9xMEok$uS01aT4&=L>(NUyL4-BqMl_)>#FB=p8NCxs9jGzTg3b_6qFSwOXrkqB zu9>M0)WeQ^FNvcIy~RAjrF6L)eWdn-Mf7D4I?BTRNQbr$jqKR7ZjJ3nf993R&zv7b zMNu^Z&%Yn&3FC!8#`&M9bW(tI{{AnNV`?{x_xvb&Ws_&-#`iIF5MK@!JwAc{gKEvz zVUwt67PALVJ&ls76eOI;ok5cqY6hcj&!NhpH6)iz7SJKx%TxA_OK2oFO`mYa3OcVo z^esAK4Lv-{`$pTlj{2=j^XDU*XtPOeUuEqU`si)pV1eTfnl49{4Pr(-O%&LM8dc*r zUn)$`MTde-hX&)m%huU*kq*m#l#<|en*rmXwka;HW5UKBGMe>gv0zSjl=y8a*|3UR zpQhJB*|GZU$j8EhoLJm)@1>CmF6q8bH6FuiAo&y)s4F*hTEG|k6Tn1&F6J-b{28@@o* zG`1j#Jr@y>>b)X^P5IdW3HFx7rqZa)Ka8EldMn*!TeQEXqQS9C-wqWXyDMV~v$dtK_EfOBDlRRR zcWPLy@-x|Le+_Is@@jgvuO?@?TufbbibIioazK{Nw1s0OK+p_Ox ziD^H)nEG478WZ+290VtU>aJa^Y!{pSVApX1=nL2j78bP z({|q#+geB#%g%7a0`#N2q}4sJfr$?Bs$owof_0x-HN^{45KJ;PzvqJuPq~{d$N6Ce zMjas+2k&AeLRX`Y8w6o>ortIwWiZBao~kt~Bm{f+j)CN}U>MdGV(T?C7mgu!4q8Wk zMq2Bh|8?$dK1RE|D;{WEhy`qO z8Xt%hV@U2}2ZwJTF?NJz|5s!Q7I{B5m(iyTYhvUKK1!&-NRGNB_@+N&GO4_KGPkR- z>3h*8%6v6g_g`_IrI)ps8DlIj(5%BAt$H10^EP1XTpx1RxSFt+z0=?2)LO8$X)3sy z)rK{OlPzDE70pNICy#3`=v@lc-FYz>Z^VHngTwnE8&5yM*!# z*1?m?y=O9qC9;bD`uKVQGh{E5-r`@zjyt};)cA81Tk?LiZE$lPt5fruXk*yK@Sgm? z%>7$fqczpV;-(#J!Xx>1!rU&#Ax~C^1_6P35*J0ot$HNXXG3Z4(O#w9lQXR z?(wfP{!;>Z^z!I{EUE{UK}ovc#&e6f!Yn<|2lz}GPwIo)uRqlc zl^KA4Z_8I&JdFVBX~&3GLlf}6adL><$_zLjkhuR&z6M?@DmGG++yE8#D?cN;R-op6 zv*G6oYtZp>;-Ev>7O)ypw{I-k0c+nJ@_|YRU`PM^ky(KgP^WzHIl0{hNME%B)mAg)E98Kn`tC{ z@&|QyPF}40831|$&gET`xd+OWV;d$+gMs(lmaBeA2vDiy$aa$q150E_YI-%{;PiZ| z)EVzc(8+H4?zT=8&`F{!f3EouIGv$RyXpEEm{n<7G=F{q0`dJ?yR=wfX1uXEvK|LG z8CzQ7Y!X0pDz$^yTLBU>wqukrKfH^nH(^BajICsFloUr&F9IxZg zFZz-Le$f?278T@yVKE`Nn+)fX7S1AH{(|P_Y-Ci%Sc->Ol-#z&D~P&Wn?JR&2@K8}Mhmm>kTya|A*w!SIIp9FtDH{Nn8 zp8{_-^5$vUrh%iyD!JP14B*V03oWIe1Nfh(Y`-tegJX%~3<5V7z$clsKUHrR{~r_Q z{Z94cK43e_CfR%M9pW4#$|jVoWR}d3LiUW5nVnHovN`89&q-EhW-_yqy%Q1N_fMbe z`!_t-^IZ3R|L|b&;_vEv7uGSw(Q-z5Vw;$kH+njiUN$^8x0zW&l|;!>JFo`!?_qSyg|&EI z>|!?J1DskZcQE$b$3VT+CPsIu;yl~p3P$Pnomkg*i9M{#M6IN} zjz%LrqNzIP{aHHdx6rH zM4u_19mAm;#Lg&EDnhT~V?dQIXR;h1JZ1h!+TJ;a0gvbaxi$718k` zK0kN=S8tFv5nIaieRjx~cy9NHqf)IuvGzqstU297qTivbXncGik)3j(495{nBoPWD ztn@r07F&1GT`zu2ZD$ga3RI=w}-^(Xh zfy)gaYW%Kx0|vAoesd1^0q*CmLTr};0DZ6ePF~?4Ai>Oz@#_0WK#We(xAzfGfaY(t zR0cKS08RC@YNbFFu#~KmDJ>EMm=+!6x8I8c$R10ixa}tZpU;w44+fHf7Qsh}an#R& zicbtY5>_t(J~oArIQtAB@M{(9|0WCIJw@hLRbBzimSeQ*?D@b;ue0Y#w~Byl_e0y{ zh!TL$-*<&Sr3}asP2{Nk{sw>({&1@MRRU!GFTZ}e^#K4Cr@CAVtATVkep>9CI^Z$& zqj&e38UT^2nF`-2n}N~`E_ZWF@IdL7T6f)Z2)O98+I@8l1(q#4h3(ru0WV2)Z9K+4 z1B?$F^tPqi0T1dv2deH);9&zzZ3EN|L@124b5i#M0W%CjgEIp_D4oqL_5KiG8UKiP z&S(^P8g75ib8QTWyl&N3J~IK3%9lFBvNM3kYPnzH-YjroOCwL=`~uMR&rK%5gr><07lBt%HUn-!EH6Q!csaWP(AqfV=7@5kYN*%DhRs>Qq1MG8FRCPPrtwDKceRX z(Y(XOJV#!zBI%Xa2O0qoO!jxU$twg_Q1I&v2a15WlL74$CUH^|MC2HDi6-UvdPR$N?_o5JI1H3Dqx#JWGeaTWsvlX6P?3~2KY|o+`x4?3~1I5 zNIGn2gPd8$Z}*RN!7ICcc7{g!Agyju5cTXeaBsrxS5LbUm}$nGr9x>2mU?>LNgS~N zN9`?3nrCi+m1`FtE{oZK?-|0H>+!e17h0nW&vNWQwz~1G?LJ44Y0a=qyU7_O_kS_{ zpJRbJx6)c@PuxIkzTy76I8TsRaZ0Xk?*(SKrRy>z`+#abz@ka2KiC%`MfUmGL(nmq zo6WZ)2#mY(JG0mz1T;M8_z=Mo3X*E4dMc$%ZXQ0?7(a<=eGaQ=yOoE1w3ICnO;`d2dxT>5Ku zKK@+}2#?cBp^kYVD@!i>f2;(C>WD|NcD)8az4S!?VXQ_tf2ayi(lw;wNZy#lmj z7ryJZ^$yGpi6)`@RRv1UeLl-ls|8nr0|N{~>Om)4%ZJX-8o`{)G3~Nb&ESm_T{YSu z0L1L_&B)rppdjYgy_Q!5a6?k9DUz!Nl%{rb{UFc^T3A2u`{dRR@@=#A%&c{S_b}=q zzX{#o)%4$;KbHEyI)fW{B5nZudP~D(qh<)CH1|h&=SM){z{Kxzx5hvj0m1c-ixc2l z!uIY1(P@z5T$`Mw;Vh_i?ape9;yjqPWXaHMy$Ei%eQ}G#ErCtOxp%JISph>Y`^j?N zS_cgy$8@wRw?NAw{@&$}JD|F5G5sZU4^)5g0}MTX06tRA?BFRp1ZjQVBV$jFL1L!Z zzT3xBkg>2=?GrC46n<0MnuMDiLPArZV=GGN5go%fufB88Us@lFt{3Ma=NGjXyvykz zg1Am+ZxSP<9S&vPc*qQWe!!hhmBk8eFNpJ)?Xy8N!RH=d%jbZ8B}E3zJm-d978>7M z7~_M^(AQqfEdo%8ZQ^Kqj}Wx&AmPKqEDBj`GD(c*i$m_*nOmVgQjjxdPSPPm79tPY z`r$&O0OdUdtm972)VQ(DzB#nX-3d5N1A9u zuWQz7tHg96e#Vu>IJK+LTTzIaA>IJG6~;e$$YKcXT@`JpSvQ8D6?zK)OXiT*i2z%p znI#labzAJtRVygv$=-&`LmSA-d4h}b$1UjQhVzP6x*fC^E}zo<(h*|O7I75_bcVd@ zZGCf1U7*+Xy)a5C7H7Og%IH~A4LpH3B<+|lrT76 z3Jv4p^;QPUAr9rOOUk4bP{%Kx=?0&7ke(@DDlKmnRl-pNEmo=snl#lxkf%G2 z@JgoQE6g+Oy6mofw8h)~1$U(fp{Ezqvb z{AikdD|E)1wr*tD4wA~GiHAu$q#p0 zUSa*BF9?^{G+z9uDGbLQbg*UK7lm0V!n=%z#9^K9>c;v{rQqNg%*&DhS$NlJcd~R+ z9`?R*LJX-=gy$e6S*ltY_RBrK!n33bm)cA2>Nu#wrk3oI%50i&|BTIqJ{K0&{1bS! zTU`gnt^Mj_VAX>?I|%9i0{Ustxu+I4I^H;mK;9%x?>GecAI9nm<<(iu#Ooi$=HeGjy zXDX)d_HS2=&3T|ae&Gswli$wTBNE|Ni*`|Vrf2Z?U`Kb&^;9?`>}$9z z?Mqm1wMfG7YdU;x?btrDEfZEWd7blUCmTL`cR(5HkPGJyPmDdMEr6eML9=l*#ju+9 zyccav3H-xC6GxU<2ES`tcVNkW10U)56qwFbz)2p|n?d*A!wZd(UTm0;Ft}e@UE)>? z=i!cTVgu`8jv9|&oSuy^`FE+l*34#jTv@xP=L`?)OkgMfTRT{BZTz2V6ADZDybC=` z_yi~91qrdbe}+YDH-gzi+u#o8FPWje9dOXkLvkP2E?D4aLB^>;FU+sLEvypt6=v|0 z*v?KJgk3r-67$oB;pq4#-l*PDSk!Gj$;I?L{B}yCMV(_3zUe_n^;>NQzEZmM(PVNK z##_v-*7wiD-;iFB!;?k0aR0lrqR%gQL|#eRQfw8DGjesrC~v^e)ER#MGuwvq#7eI% zT;7F8#U(`f?DyeoN^%`vhzIbu{DXxtpCdTo!HrpM-xGM9ry`cM=M0We-uyY(Ns5?t z%ZOMnk|T#bTqA0_RLF{#^wZQ8YNS4^;)dhG1;p*ahmlEj21M`X=nyuX38^yfOkvMq zK_>PGsgRwEh)L3NfbA=GWbJ_>lkiI}$krQ4jHs?Q=QY*GCp#>X{VO zT}O)lx&_3y86!)_j0%6Rm?O*u6Nh8zmPj-2G%HEF6*3;UG?a$DiD(x_ZN58y8%ck^ z$y+M#fcS3o|e#B#2xF^z8?u}mks75#V-xx)}9sKfUQd5MkOoZwkS{z=8D%~G3Ki%t>v-SjgvLsE3v zC-o#So*cb=z-M^(0u_qQ6C8N4NR7t(guJZbqD9%cv|PIo1~f2D_o3W@SPaN^`aPw?cKo8J6rU-TR#|4b0o=B|F&yDp64f+DWw z6p5jXdY`XFRY;=u&-BMpW?57-?9)u}A9)n(;!#j6tc2cqHf=qYtAaL3F=F@})zD{S z|3Y%YuAqCX(krEB7}Q4Z1f~hoK|8k|zN*6Mp|eNyJ3|@QPJ=52frQADCa3G zmxVKWw|MmAfwK$hOfn#P)Zm69cX+Li%so+y%gPRKw7pOzt$h0%FMQC+c1F>!<^HI& z7`L+l{16Q&70vGsYE6TILeL&ztqRV@P&BwX{#5;Z1bTN)@9gEzD0E{<32-Eh zMGd+<*3moh=)dYw=5FdFG;t=_A%ijnmAyIAc*Qvlt^I|gWMoc9-)?e9AIWB-&$v6y z_nv2?!%0#4TdKJzDPKoqpJD;JEqze>B&8S?`tvoDLbMcJoVWq`YL}z!%Z;Op1#i&{ z<9)YywcnvBu}%wkl`1q}PC&vmp$1K!IO{gztw$FQis*F}8&TOy_G-=X&FBDaH3=g> zfC4(A**P>Y+Ia8Ilc&Z6v@(J159#42R6D8d#dXqFG_eqpTfNqf);(5r+#l*hi}GB# zF(10oz?t#Pr{DTe)P`lRwr&75EM0^KKMbLUWdx7>A0w!uN|Q>Z%^12-c6Z#Abpq8^ z@UN&9nnt7e$!>TW%%VlVd2b9!&!h6YQHI4vizpI|yZGzv68e0iPsmq)1%+#WkyOdA zqYSU8uH;Yap5@^2tGYkWTtB(gdtn^o-;LZLb;}5SJy%IITqK^cl|Qj3a4zH*i*z{>p7iq;{or#Z5M{cJME&j( zc3LVYJ`A}KBKL1lgd4dLJgg1uJifUTOkR!Y{|xpd=u&hgGl}0PRI@1Tq^@}p=JN<` zUIcG~-zAMq)?#16^T*BZ*?RtjcwD?uokjq`T#H=ry~0C+B)|DLNs&Op&4Tl70!%@K zOTn{|h1)@d2>UsycW5vnF#BegSimDf&bQgQw`3uN)jLc5eD^{KTbK0*y0SwEd`$TB zJx@aj-B+0(Y%_!qC?1)Z+NeJw9Fr!!Az2I}U>`|q1|K{mr1&;QDOvdwPGKs+n|1dI zTtX`*%7`=J_|h}uc7i@3texpwpSmTcNvt;i(d>PU-H5$UQa}hs_^tUVJ!1?eg+C|P z=|eIm#Q4=OCGe=p|2*A|RJZh3#K`_#6ewy>VEf|_udn+&bHca%`FMc_E zCq}dKB4+1v59U;`WTR1W0D~(G9n~5i!Y~(eN?)BF#SCj^6yC5N$D~jPk%Z|@W5%rO znObIMF-4+YjnrNXn4a0Bg-51K7)h%TPtL3rOzW%k^A<)Mm_VZb@g89tnp#>Y@W;g-$`hR575cAAa^Dp@w?4P#KlSf=ESTmjblA6yiVh?|m-jFb2 z#}<5|PVTnj!ZP<+bmV^F#ny7&u%j^-z}}>p^Vg8gK6+|4DchMn`2E@e1( z1$*VAF-ot9!7hC+SR|d&##X*OHoQBii#>E2C`moNiq-b2zYuW$8up++`C$sX5jIVS zr+1Ip6#Fyy?PV2t3+(x!iit*w8`$GF=hkyBSYt<~$y_%LZ(@C^N80t;ZLwBY4_~~E zwa3;jRcbb-IAO1ho}O9nI%6{o0y)%sT(B!&cDlcfxMAOJi)$>fcw&VX2vd>I?_+Zw z+mh*B^TyuTN}bcb=7+r;SDbm?C;$)~MBZvBKDu@%}vM$@YIxrL<~>Q=0VQ zhe&m$l^Q~7>GI20FV@e33>>YPF*YEAy_>YNv(}RX(pAl4S$1G0e<;eBq`) zOmE^)bDyhNZ{fG}e1F3d%bD~zeeE(c+54I)e`-8h8#Zmmop!6Hn{n`aFN3esz9!#11D>zv%Xine-pfcIVz^9!zOr0EL@#1^ZZ0Wf>=D z?R3cjtMd$(v#c_sfM}#ff;Fj;U+G2FpY$gb!sx>jVuQ*4F-*?i&-ApIzHU~d9=)t6 z2k)H{)^hFeI1GS`T6P^+A9cHU`?X%E2S^bW;e5}C!5%+G*PmYm!LpfcJOcd?01&Bz zGr!IKC+n*QZR_Rza!?IAEwbEOtDsvEpT4KB$pLg8Bl2KVZd^-_k9_}9n51hacK#wA|I|9I&Rgs`MBkrfJwAB_$|v1dfgiE^&E2kf!frY4EIZ6gW`QG+bA$b3 zl`pqBYQ*|zG{w!Gt)i1MAQ&p=4Ti4*5C)3hFCUqAKF5&Rr`iD&e4+DO^<+7UY!-7&2mhVwO zBj#3G3xNmy--s$bppVJ{`P~gc@_Bej7Q6LgB;`Z)E~Pi%MRa(fw=f_>rWL(^L5xu4$Pv%; zx;|jVh{gc@V>oaMKX&^jA%#G-W$tu8QW&P|$$1RsaPY+lYgC-Bd!ejLx4xGovb~do z3g4+A)jb2J)u}NUM&Q|kN+|C>xPCgUZ(X-+aNS!kavF|b7^WL`Ou|HZpwV1D$Io^( zxOt{69=0h)@OqFm@6y>G;6+KooMv|oAZigWfA*}~z%*D;$t0!a0M>QKjHfi7Jr;>y zklhlAfU+jPU#L|$sD}=fw*Ro{aUw|r)VLs;fMxlNO=6JKlk_1Tasg0^JbQ~jYOTPC z41LIP(3fDcJMPQf)@Kk@!~!9Ko8SFjJSZsK*TMnwRcs=3SP(b%UQHc1{-8TD7}&Jh zaiF_=v&^l443L}S^m>l98Ib4CbezB|FCa%=Kc2B~R70-J_C3!B*gJDZX2-Gr+A^$I z@~1s=LhxfG!qZ#8^52WHLx?XxvooYah-#2R%NI9Gn}10Sb)+(Ko+kzKwc?LS+!&zW zx`7fcKO`VvdK5P++zY_o-|a4A;LC-d9bns@a}9vFx0ro)>sSJ~o075Fz$g^GXh)AU z1JLQvRK^YPOh6}$H70B{xP@xBA`Z+E378^{3Vv@T{T+{ym8sfkp!|7m*v54<_oS0u7EbS!@i!9piEh=)+X7qnWdo z9#_HrMOK@I;6^{I`f2SP&#bnK*7fxqFZzx7S5yhJP1Fu_QMwUW6yrnwY~Td5P3D!Q z#ZC9;*5+fcqscH8epej!N26-~BUQl>4e5_pG8np>8K|fd*NJGZ!n$w<+k78%% zWIFf~?1MA{xL#|_r{MXfpson*@4&=GQGKhzx`hf-FF;v+3>2g8j=Q2|fv7m-sx*%< zp+0=#`>MV$;ZAxwep5Vdn5F7}5(gMi5Hh;~cOl<8WS*f>{BsI8pd9G}&fiB!kbkB% zs%#a>gM2(Z20yHF18Wc$nNfe{glk`Z3dcDah1I7@9qR(&=9zWw&vuq==Lfr2x??CX z;a;?l`Qzz;*6kO={q5)=vf6$i;5Sl(m_uZLr1ZJRA7^c~go;6~;@=0K6A$>uxpqod z%RZ5xo_0|MbgICwpLd_@l-L8Hedt~>uCjzMtFIMz1o6(?r3?K(IH53pqAJ9ul@#~H zoJ`q{Q8h>S>G+ktv8!q9I=P%1@cEj@H{aGap-1VR8b0ORfX83fGj>G-5}hV;sLJ&ku;E++ zyJI0fo_x0RO(LUX%@7r&BA(iA6>J!(UVHxIEJq;zs{~WH@w*E%ebF3W73BYGF9^5b zGlmR@-wq0bF>L17yN?j1iVagJ|B1B^9DPbq1YVhiPFlAOM?ZNX*|w> z<(FPE{vuEZ(xcH`S=V6<9qj0-%kWAT0lqxkn$rQtC1>41%c)~ z(qXelE|_ILh8gIipPvI%gzaeC#Vg~d4O(eHvb~8wQ$F{?;-q~E?* z!-y!i?ylU7d`J-SRFy8!G^Y^Tz|91RWMKwp+baTmQb`=S!*I$T+-WgFS+?J)9rGt|fZF57_D7=E=lG;8Ge?WbqmvT3-27(zm*)ou?8t?k>t*+$=!wwK;XU({?W z>?vTx!Hk9u8I9C1;-lOsIm)@()kvAaT>-Y6p2EEObcgU2Hi>h=80oP%Y9&hg(V1dU zS02EtO=1S#qf3V%RNfdJ=o<|;&SHZkN$L!vAxgU^xd~H=S@#T#=t4tGQb`D4QwFn{ zZ(zgbCXIkDrZzdM1DuwwOuKe&jl{$5*)SWe*XUwdk7plw)&|uA_&pndXQloa&&xQ3 z!V~hn)2e{Kw2R8He`yl@tNrAMxE8MF3v$>m7A3yOY zIzovndF+DIb$%UJ4kNi^B;o9Kk08QbYB#wAdH)xFJ@XevgvEQY~?AvY4>&Pw{Gz{dA z7+a6jE}r8Dq%zm=N+wG<&`Ji2mAVLUJb?b4Js^fA$$F?Z_$KB~%->Tb^v!`$;xk%i zl`R#<>G}Fgpj#s9ZUVjPv5Acg|KXk82m$N-8ml-l?Zq)%@uVhPDgfy`KdkBRJNzF| ziwSiO{hpLWeI?V{RV3VUeTXd13E9-;s3Dgu7>wm*k2)cYD2C44Kfe!)A1^q}WL4M{vx+3WHqVh&IOx+&;~|DFenq}BiVU7JK4*)XbczUEgR{LR>5; z3f{BOYfmMs@eAd#on|1cadj~gr|D_zo1(1P((sn3e4Dl;$a_yhMe{$IA(5YPnmeEm z>|Z3WqM2sdQru`wM)==*H$XI&e@y=tKdfl@kMRV0w35_Q3%YNw0oCj!w>hm;}TS4$E2?zF35Vn953mk zAjN%Eo8OYBHh-y4*l*({un%rqRCPKZAh4wq(iYz|BC4~$+Pzt8VUV3TznC8iF^m#- zk0K8(g+SOu)G7oyi5)x~LSUZ`6Hq^fnP5?|0s|C`olC|3_RYqhjLE|)v)*kvQxw4~ zrS5IyPo9fO*iPa$S$W5oMy(Ym0KUVA!CFiAABk98)BHNd?7^HK3E-XEa73H+a&bOI zP^EyURs#}NS;JZVls6ZfxJ@P3OZcPwkiAH69}aj&7>^dr*bX}nX%>%A-$!MPP_^(b zw4gb+gr6XPTt0I7iasUq|4`;pW@AHqSQdz zrjpNSgtQ6ZE=3?H3WZlsZwtZdCYwvP!e~$lklO6x^M8xJqy_n%lB3g-jtKN&)ILrc z8kDKS4wCsN#I?|rX5zo>2As{3Q94Vg?+#cdJ+&Hb&4odiI}g^mwWEY3`-*6#nh`2F zaGFE$E(%P4f)k)~AS_Rci-PYAMF0JhJ=W}6L+hE&UDju{2<&`iz$3TTD&UA^&vjU_ zn{SRrr=IF-wPe|p$`Q>6?L;7+{QRc;-R(P*mkx6+g)-3Kit-vbwyGwEY$p5Cez zQD$|ga7-|&!z5$3pw;7To3KKyVCM-CY_(}Kul0CbAe82v{PtSPE8|5PnVU7;K@lSg zt44fjMT68cQbzryjw(z@-8EC}M_K5VHDhY;Lw_dNqXjUC;c&IM8FqY>f#ebJrz+N4 z4;huzjsc{fi_36>w;0U;KgAzAs7KEtSyL`S;t$8>TeQ!F!(xukOB!g*^jM58Ol`A; zR;VsnY6>m6_ylV`$(T=UyViL9>n*xbNWLZUZmvZ$|1yE@5iyBZ%WVqMjsod60lz%k zNl^HF4W@7Y;As$f`$XVLMj?H%@M%FEmQcu| zP5Vz@;J`Q%d<|#9BlakrmZf7;Lmb5fjuLyv{)9*Pi}#7UN7Q>hf5ctW;(=|~<>33& zbwFicQs8kMc|z}^Z(E8J+@ofX^x1S(!ys8-|LX0*p^%Kvy>{$0E>n_voNNM|{!AP9R{}AZ8IS`)k!UW$+$$&!>Q+<4 zL@NJzK=)U3nr`T>c;!?~wr)(_Wim-MdYwIjQ!~+ZnmqxgWrkJ@i=AtyB$*DA^rMu= z@X??6L{4Ka*~kaF)YPVj`b>q}{0kFYyG%RcJg<~ldbxbiHoO?ddS=-eOp|?AESbX) zXRO_lxEUK&c>#aI9Mm{eMys!$|J zz}4hwW?jXk@7aW*Ntc%EjVIg+(bwuUk0Z#aw~4lBhby!qWYV7^L)Lg6N|kBr5^{7p zQtgO;h>ya;Owd2OJ>1AdUMU>wE2N`-L1x*aBXpuR=FEm|cW^$j+#+vtYk&+Z-RXAh z+Jwx^|C#IW*MJm%FUlU<8)j2YHix3JbL0Xc@^>-jpUCIbSa`)9lCeqdnFV@a)ip>n zBcrwxtNPpQL|=8$v-(`j=U>-W7Gsky+m<|*Mni0G?~WA#-C^syRzYG$=SMtM~y(?EfYn!8te5;^@hCbt`R$f!S4cl;yoZMQV^{>yL?6$Mu#<)>zYM)_12}F%))X)WYt*M__AsqwYn2jLj1B`-SKLktq0b#>|9QldzpJII z#)hyFy>2NDY$J(B*bR&rEiG1RdhhnH_I?(M zJ(>g=_vTU3&k?&aZ5Gbel}i$Dz0D9D%P9|}uY*zuYCOHqs{6+FYEj2+#&DQ#YDMIJ zqf!Xqs^Fef+&7LeRr(fcHMo9cl{8H5U(zvJA}RX=W=Z+@{{^gpEUcNrvM-7lhIF7; z(Nr(DrSrO^s4IFgS19^w3JO2mH>NYL7tGX{T{brq1plcS=yl1h;}lj{b942y3@jT*cH{bG6-V^KVyG8rYA7XE9RH~fB$|W|HRx04U5hi@ItC0UdIKcnQs+xS&|1RQjsMRi=w@*Da ztknisjmzGpRaE*d<2?Gon=B`%mZ{b$z^i7NWpbkRT2{RPUE;*5{CI$tG55-KDRUMw!GhurH8t4MX|*S3L~wdge3XcHAz~Y9U$X8rP@M)d95}5QT26GMP?U?k0?PVhj%0^JF~v!2-D^C@^(VD!-G9wBsSncJ$85>{}IeO5#?9 zmfKL~ATTaWkoN*IcVnOb_tjNv(#=6}q)(%j%zw~o@Agx-HsW#M008I_&P1+^YTG=G zoB@Bj2Vo=Ig*|}3m+oB{e@dX`56MuyWBFDJ$o7Cy<_jH<$B-WHh|eQ@bCquA%6oiaFyI59sj1**Q(hWmSMmlbW`YA%cDoQ&1L4ZekDWRwJ0S_InD{{ z8`~0SmHt7Qt6Q_!UrrCy;F-0%A($AYRQ{5M6n(s_g8`rR7Z&#9Wzq_VOY*&;QPnce@XNx51`@6r}Zq8cS}c(hnBx`h&2Y*pJ?1% zKW+kc@QeJ5g~k;)ln{VTsiVIq@K&}=sRA@k`@3yo$_O`4*}8&Y77lF1QJ`i&svH1m zSR<212}yQR78!;^12QeDhM7vH8X!HO=2B8-9Wd{b2IGV)XPDg5tv#-Wd-v3ubGWq% z4w6?g<5+hNx|s~g`q%~s9J}@(nma2UgzJ{xY>^&zPXOL7LNye<9`*o(Hqss>W~ssF z2gDud@Spa6U^c4ai;jiV%q_#-qWt??a6Yag1KH=e+$L&!vae^)Sr=x#y7T_(x}ZTm zYhPrXGcl0VHl2a+4jAw;3wo!$C~?5EgO76A9h1=eUaP;a3z?#huh^~;7HGJ*Hw=ef zBqUDhkaVg88SpDZ;dT_JGO&_M=aSJxTEBtbG2rMJTd1KX0^UTEBi?*Q#HWrNGyR&v zHJKa@BxV;OSlw3!=p%VHCSZmo|M+oRrs3NmFgHH#ah>lLm~m51|L{4kUjy-0gB9Ja ze`NjOU9LjHkbf4gZRRhikddCCqykbsk-`$rf>>b7{K(=-fkOXE4($o_OHF{bsBdDQ zKV*LH0o`*`J8U?BPYd^<9Z^wdV-?WC3zY2IUH;<314l(*Yn8S23*n3HO>)<5ho{-K zyRQA}jYEa%6a-mu67U@@!QUATzE@hacfC|5Beef?dHj4!8!$Pupz=HlzxOg*`VVUe z;ZE5|)1Tb|aV@h`g&xxb=!*JS6jVjB-)-8FLvBe>NcouS8-|~nE7~q^)FLTZ8Z)Ik zYp_BH)bCb5MHuJqj}~W@6*oRrnC~JKjUeL#C;3ISRHnCil!Ob~4+4{EHhPBtvbg|ZUcS|OXKSKx5MH-ZaJu-_C!iRNQmm?pqGPQAN6&HkWQ_La3s6>Y#x*RmrnWwrzE zF3wIfP>}IG0Q!@f6jsk1zz^-eD-f27$sWMrEy7N<{5+dnALPf#LX~8scd!cT5bFBZ zkKxFknbFvMSiY=ulVq?eljriE)p?Oq<}-}Xk-9QkI5s3kPXP`b=o3#hJOkYJ~lXsRM?k z%{cWx#4e{z<=#vsEG_3KdZ_6sf^xjh;h-rn1hRnUzx^k$Y6sxPV0VZdWj77NuvCGo zYE73`;3Y596Gb*W{oiyik;Gcep4swCayt zLSak<-egHnz9okObP1iBMeW195I{4|YBzdb!~`=-FRz$IWGUyp!6l}p)rXSviOtNifhmEo5x;EP>WdO8XAi4)ix4GHcBw`{-#8^8aq#vA z8$m`CQpcN6dGWY_7n%1n&nhMVP7}jA(G1;kdDp>JR2YAd-M|HocjYgL>m^x-fVC*u z3G}qPv-iBhtxw*87Ch0(&fX*sqv+W=+HGKOF16T-`D_KBJutcW?cWt|ZDC_rz=X<` zy@;>@_Mokfz37YA*R(Es>^$Zw=%vzZ7ZSF?EMRpL{bLaKpO6q=K0k2}zOozRxQP|; z#K4D0=!f9w8VG1U0*?BF-vCi~)8ZFa#c0O!sX6?yqP-Zfi7W-;Pb-&Pn)*HbSc~ zN7`_zomj7sz!DB$mT6DCooG;g?F*3DMqmDz06SO)>N24eL##+DMhp&${vne2jCD7~ ze|Lj-Uo9_fM+D^-P-z)|dZDg;o0loyJ|b9|M%otEs(Ed*VDzF<477?5xL+Al%+B0$ zANS!O+`*D#O%znb0__0uO0In{7C#TAB8D(922^RaTy~1EYYm6TT*Ts_xi-vqrN^8w zhcob(=#HhKqa=mxrHk~vGPE1I**Z<5pVrLqf+3l^iMDO~X&B6It_N2Y{VYO^w=QL; zdIqSaBzZYmr2C>11amL!J(kodC{$iSESiNN`GvJBQQv(BEZ*CN=|=)+FTCnvhP2)?fgc4Jxdz48xN*w%>f{Aj2hZ%n&ziwCrk+O!tJ4M~0Fg zR>jYx&zXxKQIgC~rk6q|sA|XdcwV)xe@tJ`xz-2Q5xAr4v@9O~t?q{$&f9{pG&TP} z&gM7hD`w=m*kSCx+d2xhYBpGh>*Vr-OEjGH)3Gd%c!CY4c&{5= zB+;|UHA?Fa3Y0o63Iju`@ZZrE0X7?2qD2RL&gXhs(lqfK+4ZfiXs)U0XZSOHVSy@U z(>dh&aDa^0YcP|Zm{2cG9h*GidwI3DS@;Ulf@zDxI3}mKEc#C!?B`Ee=kFmGL!+d- zlbJ;t^7`p_gERt*yo7s5ba*ubiI{0NiCKzTFP)pWsyc2;wrLZpDr#x zK)ZkEELtFi*Rc= z{i9c*u5|{E9CLvfW4o0}?1G)ME4$_=`FO%i?W|O#1hnuM@=h2RYp^29=7Kh2Vl?(G&g2ES6c~lG2qQUA8a7e_#VKDkHQy2`@@A5a@v-CIcnwuw{8gXjsIxRW?Lk9?z8i_fc^xqOv{bs2d47?7{a|g zGrxYi`WUTsT_5Jh=?{=yOHllExaf>>0aNtnHi5_lQn0&nA8geyqR>qd=;GGaq9ZYU zRQD|{qz8R?DZ(C_VMH^~u9l0L=l43Lv1J$xoCo0r#nFGkm&W4JY5}=$Zz0wRCOp$x zThk>#0WA7~t$$7$a{%>Gpz4S}X?UIN%^w_&klT??1`V9_A!hK}Tc>RGd5M$E8Z={g zHK;F9j5xc3O=iLAg9B(O9lAa1_(Q1Ixz1mV^;?jpgW`{!#s3@)kk%x5?cDhu`qxFx z8LM#|PW~prHC*Gb0X}W$-aDTPLu4yvpi<5qK;;=8*!jO&@{_+1qLPHKX(&0EhDm^t z2#zkt@f50hOEM?qMadlp6xEnemv^(?jB92`!vE}y;OZ&G`yI@(>Z*H3+TRY?+A|oq zfsuPZJh`A9u@cZcHkJG{@`A=y_KLg8xw-Qy66|=69SVy)LgW+ocu+Ci+969o$P6Ja zXMXs^YM-oG(;oS{F@Mm7990wye}l7EUS+QJY6YGe(cnJq<jbZx+{%UEh8=7AK7gc9w zi8rw?v}n2!TeZY?@9b=JvCP#{s6*@eI*s2oU!%pqGAS6^*L9xeEWsSIO58Iu7KA|v zlYgKj_zqa5Fr>pH6i5n9bvB_I9fDiolHOb)A{Y(lyLDPmIbb3L?{DTLaLOd`Ptr>y zk?P8w*HrLOz-PmIEuI;By*wK-z3UwTkZfNwkoc#%=vm zSyBFihcfwFD|r0|W5QR)435}8H%H^v=>+ZeC`Wfw3&4wBzAZD`ah&tSOlr(iZKY_o*$W{9`^}C z=Dra=pfM1qjoP*nsU`!iPgo29ZUV98FJo_%c(B$K0i4A6lNSC@;8q#zgw1nNiqufSEnF+dto%LdflU z1xYiuqZW{QPrp{5;K};yhb9)AOXCSeA+!?|59(xzGF|a@NB2A%kf`7?gO8I39Ol>0 z&eH&n)HP~&5NZH|$G*E5EvX^2xyW`YHOD#cW6U%rVh!4ky}@4k^9BBASzhOY>PkdH zPHWahZIRgjoaFdQ8g4vHGdo>|k`)Er(!2NA!F|{eHpR(rD&k*^=B59?V_~sWK)2_2 z5@N)|ekJn>rVz^)259Bu3u!ksg0B@uVR-19U@$r7Ffs$SXdV%@G+ENNnRB`dL<6+o zaN{0b9Gne)^gfEJnDgIhEo-u!O5Lb_$G80HsvVejnb(QAfd@kAfqQ>RfckKXQ##!_1Q zQmnLlb^0hTf?2G4d{QabKA)>jMXr(fWipCkpu8wGz6iu`WC0^34y9`anCk|+&mDB= z>Vp@rp`@7iQ2w2s^K2SpXABsQnU;Pr!#;9)%oxph^+~AnWiGUR^<$gf*Aot;j7}zg zq%Y7s|CaJmcu^W;BP4f$!|p9xiDPeB{nI;sSfEK2vJ(BoM3nB!1W0%I<;k27i8>NzNQYT5H?~4)df4Vq{jUHI^6oRjxv!p4> zY%_Xv%!#$`@t&)9szXHb*Qtk54yf-6i&blK2SoYJR-1hWRyKAuCs3yVlk5c>O~a)P zXrtjvhjTS6upI=x=0Jk=Zt3W(qxM+?^wv^EAtu~TG_l)MYWmikPNICMUAr6{X@`>L z`bQJ8`9yz&W%^&BNbdf|3Kl!j%}8N!Lm2uvI_@uOi82HY##~%Y_Aq;MVxq?TmAa!_ zlKQO<{A575TeIG(CIiM@hhLNt52i^4*;h$ND(rX}d(G(^OTa^Kn7;ED_TJ2~#|q&a z3ln40I5IUP{2JDpyGJrPg2ras$BH&NDBlJ$uf;?8lfFH$u|47PQB%(*CCN|1ict-gdHd# zx|*AsZ9#sfZAyYHRPTlhwl=O&dIqja!LwtUZH+?dpY!JUL)UXQjMK+ehNlkF@NHyX ze|JefwIw$fLGqHJBgIc%6PG52^i_+^Cm~#1!#+}T`D<;X-f*^C=;z=fipJA_$Kzj* z)Z36RNK9pn<7bV-%(9ODI`eDTXBxTTkP>dRVNKyMA24h-olB@3% z&<6Ooex5b3<;H-GjO6F4*9aC21+}6&_rm#O_3u+jXV=tJIMavlxQ`qTE8EBTx!@~c zjQp{t!|*k5LWn6%w*~zww3eEm(J4J!GXKt=0Gb!csXsd^A=D&9mDW1bc4ZCPJ3PIO z(?go$h(@o(ZsK%F)-21B#uRGa`Vw}U%_dOaEgmXk%>mHo5mT=a=>2=OXFEp2rQc&V z$l%OUe`(g>HZf#2t>`kn1?y{@WTB~*HQ;BsFPBD2>Gjy()X0@^O~|jqb zufwGkw`F=h52oqgVgA)O{p5O&BtSCTRZe;2NuX#aURp>Y)MYXzuVC^u`(#welwJF6 zUddcnf+*IkVSFzAS)jW$h8gLf==0G1k0Di&`BwV8K?7(t5QF-rZ6-6QC`0vl# z-?~@ud2oI??a(~P=g#JThlthp2%adL>$tdoK7ih2*D#`3RJEgCxEIIHjW!xFCW7U6 z(;DC|X?u6WXoQ@}=f(&f<6go9@sLrw8MY$*uqSBzmZc0Y{FsTY%z8=G_dLgc zbY|Cv)At8kbN-mx1UYjli*ooFD8YK_h}CI|N1G+0GQ{qBuQ&om!WtItcTBBm*9i2tT^;6eus$Ti~6Z;FS zC<6+H1_T5I1+*dOqPP^NLNy5m1jH@~1jPN{S9=FjGZ#j7c19L%Mov?9Q+hTtQ)YTL zc2;A0ZX**WdUGZtb2d{Z6BAY=Q$|N4S4&1Kdq)OOJKGr@9fwUW9KYMTy$dVMa>hm% z8zacI;w6%FSC#&p?b&XyX0gp|#p2XrV+;T92V*@f8D3}cjv|aese~}XIS)_d|7s2- zREHjHGh|c_zcKCo(X+w#jyMpj4nXPRbkayEANmHcUl>A(_C!eOd*KUhh8YQEZA21c zBJs{Iypd#z^NYddGd~Y}b=o$(q17kpZ;&{m0p!ri5AaTR#`axulatXsEzM&c*#i; zbE0AO;*-#7C=L&BTsjcvB1Jbun4Lv<_r)WnlqM-^-`W+8**J&%evs;vHk9j zgH$zmj1Ar95HboTtOMY( zD)ZBioNtZrOA;SG2>92g1O`hTf)u%1m*kZ2sX01{$~v7x#K1y}@haD9WSRIQM#KtL zp7$XtZLJO#h+3?;d3^@8<4oV8%B;9NjL3Y|VNKrYWN3P;3Z{|D46h=x(7tjBsM0+4 z@Dk&?-X1zw@RMtdlr+GWQpCzu<_8?K@(&FdH)1VKC`IMtqhV2KBn!?x7$XW8cj;vw z)xjmEiOyT(XBi`$idWr5KypL~&iVNDH^%<6#0p=yro=H{JJkS8yI+Y{{^L_XPA{$j z*eZt`M~|u>L`=l`c+eNMHI{zpt@A%Cu5)1a?u((7dvU^6o^tm_7Zd_4E{{wmXvCd| z(2Z(3ht`$xYW53LZ;qbI<7L9iQEEj-T4;xIhPpB8stjPsx>2p&hz&kOU0pvx(3x_* zWV6l;Q0@qZ9p`{xq212Wi@q3c(yw59P_lw;BK!fi1FaIOr>wHs!j}7 zVYd@H5B8^)L02u4EU#;0n<=IFIQ^dW4e*xZ$^TBtO#nNI7HGszI*0Pv+_P;db`sB{ zRlG1fcN@_gA%W)qyw5sU%e}Pocrd);$MfWFK&0c%`OH9OKwXY}J^MYY z<_I29rBe90aUl)ilfcyFecVLV{(YUTaVyU##LYlga3_nS-1}r3QUj6vwL2X!^ki?< z$Zu@?#|_CIGb7=aCuN=hGLHCQkoY~p+6W&tDD`|sT;rP1bWPNaFo4y~^V5pB6_j+z z40Q=3uaBOsZ_|BOzt?O!)lfW88+w9pZG9$W;0_;4WILbOfOG~J@u;~qukP&;oyz=} z&ok@wtME4b^mS>DVq|wHU5iShQ7jNUL4B>Np zDa-w@qympec6y_}dz*f!5TXNES?;2L!03h?PR(l^0rsP924xC9XI6_Z=^HYtoy^h~ zRZSY_+)^(>_F4vi_h!zA+TJkE2xz~kp z*fDH}@j&9m2b_9^K#s+IT7o787h5c|3Dl&Kb!V(pT8&alf0b@%j8uG*!3q9;RB48iBP9~BcOvYGnFxe9qh{3$cLSEMhHVsD@E;%_wp{d~OXt=T@M7aiYf#8jD zq4EeYc2C+=bH~o4`K1837w%nc{Uk|yIZe3`7;PsWR)JRPH~sz?vwW`+QXjvF4i+RR~N#EK& zMLL)~QqDHAmGzASeR}&4qM1{r`5R3_wEr`T*t=6Un_0ujSYw`dtV6R1*f^92VRusP z$3cahr*K6$5-bp+=vSt7dA(lURXw;|g_)9{E}%9Qxrk(%6H%U8RZ5aUjd^RYn)0nM z$H=8_XYf<`Fdk*85OpPJ$~P@A4%jeg)0u8)QOM+`{tYL|@Vpz=o9VH>>Tx9Ck#??c!IVFxPt6H!9eA zFC4bZZ}?G*X4l!Mmlk@?@%y9M4C|6A`!FJRLvge8M|pq@Ld+f;ppA~`?84G%ET&Z7 ztAq!V)j4sFmZ44eB=$1%cLrm^VEf2{N9%77N~yVUCxqtix}hRJG$$Cb>cdoz$3(SRS#{aFJHsR9P~K3UkO`s4{9+OJcA34dLiS%&OdB>ee`{ zSbOGGlORC>1}VNaoO(JD z??z~PX}@fHA~j$8G~rqa&X*oh@ezTUcO<*lFHmrxp26KGo=X}M&Ccx|-N>jkK&*~Uq;O2-rFVTFX;ai zNb5;-We|*Ob_x#DRX)zZxvCY zq7vPij5Py_HFSD`~1)1Ym4+l}Jvzwv@<#q-qi{ zLVs9@Qq2jy%4tVkJew}(0h%+60gn5-Zv-#25(@S7c!H2M(bU9lhQDBS*jhJT{PD-+ z_0$zZwrcWR6?q4>t*L@ydu8pn0?3SJ24VZtoVDzHDkt-CsH8icmk*(?6bh5BIBut~ z3)m8{ber%yqHG}v30_+lfx?U*~Pm~NuX(IzJEZrty${h1gX zFRK$tRsgc(z&2H+Uj8HZ70e{gQ^l(5L_YUT^BRtia&GknMJ132my>pgZcp-n#c$o?sQcz{ky zVn-$0+@r`*U<~XWIe9#6ahFbzo^S4F=J$(IAAG_2c>B$&Qy*ey$OVIJz?Jy{!{`ZG zUNeAY+)^3sjCtBtIFhCB5zzBa*){NSg3Q2onjGC7HXK)~;88@T3FeOzz$7@MKRRWW z{yn3xA2yTF`x_99eNX6#gtRXh-XVwyr()0$KpQJ|KA4fm2O$A<#wPWXDVsM)mu`MZ zCS~w-_d(c}`9{BwG{+iS0TEFiP;GM%P)_Y~5G{vYY((pkA? zS`Pd(qE8bn>XS&P4rX6s_16C(|1V)~Vt-zVN6|HYQ2Ho!o1v-n3yk$w-NKb9R7+FU zH*_H?+;ZlCJrn&mJ}7TI>8*!N0zd+*pyr4PBqRV$a2G5}8$zj-{-56)=A1!wt}i1z z=002O&jEimX5P=iOpy4AkE2UpAWBcL;QLH6qciU_=8~KJkeZ+Dsuo{f+0Jg-&uSMk zzDqJdVh7P3F|<`<{rPgLD}f-%*q~WoK!_KNzfc*!bm@JQOW(^Vbt(ij&FG1S&CVTW zqd_DB9SHIZYV^SmMQ~1tjfzb-)DeLSuaY%ZqQicg+t)|QMAV7x6Z*o&N}K)akA@Lj zSmZjohqCp6^qn^_kL&@}b7=-4!;J)CzA(kz)b`oT_$OP4yRrQM>sThJ0E6wslyT~h z37V-<=aWCHMzGH4e6@hYJzJ~DBIEg_zt%Kn!MD1y6yI|rWqfzX>x>^?NXqqyBo{+k z%Y&c>c>wlw@9PrNGeN;(YH6c;OZ-R_(Ru~OsZTKDk8$f=8X{{uU3;1M9eQL4xM!Go zO2%eAjGP?v;+KQ~gNATpM@3xz1KZRyIQ!Fmf$mcc7U{X*JVTTL(t~#IuKZ8Z&DNl) z$G0&d8mCmF$30y@){k)&C46b#cX4IL7?D1N*29-SxE%A>wl+7KhrJ9>T#{9NLw+Ou z=J(2cFW_;A|I9jNRzwBxf|dPWx^rV_lGh_mZ~7~5pJlIj7n`|5or9vIVR?|QO@krl z=k7C32_s;$ape1oF+X@y>wH@(1*QuOeJdt6|6OPx*c5w*@&B>+mO*j0%epWgAn2gM zWpH6Q7*$pIvMS|71bjHC0tsV^JO-?;Y)Eg_42}V-1LyLPp-K(^?&GS zJo6n2Zgc|Bz(RAMTX%e{s#lBB>07pcmkL>Dwa=A!yS8R3GhO*Hneuv`HjEX?3EO?N z3)~sGYteV;n4WE})ErKzIq-#~17u6>%sb&b>|M*gOsO~Vw{bOM> z^TKiV;)b(Qz?sowv-$L+kU-!~{=Um_`%%lXn$l0l)*bbRY36&A&gn1CGP0G|bSFNr zSqHK7FYdg<(Pp!nB8yg8YPZgFQR5>brNedgj}ZE-n}KiQxjj_L?Ra7*uQZ8J7zbTWBDz#)CYaZ*fav!> zPMly%9#a!$CZ)aG7W28U*VDPXc`hwX2EI517=A`~Z;-jZM;{Lue!l}==3 z?{(Mb_Iq^S#@Fv|pF$c}=iTKzW>rggi0i|JuBwvjev6fzmbH#9yO{6O|5m|;`jhZ_ zR#n~0RSu0Tkl#f+r#fY>bz`S)MK!0rrS2Ek!oF3Yp`h4dheeoAdqGt z(85&YoK@rOY^R&VVvR@Rsm*Y(?AgMy@ky(RWWpH!1ZRbN+kt*t;nS32olS!aiTRX9 z9n+jk<@)a)>J_k-on;)cN=>`&ad!C0iqVSG+$5P9RGd&-^_p}@dzzT%_$}*YhZ9&} zxf@<+L28blmi&6P839-|`eQL-)c;mKZ)hoZW8e!Q;gseG!11+?2x_o7{70aINRBe_Acak z(j84^ZEf@mkM1^#ts=~CU6Ii@erdtY6MNdxfqCq3{8iq*+{CiP_P5*K>x?X_EzKfa zIiusjU&f5U-E>(co!jGC^{Z=$DBB8fYlq5b;IXs}-8Z>cr%_SSk5mHl{*kX9Yv zWrCA79&4_IT=c5NLaMMVVCH^%?7e3WJ!D4R^yfmqtvtUu_2=TF27U_M>g5S@-Q%2k z44lFCWRLR~cK4d)%rfPtV9&}4Ic!yPXSUi zC$_Wl-+Bov9pQYIf0Y1KX|^KMxwxk}@-(P1qkp_nX2Tx%#)>p+`^;Uk$=lRC)H8={LwA7<2pJC;fHL`6ig8}}k zgwWwbH#PgWr=#V2y>;!q2n0(8cu%58v>vzYMv`iN2%-W9{jW7cnUF9T!>&Kxx=%~&DD)36W zTiY$~n(KdbwfTgoOx<|gA3Y0wJmXmG7`H)Jgwi#*V^sceOK~-B7A--T?$fX9y)?dm zw5O@|+)$5Xl=w;ZmP$!@Jc85(UPtE24LtvNN8)t2tGn@aF10dekj?f?Q+LAX%5ZM{ zTiUo9!ZQUz(+44socqM0IKmK9sY=}YiQ4%hWwYTh&6d-%oR_iTX^(r$1e_6c(tv}y z&SLzy=?<%J*eMC*3hSMnTKNqTOVlSU++|5{_VsQPHkR4~c6BJ-*QUwIg^)JnB4vVUVvFq3PYs%{aCEtjmg1wq6@A`?S0YhZBa1B+Q?r zssm_^^A+|q8$IW1dwqS2&F;4(iK{WjJaXxIw#o9k$ZUkma7(QYJR!9h>%@>ylCTyX z?a!R@^6yoLYV`--H-}-P(2gl+Zz&89rec|DriP9QnSyasQ+wpTvn>S1=uBQm;25>G zqOS<(nY}tWeOxi_-kN2b6IYdDQ2M5xX*4)Y_dq7uT>BD1wxEAtO?6^@wbV3;bF_@z z5tdmYS@nmd4O?!t^IUE0%snv4=+WxYL*>1zel-*Q538VC>L(TTUx9D=$Q5(`LjQ9biDE~{(zzlKZ}&UjZ35Xw^j|$R;B7`b&oDn@pp13 z!(UetCvEb|^-Vh`39A8`PTb`kfdUn8jbBblvzk0cNCv`-q8$Xj<9wfSZ_Org@+m%F zcRIC}96WeZ*^(*QRoNQ6PlQ#hi8#-t;s4oGU)bP!vmb!`^{bZmbx!+@BwE$Msw;M5 zJu<%mlc&DzBW?M+x9Q~sWwe$TcpH+stm_OrZ$+8H+tS#e?|dC+udDLcz!>+Z^|UNv zI-aS!RdvQS@9;hsn!ly|joxvnr9xkVQJ2JJdW`VsoJ)BVmd#iZ+tJ==9X7}M6$(VlcP1nFMkq^t$`)P^6REX#Kc}% zL~K@!iUp1h$l{~FpU>6q)uNV4`65EJ-5pL{e)-%#Vp0OYniTdhP(OP{wUn;F|VI*sR&|bDdwK{9%}+|xTC+a!pu*uo`{ys=d%K!rnoY0cKT$U(5GAm%y%Z2X$$dr=E;#6y zP2;9F0+nf<=fbt+i7FiY$~Ox6_=}thk4$}5J%9VhM@bw%?(hpGdaI@o{R++8DBK8} z45F`FCRV|VrrT3El%LM>`0_q)Z(|U$FK_fH1dJk3I9jo%#qA*}2YE(j!t ziSz${d%e7=zA{)=CSy>1Ig0uLlxe|@ z_gnemk^rSO{#E_B@H|E&(s}Osnwbo)-&~6y4@BmtOhfh(u{H)IJCMaP%uF6mB~+JS z+Bg8LtLj8_$Lw*gFN|8{d4uRvQFtD`aKUrP@?y!>M>e&cPd5Wkhuv-7)ZVBeeP#Or z*qqA#+5Gv%JpB(Jx)&HPH0?W@7enx$pJTAT(opTItQ#h~!@)kvSOL-sJ5i|7FVrtP z>rbKRp*02ec^TFT^FALavepaa%T{2IkDiE@Ld7u4il@tpSK@+-=TQ&@_hkUmyeLm5 zj>%lS5Q~X|(U=fv04fs!oAW1Nj!LJT)zxk0Y;2&Ts3-nP-GppUvOjL<9aU@}M%W?+ z()7o)Kk_y)a7ZSZ#?~=#bkdecpdsy8xX}Dtg?^L;lvqxWSSu+296gr*k61YASh=3X zum%cHy9c^7pjfgS6YrSy5YE1b*8B+~D?Y{; zwoHM)*ZgrB(`XP|vzP4nc%kq~=8r0-?lHOzRwv5j)YMo=ETwpS%C-f zMv`Gu5^yE>hym$RjWmJwnQoteemNiTQfm}01NFs_wh|XT~r{sf?TCjJ328oh*llDS@KXgMpDiX$%#oHwh1v8%1^u| zf+FlEcce)GP>E4cf3(_h~#!QDfNc1cAawAgIF%Bs31KiSwM&Vevfp>E6=dCtz)v${d$X({@fa#!o z5M^_!J^tlTfOT6SFe)T6fHI7oZ8H=t7}!ODA{#+FjRf*dVJlAHmdI&;maV-y3ZOuW zgu5I@;%tIuvK)ri0n*uw)9YSc z&fmwtSq2i;0rhm-Up2|Xav>^V3NMiNpnC4vJ(o`-VL*k2fCF^YRdJLteaB@MNV55m zFV&BYE~ZG}c^rTSWJ7_39Vc(go>Tb&RQ!?Js3ICj4az=D{6mRkv@Z=9=N!OC4F*mL zfLV>=W|omaoRMDEIh@;Qb_1Ls*PZ*3N??cbqPuwjW=dbCqJuOjfbG3MHmx1hydS@#I(!4l(ZHOs? z3?Lk6F6GgxJqRJOr?)GY>m~?fNhe;C?n& z3M2_N)yit?Q7XPSvuM0 zhR}+s)=@4;0Oc}8?Bp$rn{ymMP@v=wESO|*O0v0K3RJl9&SO+>_l-QZua8NBK|Gph z!d6zCZdXffFSLpm4uqemM?Z)?+T;&RXG6YTX0jVCAEo*r9qro`K`~rN=OP0b3^SJo zn2585e&MfPkCo()l{Dahu=WsPrI^3nR;N1CbXd?B48M%|WV%04(n9qsq68%pAQ@vB z3&M(&_e^WVH9Q{bYHs0qejqS;h#m4-O&Y-ZogKm}cwr|Cp|MzadxRg2i}fZ@ zgaP0akt`pGXHT#!hWi$YfKcjC;?U5V%KeB z;OcrmtuB*?@q2D2eo9yCN00=3If~h=lSaQ}K@TJ&&r)W^-)y}tz3=qs=*_J^FkCezypjmS zOEZ4$f+nFkn7of}20DtHc-tPT>+Cw9nFcVjdUW(e0#MFb(B|mzI8lZk0UTnq=uPeW z`2i%#5$N0YD<;rBA5jF3{w0edOq`kt(>aX_ET_WJbP{^nN(FC2Z=r?gLzMyYY+dE2onn{@QpD34ALq6T5eC#|i`!1n((^ zyo{5D#cz%s)kP56d&czP3vFvQAlgl1e;0!cguUUI0|a<;=c-j6NbPckAQ)E0#wQN{!s9iZHnlX#&4 zx8&ku17}7UK~za_Wyp|pwszu}zWg?>SU3TXD+Vb5$^%zUEK?njH99aW1aURjY(0U9 zFKap)xN0(RP26m{A2toj{v{5eA20s394nce1j7{@$WNlGPYL-<7o1KN`!OvHQdtLt zsOPr6v&Av+KF|h$_mw@9skZ7b#0kzm17q##qH(^zoq1I)QPN(5$)>hvSqeJo^jw>NJu-;0@MfG!RijISlkH zXv2vrUQw)Re z)$n8h`6204V&V)GDoL7Dd$KTjjB7&e?dL}llwlw{*T7I$834?Ys^V8SGKlPz(dyR^ zAkM)b!!PTkWJqjh;mxH@O?ZkIw7`-qHy@x_d~(4}ury$QW#!^OjH89v)x8W;#RRs_ z-}Ayk@Ha(XDY_6qv^Y0@-J7y7l#(N!;sF1#xaPc=K4vI)Z6M|ke_**k@Yo|p5(5vy zyRPZ=*3Tn$eAQM05SPm@%ZwCxLB$6kxg|$Zjl78l?or*!?6n_@0Sb@RP&K20Jqha* z#lJGz-(ZCG(6tL32~-N3t0s`GYRWSB(eqmc_QnM2bGNT~)qjy5CyEA6i56Wo*|&`}i9S|!rMz;b ze!%hMxI?Q15d49g4s*Xv=(cUHe~At|8`C=e!|Q({k~c08W=r$m!8Ff{qJNCV>beRq zcf7@7SCIZ+W3i4Frq}8Kz-dXTwCwD`hh=Kv+jBUE+<^B z{d@)Nj5aJIJfby&DTnW}?j;xgByc33n;NaV4v|;*U_=qRhX2hBUri#6z(w=<*K$|L zudQeRiszl%a|!(hpr6|C1r`4I)$N-mZT}3~v;23!rs62;b2N2L6%{>QJHWu>ENiCP zZ!-SiZs!68NRcU-Jpk0Tt2AFFUZN z$Bx5|DV^O)w3-8M_#KGuOh0*<9N$#~DQAMHn+fA%jD-#Bd>*1wC3H6nM*Y-`J$!u6 zrere4bqZymDNbM=LXZOi6+t@;933tqT0y$baySNKrU&#S5m-2b>yQ8RL6GWzoR!kM z;dH(pFaD6_D-{sYY|&;HP*8dzSkzOT1-*fn`* z?&I5j%-{>?1tyAThq7F>>K5V!$@aj%CXou+h|mOLEYCxN>wo{Sq|P}iq$8m!h)+JS zq&n>MriM1atbpx=*|34=+-jb!)tHKp=)!%!w*Asy)a+Bb=9GWtrxz(Ax#T zojCCf8fR^oGMz>NFCdzsJFVqXt$%DG(5M>8&j%yaet_edCe5`>YdY;VW=YIGhc ztV|r-oLoj`rrgFx>|Fmu75o3Ks{iE0{@2tHp=SR_p6%a^_&-kV-#puY^KAdkv;8;E z_TN0)fAehr&9nVC&-VXcJllVU@_5)-*m?d5H2XWs`s{cS`|BWl(zWg2K{fnypH3GyBWP{|IXTV#I$+ zavGZ%F>$i-urhJ-nu3{lS=rvwu-Liz%y@Zu_}EQZ|Dx)j81b*E`j?I1WoI!l{>#Y! zvk`xjd`lbSeFNAWZ&PREGi5R|;p1fD26GvM+1bt5IXM0b-TxyG?f-ix{zX;)x)Gc# zJRJX;5&tP^$^+(l`gmR4YA@! z4Lhe^ebxZVzYtEv<5^vfe|yD}=??d5=y>!{u&wmzaC5)^rOAWGGPuK#SY=t8fpH}k z*f6O1Cxy)c^Nl0*J%l+_i_HN?s;Y{)veWx&RQX)6 zxk$e8eO*XLo^ZHu@JP~5h1!&j>`b5+PFPihfH=H1QtZXy?k+o^Y}*!)|9kXn_`!R>%^g_vCiO=;NRv>I&{6mejGJ+2_i?T$_-1kbxhxZ78lmCKBj1$a$dEjwl4q8< z?b_ySU1jRTBD}u8zh6R?L$0q;>J?8x9XTWbML~`Q?0!nY^&lD{3rlk|)Z0SQ^A;=R z!zu8OgyXMXj@7#>!c!ftb#@8xA;zlI=IE|nX4WX&K)pP6T?SG^r^$*>;JW{yzPita z(lo!~3!B$QMo>6&Bf#??pF88OM)`qz&WWS~Hh-`j;ab15Cni*(b{M#JEJ*?3;*HX7 zAwcmp7y8LwRiO3*pp3AxiD1!wVa@6;v1Z$UCIO6qs;ieL6J8`lg|#iTQ-T|hCnhHH z>j~xX>2snB%`MPqIi5TVobuq)>rg1)Ny-AsEox1O8UkUmK??L@jmgW#7DtU- zFT_37^unbhapxU`NthERJr6t%iw_*=3sz|+M}}u;EChVH^-itE4bvCH!=$zSAXMEa z_L07>p44(7v>LF3dZ#d^J|*(rHxJQ*8foQY_CY5)Cjvv)oqQ5@L3vo&RGX4tAP|R@ zOl&6)3G9xhiZ+!~BI~MlCjYUEEZK4Fjm>}Hg6ac9iF#+J$Dl|4rhM$Cvr#dmJ`OY}nI3Tx&aWRh72}BFyC$}tj!T8)l%;z(N zJfyJ+aGd1O;8o>=)F^qqimaIj=sJ{2V;lE6tI%MV;(l~VHs4;v;Otoh4=*H%?)|e2 z9un%{s6vuL0@l>wfh%GKLK5$ti79PDmU_gWS+Q3-K|<&)KK*>)Qg!E6XASduHG`0m zw!mYsc$AV788*Z5sqwhyh1Jc)*s9U>nHd(j2V0QuGh0UJ?V4B-s56`<%e9(3uPd`W zGHQ^-#+&A6nAjiCwVEkB@z3vUUxR8$G`;w>EsT5*^qp(W(PgSNOsO-h>GqF%C4;Kp zbx_ite(EEMcL{$aL$1iLDXO?8U+7uJIwY^x4a>@MiL+V~BQ$cuhkvKlHm6(Fmalmx z;}#$_iDz(XnL?fw?HE);`r7&+LftjD599OP`g|&vQ5!GA8#aejx##BkZLMHs?2VDT{^Y92TWZtKHBxV%`jaMmiG1JiH8|{J=wTU z9$V6vzF3KMVXc4api~ZWpcx1#uADb>fs|Xf;BDc|XUcxhUn9Nyhv*Gx9pf^+qXV+Cd5IQ? zij|sT4)kJb?Lt?13wGDiRRh6iE*UsptG&Z_uyh)qc~9T|kx#mLT=GdEI~;-e4v*xV zzEgaHu*J(fqV~Jl<$X_llIX89+!f{N7N*lUNt@YccP4VZFCoiwzDh{FoR?vuCA-c9*v)#5 zaYNpRW+D!b9&Wa!l-!KwbKJK-6J^w`8lvSdK3V^s9-(L~P17=pcgx1?`&OW;Ejq0R zcOJK#kl6Z+kvWTcDwlLa?Z8oKAivcghwCpxo_5A3J(7yJ(c#=_KVx~?cFgtZN4u`) zO2)5(Svls9XG9!?iu`W%1LFwZnd|8_>fF^XJ*@-ENLFX@u8}!_Mvob;=;iF@g*Bl> znDU$A1-B#a>EDC`((|m+_uNeyehZbv0~rFR4cpX-fm%eI{dS+qWE`!Ky`U|4 ze^5~{O_$IsYwYFGH}{1j*M@E5{|*u-8^sP-4l*7W2lfMTp||kV%{6NE-np8v^z|?^m5o|5qNgg9X3$yFvqAaon(xfIqBvz>A2qO1UE+#n5<9?l zsB3kn?zRLF3>+e4dN?i98F+%{X$0lxUEWGU+>jS!y!+jC`V!{!`Kqpyn&_qIlU?_Q zYU-4iF6u*aF$fS@7SMXm}o(CsX z(6^o^bu=CbuiO$jmD^^@{>EJ^b{yT}!3@M$^PD@TCqIrIDMOwKn)NtJ?9i&Jwll&A zC$If!3EF3@{Dye z$BHCaYCQ{8>HpLrFH)z_mfHWMvYgS~Fz(H1T)q-Yh4@qevz|5IBYVftUojKGay#9q z(Y9H(b<41tmg0gVA(BAlm5QSA&Zji5L%UjbA#+Y-c2MXKIz2@eW3 zKj7`@L1PP)=3ds`Gav+hY-vG5{1kg@ZRgGYoV1oMyYU?K5qIq;{gjos%I{`}a_F|Vwo7w# z?Da&-tdECN2rGUZIc^Asr*EUWI^2ulG8=wAaoe@unDz|jMmcPcX|nU{Ca*3LjQ)^t z1bKuUNi(@qvChjVi8JBycfyKf{7b}-`z*oxE5YY2G_m-h%e%*==_b`lOx+e0w0fOTIZV z#^*ohi4867bv|&riOGZl&nZQMO%Tw_NQCZg6+rx%h`+SB#Q zlexU<&PVOZvIN(mq6N~T^!@ED#XPh6gJI^!pLrfetAln4)wAj4U9j1Pk$~VJmG9Lw z#_O1Hy8KdxvJJ?G=pIv1d>}w@t!s{PTT(#Wq8M2#@S<{V@A9QpOU zW{Zt?K0XsBMfwXj%yzS5y-!eWRV*%6CDHFMwu8IuY0iXHtb`F zq+u2^H_r{=akJ9Vrb<3@BHd}znU}GEp>wldf|Sx6v^%yQQv&$Bb%Va^^z(@U+F|mD z1&}+E2^HeVzZ{n_j5|(QOTC;pO`_bMyB?C~s#f52ZZPe2JbheAxrAm5&y2axef<4$ zxLS|ESJFnT`$O}@j6-Aag^3T|?2bXA_t8hidnL_FzWMfq>Q6__ztiiSyIMB7&AhYk zrOgp+>Y}49WQ3|+cwlpfPfVRwdM=dFPLKy%$g46e6?*NeJ3-a>CH+>h3lATl zw`^zQmE*@3$fJ@Q0uH>f_>8L@63os_bKj*hWTAPLFVgKzjlVa2S%DC_cq5pEz8=XW z#Crs9OpDeP4%(!Z*46#Jtkyqs1oN4&u!H~NMEgg_=U<#?|0&67^u|^0<)REIc~w+ ztW11H#&3RHPE%7;He)^>cJN;vxBsZ>@1DDVQPn?q?q=Q`!OD&yzAtYD$+#u|S`Fg= zM&ExGB-68cD@ca>yl&V%SEQfqGc-{al+j(7f!m#*24m~We?^sTy1W-i(E!9*x%&MF zaxGC`p!8AwqFIVe?dR5Ri;O0AL0Px*XnYP01rClEe*cMWAfj?#;N=qQ!^5t`Z+dY# zW8rQEJNC%tP@f+@T*>T^QQgd4f)no7R=>l>M|-DdWO7bf$y7k44YUvC0|7X3oJ}o$ zTJn6sBE4>K!9qNFfHrO$9b$2PoOP0?eryu+IdKQYs@g=-DbwZ$39toswnV%$-;FQw z;pD3FQR*PCA`E}X@sr^?C~!S8aYNtWB<~G>=g=k$Vb+i^L2o&$RI^XR3Ic_i-r{XBWp`{I6Wt9 zR>6R#0F6a==yeKLs{UtgFfXxk_KI*<991%op=5ENu^E1%is_h&Eg8C{^p1xLx@gNx zG|Tx^Z?nY%!T{N`a>?(LOj|q+Yjpm?t-{BN%2ylWWcmCTAT=C9d-bU*(7k8I+)P?h zu3m4=oFM zgJ9K{;V#}!^AJ5NXNFP5$)MiBZ|e?|fgdE3Gvq?1HM$qEY4N2Uc|E)ADAf7o zkPdWU{? zA1<799vVn4R65Of26pF-^kn}M%?u(MOu9%zbr3n_3M4Y*GIjiuOP3~Of=AFUPR2da zZfi{*^Dclwvy8cToC;7LwE8>irb^BoEyB9GaVRNBT*V}7|Dm+ZtarA~c@uMJ&i>c- z5ifUX?01a};!A`v2BP0$=#0hOa$_j@QD1h98@9+FO9j*j=N97gDAlGd#e}qAaEAis zhQLBvZG}V;$Nl`T$b^?)oR1q?Q}>N+eD+di(lY~=y%;Y!%;80RkH@phV)e<|CZ6tD&*4)Shz0Px_u5Xi zGUdBqF7%>^;>}+b_#pdlGg>N^7UsA8SbF-_=fg_Qc{TKVq^yCLEn3S?J(4pqiiaZI z0Dq_5@me4G;`cgLVkvoQ1wS|8`GCw!A)9z?@x%M64630t`noBd@&SccBiG1KX>DeX zoFUd)`Lo^8(zCJB-)1K`rJm+%h~svtMqj&ET(sdfm~uTMMp~VnTIH;h3d5)`qYFPW zNaABffPa25bY5feFBQDP_1Dh|P&)jTCkBJjN<&oU)Q+sI_ovi?<+y$F7J;ch7sqh0 zIN~<*eKT=Ll;?szb`sZV0_UkA;p$zr{l+i*jp{QkooV-$0-Rot5AwQPAe*_*T+(|R zw|rozPothJO$}d6SWKr=_;2mdemeanXn94Ra9$~p5h6%ehI9YCDf%E%KQZHTxGDkrujj4qIa}7 zd$~q~qf(8+KojMvYKw37228{9Nm76WRLA*MfU7|fY7Nc8(UrufiVv=o<=iG# zb`A62E4fbFl!*1MJ`n2EL{(y^UpD^E2Uwg>Jk~eq(ae46D-!yJsmvRE~;suSOOV~Il#yiBN>VOB4+-0I`}!oYslS5(C-6`hu@900tGP49lR4p&wG>+16kz$@`rvD$|^RR z9imcumE*9>H5J-ubftsLmNHg|Pwt4J_!3uSOwrwG&TQ(MUqkKdZ3YN8&yqaaolEsv zRh4cTeqci5nai_Q0*V2YEh-fr?7SNIET|9rI>4|uNkVZ)3}#J&u~xdvYj-^j*9K$u zd+oaB8m+)>2HwQpsX9I_udSvHPaYX^Q*~#KM^~d)rSa;v0GF1ov0a$n>hXD_F9Pa) z`LW_m>0#!@nzH;S#V~6D6CJSSH>3qabr}+k8W!Hi%M4PUU2|FR_yL2|5d_39*)2j? z0^DUd8rA`1Oe#zV25@3UzM9&!5>1Eg7FRA3%`#dSvg*WK;1)Sm4-KZJbG9xR-c=VviEt29Tr)nG^-8;_Fqc zYeV>i+NhLQhln^U=j804Ka_T7$n{^lOWgg&KUC=#@@$)po|sZ-4-j@(+wbBf34QyT zgUDI}KmDGZv}lD`AizNwXUuDN3!z!^EgvXHW7n>q4SbfS__Trh?JA-(&4}D43kfmJ zE4X;cf$y05v-VJA%Lv8qVWQCw?q|)Uey?NQg36>E?NnJ4h~Ij~u;Rd-<(ZCGLC9A% z&|}HVzsE9XlGg;NFuRvVEa}#)nqO|kE~DifONah&rpSXkf;M3mGoYCI*2Ft2U#whc zPsIl5@Q?$()$1H+!YC|14s%HKNarPui&@?Y%Je+Ea5xmno%S@icW$nZZUS&iO!72Kv~ zd~a~{?GMWt0@6%fq3)eI^JuDKB;|urN*Qc=d@QO4CK|d7gGR zr}v+U1Oett9%+LSrgXv67L3fbZJ~)8KWNuS>&MCx?jB!X7{c-j+*(q71aOmzzGevu zJiP4g#+e&msO^=pt|V$l#)!l6>vH7_eUBRB zsDnNcDKXQwgSH}#Y9s%Hr?Zc}{)nA3_P_;Qd(5{a&tGPBDPwVV?I~Se zwW|V#b3c2$Aft8&=jPfU6}FFM3uQRmY2vKC>(hL)QgYDI(9zMEV?S0Cm05p+7Hiao zj|Z7cRw^cTMkIpM!@J=rUb$0W;Y2dFlwqNFay|tq>?!9mG8t)2aW0cct{{>@H^C-V zd^RV;3ieN7L5?eyOD-kUGTj?P%7J;hczC*D%ZA8Ph&;)dXtY$0)X3HbEe5(Hr?nO9 z2A_R?!eRX}HfU6n9Fd}6f)p_LXNuJ~EJ3@Yf7cIHV>>uo%o6U7Emn%$=s*FHTrd3_ zc401kvhh3^>AQOSce5^K`xba=r8AQb-$Oa6ki~&70`S|rP+DW##n@z2JM{U%Y3@*u z?0V3UAtTlXBNScm)lIkH09H1Y!gEc#YxU3wg-{ynta?4JOLo03IF&P!2r_-PwsYq7GnK%|E>s( zxvOfF!R;tL(}IUbqCM+BkV6Wkf>R4#3r24F#ei`r@UKMx7XLe(MrN57dGodJl720meEFFC_Fk5@3fN2=dI?~uEH}0_Xv7?!X$pVBbElb}c7uduYiSE^jBFIlQ zSWUm=cDy}~Luo|NL;aLeE)uE2b&}%a`;upod7h^S110(}$tj<;vt)%Oy`8e~Dxwn;fbbKI31V#wC zp22Pt%l^*kGHG~L;>L|+Y)4RXlV-cuR|?(kn7)maD55hGRc+op+eJA$Bui|zD6Z(- z{q(Hsv23m*kL#q>n=s%yaZ3CbCw{=3$v1UlbUR0m9X~v@tH&pKpQNnTR4*)KTd zJr+oE9lV0Ln=}{7zuhtdf)uVbCNFBZ5}+a9u&6=tCey9iFF%rZRw`CW;*-8u4HHbtdcS*OE_h`x#_xu+`MqD1 zDA&|S5&9!9i3ha6HTJY=bWx@!JTu-%z~V1>#Loqn=jtCt1Hke^(eRukiXUZ;)Ls3) zVzILaht!9m55jPM6MR2%qLW!&lQf!D#+y`!rRVz15ZALk!}8%}J-X1|FB@eQJyey3 z**Ey21wB6)`Qc+=y`012%Ce;hZ=#%o*i!JduE*fKGf9zTJi+qp#iSu{=W?8gpi*F7o z_a%Zcl4#cN{ARZaM~IeU2DKOBi&cbQgu zFJ|Q5U6!RvSfUEs93-OGPYz9v5A9b&X;xWkKHY7Pk9sHmuhOnEEUK*w4GeR#jCKEn{l5-gmDF31M>iNOI51tQniaKg2760%70l5zb9KA@YFIhR4#( z!wXteu1InCVr3K1uubkTU|ss8YAaj!uaH91nwwA672@95f1|9W()RlfU zYOZ6eMZUS;%Z6s+`ns9v>ul?Fnl-AM_9hrrixo}M3%Y@t`f}wo59rr)57`edp_v8W zO{hc}Dq3-8LnYn=7p`be;8Ac#Dw`6;HHy6;&A*GAAC)uZP6Hnnz33L@;{;L11SyEN zuvb_t-@&+BDY@)5qA>}${@%yWo#lfF-v_jpZb7-s?7>xh^pb+1Y}&MB!yWSR!7fstB(LQ4bY-@up6>c{>Fx&^ z4y43@9*z{1Jw+#CSDyrZi69)-4@M;b`w;JikOYn|NFUu&O>^c3iy?kx)C)U#NnM36 zcJ3=MfehQ55d*B6Ox)v4Z$zrL$bG40y4DYnBI^<&l(cx_=qLvSDKHXx(btSeqlbxA zIbhn6Yty{O^xQ^Q8{oVAbUHMLI=z!!1U%13pu%IGE>z(Ekde%3OWCRbL9laHHm+7Y z4P~27gD3d?;0X^^+w1btYHS%J?sfCV)r*dk z!%nTr=B&L7ELRf+C!bL!_e^5D5JIq$tfOX>nG)|reG9~lKq~Rei@&TLDj88_q&zpE z>|1Q#I@q%jztM^rD^!`(pGV?Wn0>Wwj@_Zbi&NKpzE>9IC?8Wm&RsWukQ1LMCIoo5(AEAv@NpuI-~i(^ zGYB_o$#s@OClUwJS%;WGhh$eHBXC1&%sj}sj7fUj5Mql&^oNk}?wsOl4Pq`GcW;A| z?%FWYwtY)*y@1&^|7-KZuMEB2s2_G)W#8QoU3s?NwGf^LodfD8&BF{D)$TP9ijn(> zMcf1_%N>}b<0fyDQ{#PzPT@gdl^~+R2BM@=h~;wFW>T{fLAmO>L}LvU+Yej!_i}fD zO5euVXPyineMr{*DgZye2htqHD*fE|f#g~MpA;6=!EN8kgd9&YgVLq}IZ;~C4bCzR z@P3PK?{;;#X>k*A*TadNNZpt}7MCBl8IbuWK_-^vJQ>Fdp42Hm?h#*W$6g-Yq#n$Z zRjImFn{T7++}n_eGcg~#&mc-ucm)$*D#~r9`6e>dFA9q+WmozumBeH53Gs1sydxf3*oAtwQ$Obq>B)QhKdk6R&db%?Y4%%`d16Zq)KZs!xK4x zXyTy)!9*G|CNa5XG0`lrTSrppwTdHhX}S*4xo>P&+AOe>;Dqp+(}g9C zQ9qj^YH0}-2gc#G@eWNhM`x#bGh3k9>jfwuwToRUULF+(L zhil5#Y;fzBo%3_st5!_V+gM1A4d2seU?4qy9Vz!_Bs!z&)` z+1Twcum*Y^SB=hn$&$hAp8~0t$M39GPS}XEmE_})*l2{r^hp3D;9SrMj6n>lGf@&X zi7^R2DBSDF@dXB-Z)Tcs_FhS~s1;BNTJ%fxO+bFc1@83=HdU$?pksjH#Kan?0yRCy|N^hIQ{HA?TX=3Rp<$KF1_v(>2IlTc)%rJoV#S~dc3Cv*ZmcV>?`TiZ&3r80Yu_4{_mU8 z(^$>Mr3CR~8OV^-d!6d?saK`JIs%@FhWy5{!;EvU*z7&J3T-5;vRr_dMOdz>J@0;# z$q$Js$i;3=FvrRGIP44z=;=+M#j8kic#XA57;2gn&SHy`nT>RnXv$kDwb}%Qy6is@UXmSys3XJeRECm4>&%T(n>+MTi%di)RZF!&d zM@`~bYlOmV=aF9%Wn+|Nyun!cNXV9HQTWHA@7lwc_PXo3TgIQ{CYY<3lAiaY7p-A} zc+`p7M_1TT%1s#bW=CRaR#S+U1?tO59kNw>RgJ zV6P^qE{Tg3kd_}-R{2-utT@Td@LuoXlaELvC(&`Aw=-DI+LdAB>-J{oudn~IG{O?! zChR5=w#|c7u|CC4*|(2aNmW(07WxJ&og?G1G1`pVc-srABwTiT-wc#yn|(e{T6&|| z4jpiP%6ROXy=d{MuXW3Y05{9)t}dWX@>DWn44k7NyVzI?g6bXOZLf+-XFXDF9DS8+ zf?T4W!&(z|Q=D)2Sw2|X4C&oxX-=xjvq0cMK@y9Q?^0NVtCNxIdCV+~!01XE%SNg_ zb6rW1EWFWfzyE4&eOs)sVTDMZm|m-7P45hxS&eZSu78QFYgsJCCp*;_{GS1oQQ)_t z7FF!s9i-e|{xM?+W^6BPcC7Yd16&9|xv2VIHR5bNrW3F6yzn)c$}X_L{=oX-{T(BH z54yY-Y>OTaltMcwT|I#LIe*i3f(SF^oV?6LqnP>AWD1S7ZZ|*caKq~S=`TD3jf1G| z^~Brb7@8ZOQ!Cd$iHP8In`AfPQ<#P>+YYJk;4|G#ZcWKT313AGwOQ4$5&}7M4BbF} z`Bqbwikx@@pK75}ET9}OQ4r&>nE|ZNJLaXc*!AitR&*ql2=OuR-hGCQC>fDOALn88 zPecJpd;6Zv0n~Ml?p%#ls3A)Y?DBgmQ=?MBL@MCUN@SVdufnQ$1%|vaht%4T${5HG(lJu71R)>jaX%u*{aQRzcPUOY3Sua_0}is z(!1Nc4HXUcV0;Ik&wfji_-_vElHMwiSH1RmMiJ{rL1iJz-PAjzMqAwor}DPcNiW0_ zb2N|xJz!WztAs*4qmb>Zo3^nyv7otyYr;_9s&DO7J>4i-@#?jM&7%W1tAaNm0z1MP zE2KBRTx{*u49dPMHBB2`sHr4XUH)mDf+>eG?YhL2iovK-+DSWNPnVQNYl-GxF&&n~ zpzX1x&jh{<7U-BwoIadbU#Aw7zi*#@GdA3%pOMrhqJ^dmp#IkB3ZOiFp6jwILj*x< zwfQ?-pO|FeA3VNscj_J40h(||Qa+f})l8|gb5j-|z2I5KrplHoTdJVO+*kvN;MQw6 zI~fIWSyCTLbepGhDFj(n783QtCMRvh(;gY zf#cI5C_rdy=fs$LYMZ_^zT)%%VO|}bkdqg}ozsIG*;^2{Q@f+kyFeRZ(iix&LrNPI z6Ye?>s<_+D-mIE^&3d_C#pi{VJDL8wt%i^*_=_}dJU2`P2B?))UhOZqmucGA1x#%; zs^5~`70-d=5x{w$u2>S5!E=?BjT~+-EIpH=y)%oig6!=QYQX$_JDQ&Tb_?m{HY5zm zehYAn7CmaLDwufOSvcR~`XH@r?C$m-VLe zFwrw+UZ`G)@)!xftZctKMBzh{2|p&weh&*ZAFrRnYUP%*EQN1#obDt*F0Kriaxj-=%DdvIcqKTnqExr z#6-AL6Y*tP{I<)5@stGP@sL^ShDw6ilxHNQuld7N;|t0cuQOK-rB(uvsW+KWo_>3F zq%u}cp2bzfakPp!yRaNwj};P?)o@k$Lq6DeL^*N%|x_Z*!Fy>~McllmX;2gnF7rl&8s; zb4|Yv*UBYUK8~3uv_qhOpXE2cNzo6q6li&HZ)X&KP@u?x>q%$Z%{t{rBuSHfaYFq?q^OdaO3w$R5XS=3)Rup@nHP^wjFHq__B;Dp_k zhfmdCpPjJ~GeVlBRUl6R$0@p?wGmA&Z(i;V}+0>X(xCWn)RPgVsjRJU6k9ijTz872e>K z3iaICd04a`+N1~~wBiOgQ&r_wVPZXW>rZc99a`@jk6czOW`DF<67zVvKk3m_L0slm z{_2X#3U;)E-I4_kE$)Jn=Rmms^lFr3_TEhte2ui%tajT7Wz)5<)CTwmRls$-@!lIV@`KWo0<4BQ!UZ_I$}J zkZ*c=;zKSE#1uC(I?T3w84X-^$v!M=_Vx<3?r>Dtg=CfEmL)B1&puu#Qna#W z$)Zg{?f6tHNvCMFn}M2Yz}CXp!SQ1aiG>%SC!GXMnRWWhe7S<(RW+TA>Y^)gf0ze` zYA>Y=QIY?_W6=uztvKqYtWseqE7>Uec80gzwb{pA!13#%*p({#H<5P%u|`_?G0dw5 z%kDX-A{F=7dY`ffJFKj_R|n8^48O2vs3h=JKrQ8494kHSH7-YK7$rwunW4BJF+)NP z<=b^3@YPCutS@6&LW42JSDJCK=kD5CgeC@_zpOU&*1oH+7Y^OkDQzS#AFlAqR#+jB zuq4xjhiYPQl8&|4V&_Q~{X%5q&}OU%p^UB)0duA5t$?Hf#&6N|NVc7Ii-(49_qLyF z+Cn(?yE5U#KwX6dvlF9|2A_P$$~KHRE$@Fx6ukb9cAxkz|A)QQygyW`=XNK@zU+pZ zW05PrT4HKZNHUGXY$zzQs-YT|Ds}dHU4o&n;uY>B-EysA-{i}u!oK7_-ZE~2xk^t+ zLA!wmOR@<2J)1-SYRk>BC=m^4GoEsmqVZ7&tLkA7KPRAKNC#auB`qKcDI_ioXimKwF=b7Q=gclRqe(&DO6zw?_)4~LU@m8+gl2JIcF7Fo z)E!;!Du%F{N1(Th;CMBLo3k$2cKoOknH`)0Dq@WlJrl~$jS5ExEnVq0a*V%?=$T{z z4x_^boQ`I~FVD@FeQh2j(95maY&azF=2WehqQQmX9fS>Q zRqs_BK)#-c|EVTa@V7%!Sb2VJBZy&KH4SrV^?v z9sk0y>0%joi+30A500m)uQ3mG6cfcQE5F6-4RPDz5d{(J%xXuxN51`ihPw;rizXh9O%#u$S#<5J7CqkX zB==faQX5d@sY?-(*Y4;RV+kS~N4qz;W-IkfFzOm57cuKWK#9IwNz1p@QY zMDs3b3jb7}_k)RidNNd47+;>Pi!t-Y>};;>)CY{g0SO*V?acNGi@U2UH=uUIM>%~2 z$6dh_S9#?BJkVn)(cBvSPL=Mp=xA|0Q225rd!IO(I$Tb#Ofw>vxfi{SHu4*4EmRwP zMVk2BY@3avhmLr?($9Cpu|Pd*`E^10(?P5*w#{!o)tWKw^QPYAPfk_aeY$SHh{oZd zoaT0*$APXCJdavFmw+%KPWW4zyUtKowLxg7>(l4E?vJHdWK-uC9$$g6KX7PTO5s3l0ah8$dyGQ8Dq zs15X=0w@9c=8eYtu?J|TA8%|;QO=G@+b`03o7xqtx|Hh{cNu(ik(0N+bJZodw@)r~ zZ^Et}xj7w#yruq*Ku-(l8`6c^1K8=A886m2`u&>_H4qb!odt5-9-zqzZ4Y2)2ePt& zHNaqXuojcn&l+F<(jIUzQRmtNe%9ll!Aj5aSJnsEmesQ!hacZ$)M8))GHR(GZ(fJ) zm}g^PWdO3MsX?Hd<<%H9&$Zmb?z=y8;)0?s?1UB@z2?sd%Fdkl@l6mDw1pSM41u=r zf}sQw>TFCbKo(Y3CT1peh!&XXXMMxq)6Mc16!l!qQU%&^Yi{%4xx*BJx zgAC|~c7n8MwSB$>l?gqP4u3&^NE;g=Rykq{S^?`lH;oZJ^b^z>O3aOFbf z<3Py;{|`MX-+eGOS2xr(qchb}4&cVD2BEv{MUZ>mctp&nK7%T1AS63TsD7I_cq)i2 zT1Fn*C1dTge8K33Dy67aZY^5BY6zG;WWXg2PUxyd@n$87<<{mJyp&}Z&HFmbrb{I# z2@&KsLnLmvdZS5;3DZP|^tNSuiU;5`Nx56p$lcd&AsOg5n>OjLeS@8$+bVJ}Rk5n(R8z zhy+5zFY^1*|8ry^Y6cKpBQ4|a?2DSx&C)g3vA+aoL}-3==ga--N+r=t@IZAkj!T-$ zE@btdg$1I0o#(x&#Um_R$auK7u~!rC9s%IRC}KSs1#B1_t*_rR&C~aN zQ~hk=jir&9XyW!ntlB$bkBehhYFX{k$jRro*n_@^L--VZp z9xq&!<*HDOAK8kQK_z}Vj$>da?of;7s}0gpc^=$Ni|vdRJWWle_6@H*O_atmg(_eZ zorVONno<_6Q}H00nnpiPA@C{x^k7TH!M;v{Y%SgTgG*{ti!F2<)9ZUiGuB=mS_idt&0Re|phYd9f*k083Pbrb|NjBiGgenIGkj+7eL&5n zo1YJX(7yx+6+aHqsP#|g4SXc=7Ttq9*7LT?424Dglmq>Bj?5`>Ivh`BM-kP4J1rp8 zy`~q#Ifuq8DaxGQ%z0~;cY-?>KRuYt1EZq62BBbkV*8k?Gd~Nq+oC|)%Z#@qP!gE7 z;z*9nuF<%we=F>TTv#&^-rg;bSJv3pl<+FKwR{HBkC8JsZ+wH?^W}NLFR;w?xkbO8 z%J90f7y1==HfU{8=mtdz^0!1GGuLU6pqpPBlya18L^l9|iZ`@ZkWFd{r^hP!J?3Y$ z_m%Fc9|XwEjO;G!8g$YwCyh)qY}sja(zt+lcu+;eqKHDKh=q)%;E!w$@_BJ*$ zn`3w^qBQJU$D%cp=DX;KCeSNCTrE!jMt3s5s1_N;%&nlR{!>@9+0+nL-WNK_@y=I9 zIg^!p)r7ibUutoM)?`w{BpjeebHc&nAsn9%|M#cr#kIXUgB`DEFB+2KQy6p;)x}}sXRzZQ;frcRx_uH! z$T3~iMPYQOFzDL(;xO4$7<9GzGuZKVxF6T?q+oXngH8(HUrf&Igk9AiudPpz`}|2T zNcLlvGX^GB2#{F=N_Yi55rh%Q!T@Fhv9fAvvOs>Gy627BI28?@a=vqIof&n!=shvx z`18C`Bs3>6|AAx$fi!?x5GK~+9efNx=-UfBP)l8tL6c2Wok^4BS0bf#DjGVWvHX0x zIB(QT7$lP#h!Lm(?ZShS+A%T$*}!V7(7-XMLs&t~tXgd6Gx~TV;9vbcezp2>VFUL0 zhkjM`pD$Wx@q~Q~JUDy$N~m3GeZ+sjfn{igs7=& zXhOhhU~!YK|iY9V+AJu!>;MtUu9}?Iil~cNIZf0$|m^9(Zx4#$xPu)SMoF0jmi1 z2!%66eiOf==HlA`m0mef^Ml;>X8|}LTRRx!1sHbEh7I;OG|KMr)nLFo=`T&Ex0QtGd87VQG)#Q|K7QwK*w?4>PwL}m zmcicY=aZ@&26=&c`I%_gr_x`Y-seXKojCn#_470Nu%@4PTJBW-Nj?4Fk>{OuhCyDS zzW(o`k1OmyE^NR&f6K3m{%gJc-<2F!+|QJdGn_>Bmy7zR_4gB9|6GAT(U$G>;`epI z)Z@>lqkpc(pXr?aeVs71d03sWweB;WUl{-2n4YVA!zzZY$(|{0Vg7#=pR3SHiXeWk U>nQ*f02?F#z#J>|_Y=VX02qDyiU0rr literal 0 HcmV?d00001 From 963172c1333b89db1543c7be2b6d06b3de51ae95 Mon Sep 17 00:00:00 2001 From: broeder-j Date: Fri, 27 Nov 2020 11:58:34 +0100 Subject: [PATCH 18/53] Add further cmdline tests, code cleanup --- aiida_fleur/tools/StructureData_util.py | 10 +- aiida_fleur/tools/check_existence.py | 252 -------------------- aiida_fleur/tools/common_aiida.py | 3 + aiida_fleur/workflows/dos.py | 4 +- aiida_fleur/workflows/optimize_para.py | 2 +- tests/cmdline/visualization/test_plot.py | 8 +- tests/cmdline/workflows/test_worklow_cmd.py | 4 +- tests/tools/test_StructureData_util.py | 112 ++++++++- tests/tools/test_common_aiida.py | 21 +- tests/tools/test_graph_fleur.py | 31 --- 10 files changed, 140 insertions(+), 307 deletions(-) delete mode 100644 aiida_fleur/tools/check_existence.py delete mode 100644 tests/tools/test_graph_fleur.py diff --git a/aiida_fleur/tools/StructureData_util.py b/aiida_fleur/tools/StructureData_util.py index 49a1f46ad..c9c4f1605 100644 --- a/aiida_fleur/tools/StructureData_util.py +++ b/aiida_fleur/tools/StructureData_util.py @@ -666,6 +666,7 @@ def get_all_miller_indices(structure, highestindex): return get_symmetrically_distinct_miller_indices(structure.get_pymatgen_structure(), highestindex) +''' def create_all_slabs_buggy(initial_structure, miller_index, min_slab_size_ang, @@ -705,6 +706,7 @@ def create_all_slabs_buggy(initial_structure, film_struc.pbc = (True, True, False) aiida_strucs[slab.miller_index] = film_struc return aiida_strucs +''' def create_all_slabs(initial_structure, @@ -728,9 +730,9 @@ def create_all_slabs(initial_structure, indices = get_all_miller_indices(initial_structure, miller_index) for index in indices: slab = create_slap(initial_structure, index, min_slab_size_ang, min_vacuum_size, min_slab_size_ang) - film_struc = StructureData(pymatgen_structure=slab) - film_struc.pbc = (True, True, False) - aiida_strucs[slab.miller_index] = film_struc + #film_struc = StructureData(pymatgen_structure=slab) + #film_struc.pbc = (True, True, False) + aiida_strucs[index] = slab return aiida_strucs @@ -1271,6 +1273,7 @@ def request_average_bond_length(main_elements, sub_elements, user_api_key): return Dict(dict=bond_data) +''' def estimate_mt_radii(structure, stepsize=0.05): """ # TODO implement @@ -1311,3 +1314,4 @@ def find_common_mt(structures): """ return None +''' diff --git a/aiida_fleur/tools/check_existence.py b/aiida_fleur/tools/check_existence.py deleted file mode 100644 index 9af938de1..000000000 --- a/aiida_fleur/tools/check_existence.py +++ /dev/null @@ -1,252 +0,0 @@ -#!/usr/bin/env python -# -*- 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/ # -############################################################################### -""" -DO NOT USE, this is crab so far. The development was stoped because this is done -with AiiDA 'caching' now. - -Here are methods to check the existence of something in the database -example if a (successful) SCF with the same inputs exists - -Since cashing is not there for data yet, and below are some basic querries I -leave the code here for now. -""" -from __future__ import absolute_import -from aiida.plugins import DataFactory -from aiida.orm import QueryBuilder -from aiida.engine import CalcJob -from aiida.orm import Node -''' -def check_existence_calc(input_nodes, successful=True): - """ - This methods checks in the database waether a certain type of node with the given - input nodes already exists. If yes it returns the output nodes of that node. - - param: input_nodes : List of input nodes - - returns output nodes - """ - #TODO: some checks and inputnodes could be parsed in different formats - inputnodesuuid = [node.uuid for node in input_nodes] - - qb=QueryBuilder() - qb.append( - JobCalculation, tag='calc', project='*', - filters={'state' : {'==':'FINISHED'}}) - - for idx, uuid in enumerate(inputnodesuuid): - qb.append(Node, input_of='calc', filters={'uuid':uuid}, - tag='input_{}'.format(idx)) - - qb.order_by({JobCalculation:'ctime'}) - res = qb.all() - if res: - return res[-1][0].get_outputs() - else: - return None - -def check_existence_wf(input_nodes, successful=True): - """ - This methods checks in the database waether a certain type of node with the given - input nodes already exists. If yes it returns the output nodes of that node. - - param: input_nodes : List of input nodes - - returns output nodes - """ - #TODO: some checks and inputnodes could be parsed in different formats - inputnodesuuid = [node.uuid for node in input_nodes] - - qb=QueryBuilder() - qb.append( - JobCalculation, tag='calc', project='*', - filters={'state' : {'==':'FINISHED'}}) - - for idx, uuid in enumerate(inputnodesuuid): - qb.append(Node, input_of='calc', filters={'uuid':uuid}, tag='input_{}'.format(idx)) - - qb.order_by({JobCalculation:'ctime'}) - res = qb.all() - if res: - return res[-1][0].get_outputs() - else: - return None - -''' -''' -def intersectlist(l1, l2): - common = [] - for element in l1: - if element in l2: - common.append(element) - return common - -def check_existence_calc(input_nodes, successful=True): - """ - This methods checks in the database waether a certain type of node with the given - input nodes already exists. If yes it returns the output nodes of that node. - - param: input_nodes : List of input nodes - - returns output nodes - """ - inputnodesuuid = [node.uuid for node in input_nodes] - overall_results = [] - - for node in inputnodesuuid: - suc = successful - qb=QueryBuilder() - qb.append(Node, - filters={ - 'uuid' : {'==': node}, - }, - tag='input') - if suc: - qb.append( - JobCalculation, - filters={ - 'state' : {'==':'FINISHED'} - }, - output_of='input') - else: - qb.append( - JobCalculation, - output_of='input', - project=['uuid']) - res = qb.all() - if res: # if there is no node with such an input return - resnodesuuid = [node[0].uuid for node in res] # needed for common list parts - overall_results.append(resnodesuuid) - else: - return None - - intersect = overall_results[0] - if len(overall_results) > 1: - for res in overall_results[1:]: - intersect = intersectlist(intersect, res) - qb1=QueryBuilder() - qb1.append( - JobCalculation, - filters={ - 'uuid' : {'in': intersect} - }) - res = qb1.all() - # we - return res[0][0].outputs() -''' -''' -def check_existence(target_nodetype, input_nodes, successful=False): - """ - This methods checks in the database waether a certain type of node with the given - input nodes already exists. If yes it returns the output nodes of that node. - - param: target_nodetype - param: input_nodes : List of input nodes - - returns output nodes - - Hints; successful is only for calculations types - """ - inputnodesuuid = [node.uuid for node in input_nodes] - qb=QueryBuilder() - qb.append(Node, - filters={ - 'uuid' : {'in': inputnodesuuid}, - }, - tag='input') - if successful: - qb.append( - target_nodetype, - filters={ - 'state' : {'==':'FINISHED'} - }, - output_of='input') - else: - qb.append( - target_nodetype, - output_of='input') - res = qb.all() - print(len(res)) - if res: - return res[0][0].ouputs() - else: - return None - -def check_existence_calc(input_nodes, successful=True): - """ - This methods checks in the database waether a certain type of node with the given - input nodes already exists. If yes it returns the output nodes of that node. - - param: input_nodes : List of input nodes - - returns output nodes - """ - inputnodesuuid = [node.uuid for node in input_nodes] - qb=QueryBuilder() - qb.append(Node, - filters={ - 'uuid' : {'in': inputnodesuuid}, - }, - tag='input') - if successful: - qb.append( - JobCalculation, - filters={ - 'state' : {'==':'FINISHED'} - }, - output_of='input') - else: - qb.append( - JobCalculation, - output_of='input') - - res = qb.all() - print(len(res)) - if res: - return res[0][0].ouputs() - else: - return None - -def check_existence_wf(target_nodetype, input_nodes, successful=True): - """ - This methods checks in the database waether a certain type of node with the given - input nodes already exists. If yes it returns the output nodes of that node. - - param: input_nodes : List of input nodes - - returns output nodes - """ - inputnodesuuid = [node.uuid for node in input_nodes] - qb=QueryBuilder() - qb.append(Node, - filters={ - 'uuid' : {'in': inputnodesuuid}, - }, - tag='input') - if successful: - qb.append( - target_nodetype, - filters={ - 'state' : {'==':'FINISHED'} - }, - output_of='input') - else: - qb.append( - target_nodetype, - output_of='input') - res = qb.all() - print(len(res)) - if res: - return res[0][0].ouputs() - else: - return None -''' diff --git a/aiida_fleur/tools/common_aiida.py b/aiida_fleur/tools/common_aiida.py index 25549bb71..997d76e4d 100644 --- a/aiida_fleur/tools/common_aiida.py +++ b/aiida_fleur/tools/common_aiida.py @@ -192,6 +192,7 @@ def create_group(name, nodes, description=None, add_if_exist=False): """ Creates a group for a given node list. + !!! Now aiida-core has these functionality, use it from there instead!!! So far this is only an AiiDA verdi command. :params name: string name for the group @@ -246,6 +247,8 @@ def create_group(name, nodes, description=None, add_if_exist=False): def get_nodes_from_group(group, return_format='uuid'): """ Returns a list of pk or uuid of a nodes in a given group. Since 1.1.0, this function does + !!! Now aiida-core has these functionality, use it from there instead!!! + not load a group using the label or any other identification. Use Group.objects.get(filter=ID) to pre-load this, available filters are: id, uuid, label, type_string, time, description, user_id. """ diff --git a/aiida_fleur/workflows/dos.py b/aiida_fleur/workflows/dos.py index 88508c749..30d7186ea 100644 --- a/aiida_fleur/workflows/dos.py +++ b/aiida_fleur/workflows/dos.py @@ -59,10 +59,10 @@ class fleur_dos_wc(WorkChain): @classmethod def define(cls, spec): super(fleur_dos_wc, cls).define(spec) - spec.input('wf_parameters', valid_type=Dict, required=False, default=Dict(dict=cls._default_wf_para)) + spec.input('wf_parameters', valid_type=Dict, required=False, default=lambda: Dict(dict=cls._default_wf_para)) spec.input('calc_parameters', valid_type=Dict, required=False) spec.input('settings', valid_type=Dict, required=False) - spec.input('options', valid_type=Dict, required=False, default=Dict(dict=cls._default_options)) + spec.input('options', valid_type=Dict, required=False, default=lambda: Dict(dict=cls._default_options)) spec.input('fleurinp', valid_type=FleurinpData, required=False) # TODO ggf run convergence first spec.input('remote_data', valid_type=RemoteData, required=False) diff --git a/aiida_fleur/workflows/optimize_para.py b/aiida_fleur/workflows/optimize_para.py index 901bd7b42..163774c8b 100644 --- a/aiida_fleur/workflows/optimize_para.py +++ b/aiida_fleur/workflows/optimize_para.py @@ -60,7 +60,7 @@ def define(cls, spec): 'wf_parameters', valid_type=Dict, required=False, - default=Dict( + default=lambda: Dict( dict={ 'resources': { 'num_machines': 1 diff --git a/tests/cmdline/visualization/test_plot.py b/tests/cmdline/visualization/test_plot.py index 06b6f4eff..cfb9dbffc 100644 --- a/tests/cmdline/visualization/test_plot.py +++ b/tests/cmdline/visualization/test_plot.py @@ -21,7 +21,7 @@ EXPORTFILE_FILE = os.path.abspath(os.path.join(thisfilefolder, file_path)) -def test_cmd_plot(run_cli_command): +def test_cmd_plot(run_cli_command, temp_dir): """Test invoking the plot command in all variants. If this test hangs, --no-show is not working @@ -29,14 +29,14 @@ def test_cmd_plot(run_cli_command): from aiida_fleur.cmdline.visualization import cmd_plot # import an an aiida export, this does not migrate - import_data(EXPORTFILE_FILE) + import_data(EXPORTFILE_FILE, group=None) process_uuid = '7f9f4cfb-4170-48ea-801d-4269f88792e0' options = [process_uuid, '--no-show'] result = run_cli_command(cmd_plot, options=options) - #provide a file with ids - tempfile_name = 'test_uuids.txt' + # provide a file with ids + tempfile_name = os.path.join(temp_dir, 'test_uuids.txt') with open(tempfile_name, 'w') as file1: file1.write('7f9f4cfb-4170-48ea-801d-4269f88792e0\n7f9f4cfb-4170-48ea-801d-4269f88792e0') options = [process_uuid, '--no-show', '-f', tempfile_name] diff --git a/tests/cmdline/workflows/test_worklow_cmd.py b/tests/cmdline/workflows/test_worklow_cmd.py index 504f6ff1c..18544a19c 100644 --- a/tests/cmdline/workflows/test_worklow_cmd.py +++ b/tests/cmdline/workflows/test_worklow_cmd.py @@ -28,7 +28,7 @@ def test_workchain_res(run_cli_command): EXPECTED1 = '"total_energy": -580.0719889044,' EXPECTED2 = '"energy_core_electrons": -316.8117066016,' # import an an aiida export, this does not migrate - import_data(EXPORTFILE_FILE) + import_data(EXPORTFILE_FILE, group=None) process_uuid = '7f9f4cfb-4170-48ea-801d-4269f88792e0' options = [process_uuid] @@ -52,7 +52,7 @@ def test_workchain_inputdict(run_cli_command): from aiida_fleur.cmdline.workflows import workchain_inputdict # import an an aiida export, this does not migrate - import_data(EXPORTFILE_FILE) + import_data(EXPORTFILE_FILE, group=None) EXPECTED = '"max_wallclock_seconds": 300,' EXPECTED2 = '"num_machines": 1,' diff --git a/tests/tools/test_StructureData_util.py b/tests/tools/test_StructureData_util.py index 0542e510c..b7f1a6050 100644 --- a/tests/tools/test_StructureData_util.py +++ b/tests/tools/test_StructureData_util.py @@ -16,6 +16,7 @@ def test_is_structure(generate_structure): + """Test if is structure can differentiate between structures, identifiers and else""" from aiida_fleur.tools.StructureData_util import is_structure from aiida.orm import Dict @@ -52,22 +53,38 @@ def test_is_primitive(generate_structure): def test_rescale_nowf(generate_structure): from aiida_fleur.tools.StructureData_util import rescale_nowf + from aiida_fleur.tools.StructureData_util import rescale + from aiida.orm import Dict, Float + structure = generate_structure() old_cell = np.array(structure.cell) rescaled = rescale_nowf(structure, 1.05) + rescaled2 = rescale(structure, Float(1.05)) rescaled_cell = np.array(rescaled.cell) + rescaled_cell2 = np.array(rescaled2.cell) assert (rescaled_cell == 1.05**(1 / 3.) * old_cell).all() + #assert (np.round(rescaled_cell2, 13) == 1.05**(1 / 3.) * old_cell).all() + # This does not work, seems to check if it is the same object, not if values are the same + # The precision between these functions is strangely different + assert list(np.round(rescaled_cell[0], 13)) == list(rescaled_cell2[0]) + assert list(np.round(rescaled_cell[1], 13)) == list(rescaled_cell2[1]) + assert list(np.round(rescaled_cell[2], 13)) == list(rescaled_cell2[2]) positions_old = [x.position for x in structure.sites] positions_rescaled = [x.position for x in rescaled.sites] for position in positions_old: assert tuple(pos * 1.05**(1 / 3.) for pos in position) in positions_rescaled + no_struc = Dict(dict={}) + no_rescaled = rescale_nowf(no_struc, 1.05) + assert no_rescaled is None + def test_supercell(generate_structure): from aiida_fleur.tools.StructureData_util import supercell + from aiida_fleur.tools.StructureData_util import supercell_ncf from aiida.orm import Int from itertools import product @@ -89,6 +106,10 @@ def test_supercell(generate_structure): z * np.array(structure.cell[2])) assert test_pos in positions_rescaled + no_struc = Int(1) + no_supercell = supercell_ncf(no_struc, 2, 3, 4) + assert no_supercell is None + def test_abs_to_rel(generate_structure): from aiida_fleur.tools.StructureData_util import abs_to_rel @@ -113,6 +134,7 @@ def test_abs_to_rel_f(generate_film_structure): def test_rel_to_abs(generate_structure): + """Test if rel_to_abs for bulk function scales coordinates right""" from aiida_fleur.tools.StructureData_util import rel_to_abs structure = generate_structure() @@ -124,6 +146,7 @@ def test_rel_to_abs(generate_structure): def test_rel_to_abs_f(generate_film_structure): + """Test if rel_to_abs film function scales coordinates right""" from aiida_fleur.tools.StructureData_util import rel_to_abs_f structure = generate_film_structure() @@ -135,10 +158,9 @@ def test_rel_to_abs_f(generate_film_structure): def test_break_symmetry_wf(generate_film_structure): - """ - Check if it does not crash and able to destroy all symmetries - """ + """Check if it does not crash and able to destroy all symmetries""" from aiida_fleur.tools.StructureData_util import break_symmetry_wf, supercell_ncf + from aiida_fleur.tools.StructureData_util import break_symmetry from aiida.orm import Dict structure = generate_film_structure() @@ -154,8 +176,49 @@ def test_break_symmetry_wf(generate_film_structure): for kind_name in ['Fe1', 'Fe1', 'Fe1', 'Fe1', 'Pt1', 'Pt2', 'Pt3', 'Pt4', 'Pt5', 'Pt6', 'Pt7', 'Pt8']: assert kind_name in kind_names + # Test if break symmetry adjusts the parameter data right. + should_out_dict = { + 'atom': { + 'id': 26, + 'rmt': 2.1, + 'bmu': -1 + }, + 'atom1': { + 'id': 78.1, + 'rmt': 2.1, + 'bmu': -1 + }, + 'atom2': { + 'id': 78.2, + 'rmt': 2.2, + 'bmu': 1 + } + } + parameter_data = Dict( + dict={ + 'atom': { + 'id': 26, + 'rmt': 2.1, + 'bmu': -1 + }, + 'atom1': { + 'id': 78.1, + 'rmt': 2.1, + 'bmu': -1 + }, + 'atom2': { + 'id': 78.2, + 'rmt': 2.2, + 'bmu': 1 + } + }) + out, parameterdata_new = break_symmetry(structure, parameterdata=parameter_data) + out_dict = parameterdata_new.get_dict() + assert out_dict == should_out_dict + def test_find_equi_atoms(generate_film_structure): + """Test if find_equi_atoms functions returns equidistant atoms""" from aiida_fleur.tools.StructureData_util import find_equi_atoms, supercell_ncf from numpy import array @@ -174,6 +237,7 @@ def test_find_equi_atoms(generate_film_structure): def test_get_spacegroup(generate_film_structure): + """Test if get_spacegroup function returns the right spacegroup""" from aiida_fleur.tools.StructureData_util import get_spacegroup structure = generate_film_structure() @@ -181,6 +245,7 @@ def test_get_spacegroup(generate_film_structure): def test_move_atoms_incell_wf(generate_structure): + """Test if move atoms incell functions moves atoms correctly""" from aiida_fleur.tools.StructureData_util import move_atoms_incell_wf from aiida.orm import Dict @@ -198,6 +263,7 @@ def test_move_atoms_incell_wf(generate_structure): def test_find_primitive_cell_wf(generate_structure): from aiida_fleur.tools.StructureData_util import find_primitive_cell_wf, supercell_ncf + from aiida_fleur.tools.StructureData_util import find_primitive_cells structure_primitive = generate_structure() structure = supercell_ncf(structure_primitive, 2, 2, 22) @@ -207,6 +273,10 @@ def test_find_primitive_cell_wf(generate_structure): assert all(x in structure_primitive.cell for x in result.cell) + resultlist = find_primitive_cells([structure.uuid, structure.uuid]) + for struc in resultlist: + assert all(x in structure_primitive.cell for x in result.cell) + def test_center_film_wf(generate_film_structure, generate_structure): from aiida_fleur.tools.StructureData_util import center_film_wf, move_atoms_incell, center_film @@ -346,6 +416,8 @@ def test_magnetic_slab_from_relaxed(generate_film_structure): def test_request_average_bond_length(generate_film_structure): + """Test interface of request average bond length from mp, requires mp_api_key""" + # Todo mock the mp query, since result could change overtime, also that the CI can run this import os from aiida_fleur.tools.StructureData_util import request_average_bond_length @@ -368,6 +440,8 @@ def test_request_average_bond_length(generate_film_structure): def test_adjust_film_relaxation(generate_film_structure): + """Test interface of adjust film relaxation, requires mp_api_key""" + # Todo mock the mp query, since result could change overtime, also that the CI can run this import os from aiida_fleur.tools.StructureData_util import adjust_film_relaxation @@ -398,3 +472,35 @@ def test_adjust_film_relaxation(generate_film_structure): assert result.sites[0].position[2] == -1.1709859694 assert result.sites[1].position[2] == 0.2602185234 assert result.sites[2].position[2] == 1.1709859694 + + +def test_create_slap(generate_structure): + """Test if create_slap""" + from aiida_fleur.tools.StructureData_util import create_slap + + structure = generate_structure() + film_struc = create_slap(structure, [1, 1, 1], 2) + cell_should = [[3.839589821842953, 0.0, 2.351070692679364e-16], + [1.9197949109214756, 3.3251823258281643, 2.351070692679364e-16], [0.0, 0.0, 9.405035885099004]] + sites_should = [(3.839589821842953, 2.216788217218776, 3.135011961699669), (0.0, 0.0, 0.0), + (1.9197949109214758, 1.1083941086093878, 6.270023923399337)] + + assert film_struc.cell == cell_should + assert film_struc.sites[0].position == sites_should[0] + assert film_struc.sites[1].position == sites_should[1] + assert film_struc.sites[2].position == sites_should[2] + + +def test_create_all_slabs(generate_structure): + """Test if create_all_slabs""" + from aiida_fleur.tools.StructureData_util import create_all_slabs + from aiida.orm import StructureData + + structure = generate_structure() + film_strucs = create_all_slabs(structure, 2, 5) + + assert len(film_strucs.keys()) == 9 + assert list(film_strucs.keys()) == [(1, 1, 1), (2, 2, 1), (1, 1, 0), (2, 2, -1), (2, 1, 1), (2, 1, -1), (2, 1, -2), + (2, 0, -1), (2, -1, -1)] + for key, film_struc in film_strucs.items(): + assert isinstance(film_struc, StructureData) diff --git a/tests/tools/test_common_aiida.py b/tests/tools/test_common_aiida.py index b23fa745f..8a62956a0 100644 --- a/tests/tools/test_common_aiida.py +++ b/tests/tools/test_common_aiida.py @@ -8,8 +8,8 @@ import pytest -def test_create_group(capsys): - 'Test group creation' +def test_create_group(capsys, clear_database): + """Test group creation""" from aiida_fleur.tools.common_aiida import create_group from aiida.orm import Group, Dict @@ -18,26 +18,29 @@ def test_create_group(capsys): group = create_group(name='test_group', nodes=[para.pk, 'not-existent-uuid'], description='test_description') captured = capsys.readouterr() + if '=4' in captured.out: + pk = 4 # when running all tests the import group counter is not reset... + else: + pk = 1 - assert captured.out == ('Group created with PK=1 and name test_group\n' + assert captured.out == (f'Group created with PK={pk} and name test_group\n' 'Skipping not-existent-uuid, it does not exist in the DB\n' - 'added nodes: [{}] to group test_group 1\n'.format(para.pk)) - + 'added nodes: [{para.pk}] to group test_group {pk}\n') para2 = para.clone() para2.store() group = create_group(name='test_group', nodes=[para2], add_if_exist=False) captured = capsys.readouterr() - assert captured.out == ('Group with name test_group and pk 1 already exists.\n' - 'Nodes were not added to the existing group test_group\n') + assert captured.out == ('Group with name test_group and pk {} already exists.\n' + 'Nodes were not added to the existing group test_group\n'.format(pk)) group = create_group(name='test_group', nodes=[para2], add_if_exist=True) captured = capsys.readouterr() - assert captured.out == ('Group with name test_group and pk 1 already exists.\n' + assert captured.out == (f'Group with name test_group and pk {pk} already exists.\n' 'Adding nodes to the existing group test_group\n' - 'added nodes: [{}] to group test_group 1\n'.format(para2.pk)) + 'added nodes: [{para2.pk}] to group test_group {pk}\n') assert isinstance(group, Group) diff --git a/tests/tools/test_graph_fleur.py b/tests/tools/test_graph_fleur.py deleted file mode 100644 index fc202c169..000000000 --- a/tests/tools/test_graph_fleur.py +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -''' Contains test the fleur specific graph gernation routine. ''' -from __future__ import absolute_import -import pytest - -# These tests need dot/graphviz... which is not autoinstalled in the python env... so far -# Therefore I uncomment these tests for know, because they will fail on travis. -# TODO find a way (aiidas problem) to set up a clean environment -''' -# test draw_graph -@pytest.mark.usefixtures("aiida_env") -def test_draw_graph_if_produces_file(): - """ - does the individual fleur_draw_graph routine produce a file? - """ - import os - from aiida_fleur.tools.graph_fleur import draw_graph - from aiida.orm import Node - - # TODO store a real graph and test if it is represented right... - node = Node() - outfile_expected = 'None.dot' - exit_expected = 0 - exit_status, output_file_name = draw_graph(node) - os.remove(output_file_name) - - assert exit_status == exit_expected - assert output_file_name == outfile_expected - -''' From c28ed7b6c63101669281471cdab86d410d38bae9 Mon Sep 17 00:00:00 2001 From: broeder-j Date: Fri, 27 Nov 2020 13:55:10 +0100 Subject: [PATCH 19/53] Enforce consistent units, bohr_a with fleur --- CHANGELOG.md | 7 ++++++ aiida_fleur/__init__.py | 2 +- aiida_fleur/calculation/fleurinputgen.py | 4 ++-- aiida_fleur/common/constants.py | 14 ++++++++++-- aiida_fleur/data/fleurinp.py | 2 +- aiida_fleur/parsers/fleur.py | 5 ++-- aiida_fleur/tools/xml_util.py | 11 +++++---- aiida_fleur/workflows/dmi.py | 5 ++-- aiida_fleur/workflows/eos.py | 4 ++-- aiida_fleur/workflows/mae.py | 4 ++-- aiida_fleur/workflows/mae_conv.py | 4 ++-- aiida_fleur/workflows/relax.py | 6 ++--- aiida_fleur/workflows/ssdisp.py | 5 ++-- setup.json | 2 +- tests/calculation/test_inpgen.py | 12 +++++----- tests/conftest.py | 9 ++++---- tests/parsers/test_fleur_parser.py | 2 +- tests/tools/test_StructureData_util.py | 29 ++++++++++++------------ tests/tools/test_common_aiida.py | 4 ++-- 19 files changed, 74 insertions(+), 57 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58e0f27ae..f7e08b438 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,11 @@ +## v1.1.3 +### release compatible with AiiDA-core 1.3.0 +- Introduced common constants, for bohr and htr, increased precision +- Command line interface (CLI) `aiida-fleur` with various functionalities exposed +- For devs: Increased test coverage + + ## v1.1.2 ### release compatible with AiiDA-core 1.3.0 - still support of Fleur MaX4 version (release branch) with inpgen diff --git a/aiida_fleur/__init__.py b/aiida_fleur/__init__.py index 521641094..41430d285 100644 --- a/aiida_fleur/__init__.py +++ b/aiida_fleur/__init__.py @@ -12,4 +12,4 @@ ''' AiiDA-FLEUR ''' -__version__ = '1.1.2' +__version__ = '1.1.3' diff --git a/aiida_fleur/calculation/fleurinputgen.py b/aiida_fleur/calculation/fleurinputgen.py index 4fbb5758d..e3a2d5f40 100644 --- a/aiida_fleur/calculation/fleurinputgen.py +++ b/aiida_fleur/calculation/fleurinputgen.py @@ -28,7 +28,7 @@ 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 aiida_fleur.common.constants import BOHR_A class FleurinputgenCalculation(CalcJob): @@ -170,7 +170,7 @@ def prepare_for_submission(self, folder): # but we have to convert from Angstrom to a.u (bohr radii) scaling_factors = [1.0, 1.0, 1.0] scaling_lat = 1. # /bohr_to_ang = 0.52917720859 - scaling_pos = 1. / bohr_a # Angstrom to atomic + scaling_pos = 1. / BOHR_A # Angstrom to atomic own_lattice = False # not self._use_aiida_structure ########################################## diff --git a/aiida_fleur/common/constants.py b/aiida_fleur/common/constants.py index 0a474ee13..de18319ea 100644 --- a/aiida_fleur/common/constants.py +++ b/aiida_fleur/common/constants.py @@ -13,6 +13,16 @@ Here we collect physical constants which are used throughout the code that way we ensure consitency ''' +# at some point one should import these from masci-tools to ensure, +# that all judft plugins and tools take the same values, to make roundtrips consistent +# NIST https://physics.nist.gov/cgi-bin/cuu/Value?hrev +HTR_TO_EV = 27.211386245988 #(53) +BOHR_A = 0.5291772108 -htr_to_ev = 27.21138602 -bohr_a = 0.52917720903 +# NIST BOHR 0.529177210903 #(80) +#https://physics.nist.gov/cgi-bin/cuu/Value?bohrrada0 + +#Fleur +#htr_eV = 27.21138386 +#bohr=0.5291772108 +#bohrtocm=0.529177e-8 diff --git a/aiida_fleur/data/fleurinp.py b/aiida_fleur/data/fleurinp.py index f92858692..627b563da 100644 --- a/aiida_fleur/data/fleurinp.py +++ b/aiida_fleur/data/fleurinp.py @@ -35,7 +35,7 @@ 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 as BOHR_A +from aiida_fleur.common.constants import BOHR_A class FleurinpData(Data): diff --git a/aiida_fleur/parsers/fleur.py b/aiida_fleur/parsers/fleur.py index e408558f5..d8af31cbf 100644 --- a/aiida_fleur/parsers/fleur.py +++ b/aiida_fleur/parsers/fleur.py @@ -28,6 +28,7 @@ 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 class FleurParser(Parser): @@ -644,11 +645,11 @@ def convert_htr_to_ev(value): """ Multiplies the value given with the Hartree factor (converts htr to eV) """ - htr = 27.21138602 + #htr = 27.21138602 suc = False value_to_save, suc = convert_to_float(value) if suc: - return value_to_save * htr + return value_to_save * HTR_TO_EV else: return value diff --git a/aiida_fleur/tools/xml_util.py b/aiida_fleur/tools/xml_util.py index 994302770..4841950f8 100644 --- a/aiida_fleur/tools/xml_util.py +++ b/aiida_fleur/tools/xml_util.py @@ -110,13 +110,15 @@ 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': []} - htr = 27.21138602 + suc = False value_to_save, suc = convert_to_float(value, parser_info_out=parser_info_out) if suc: - return value_to_save * htr + return value_to_save * HTR_TO_EV else: return value @@ -125,13 +127,14 @@ 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': []} - htr = 27.21138602 suc = False value_to_save, suc = convert_to_float(value, parser_info_out=parser_info_out) if suc: - return value_to_save / htr + return value_to_save / HTR_TO_EV else: return value diff --git a/aiida_fleur/workflows/dmi.py b/aiida_fleur/workflows/dmi.py index 4dc7d1448..c7d24a086 100644 --- a/aiida_fleur/workflows/dmi.py +++ b/aiida_fleur/workflows/dmi.py @@ -35,7 +35,7 @@ from aiida_fleur.workflows.scf import FleurScfWorkChain 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 @@ -493,7 +493,6 @@ def get_results(self): """ Generates results of the workchain. """ - htr_to_ev = 27.21138602 t_energydict = [] mae_thetas = [] mae_phis = [] @@ -531,7 +530,7 @@ def get_results(self): if e_u == 'Htr' or 'htr': for labels, energies in t_energydict.items(): - t_energydict[labels] = energies * htr_to_ev + t_energydict[labels] = energies * HTR_TO_EV except AttributeError: message = ('Did not manage to read evSum or energy units after FT calculation.') self.control_end_wc(message) diff --git a/aiida_fleur/workflows/eos.py b/aiida_fleur/workflows/eos.py index fa97dbfe0..b663ea196 100644 --- a/aiida_fleur/workflows/eos.py +++ b/aiida_fleur/workflows/eos.py @@ -32,6 +32,7 @@ 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_util import check_eos_energies +from aiida_fleur.common.constants import HTR_TO_EV class FleurEosWorkChain(WorkChain): @@ -190,7 +191,6 @@ def return_results(self): vol_peratom_success = [] outnodedict = {} natoms = len(self.inputs.structure.sites) - htr_to_ev = 27.21138602 e_u = 'eV' dis_u = 'me/bohr^3' for label in self.ctx.labels: @@ -220,7 +220,7 @@ def return_results(self): t_e = outpara.get('total_energy', float('nan')) e_u = outpara.get('total_energy_units', 'eV') if e_u == 'Htr' or 'htr': - t_e = t_e * htr_to_ev + t_e = t_e * HTR_TO_EV dis = outpara.get('distance_charge', float('nan')) dis_u = outpara.get('distance_charge_units', 'me/bohr^3') t_energylist.append(t_e) diff --git a/aiida_fleur/workflows/mae.py b/aiida_fleur/workflows/mae.py index e73c96f65..58d0c3880 100644 --- a/aiida_fleur/workflows/mae.py +++ b/aiida_fleur/workflows/mae.py @@ -33,6 +33,7 @@ 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.common.constants import HTR_TO_EV class FleurMaeWorkChain(WorkChain): @@ -449,7 +450,6 @@ def get_results(self): t_energydict = [] mae_thetas = [] mae_phis = [] - htr_to_ev = 27.21138602 fleur_output_uuid = None try: @@ -476,7 +476,7 @@ def get_results(self): minenergy = min(t_energydict) if e_u == 'Htr' or 'htr': - t_energydict = [htr_to_ev * (x - minenergy) for x in t_energydict] + t_energydict = [HTR_TO_EV * (x - minenergy) for x in t_energydict] else: t_energydict = [(x - minenergy) for x in t_energydict] diff --git a/aiida_fleur/workflows/mae_conv.py b/aiida_fleur/workflows/mae_conv.py index 6304b952d..fb79b12d3 100644 --- a/aiida_fleur/workflows/mae_conv.py +++ b/aiida_fleur/workflows/mae_conv.py @@ -24,6 +24,7 @@ from aiida.common import AttributeDict from aiida_fleur.workflows.scf import FleurScfWorkChain +from aiida_fleur.common.constants import HTR_TO_EV class FleurMaeConvWorkChain(WorkChain): @@ -146,7 +147,6 @@ def get_results(self): t_energydict = {} original_t_energydict = {} outnodedict = {} - htr_to_ev = 27.21138602 for label in six.iterkeys(self.ctx.wf_dict['sqas']): calc = self.ctx[label] @@ -172,7 +172,7 @@ def get_results(self): continue e_u = outpara.get('total_energy_units', 'Htr') if e_u == 'Htr' or 'htr': - t_e = t_e * htr_to_ev + t_e = t_e * HTR_TO_EV t_energydict[label] = t_e if t_energydict: diff --git a/aiida_fleur/workflows/relax.py b/aiida_fleur/workflows/relax.py index ea81d67ec..9cdd7f940 100644 --- a/aiida_fleur/workflows/relax.py +++ b/aiida_fleur/workflows/relax.py @@ -27,7 +27,7 @@ from aiida_fleur.workflows.scf import FleurScfWorkChain from aiida_fleur.calculation.fleur import FleurCalculation as FleurCalc -from aiida_fleur.common.constants import bohr_a +from aiida_fleur.common.constants import BOHR_A from aiida_fleur.tools.StructureData_util import break_symmetry_wf @@ -461,7 +461,7 @@ def get_results_relax(self): # we build the structure here, that way we can run an scf afterwards if self.ctx.final_cell: - np_cell = np.array(self.ctx.final_cell) * bohr_a + np_cell = np.array(self.ctx.final_cell) * BOHR_A structure = StructureData(cell=np_cell.tolist()) for atom in self.ctx.final_atom_positions: @@ -470,7 +470,7 @@ def get_results_relax(self): if self.ctx.pbc == (True, True, True): structure.append_atom(position=(pos_abs[0], pos_abs[1], pos_abs[2]), symbols=atom[0]) else: # assume z-direction is orthogonal to xy - structure.append_atom(position=(pos_abs[0], pos_abs[1], atom[3] * bohr_a), symbols=atom[0]) + structure.append_atom(position=(pos_abs[0], pos_abs[1], atom[3] * BOHR_A), symbols=atom[0]) structure.pbc = self.ctx.pbc self.ctx.final_structure = structure diff --git a/aiida_fleur/workflows/ssdisp.py b/aiida_fleur/workflows/ssdisp.py index a5ec7fd46..840c4f219 100644 --- a/aiida_fleur/workflows/ssdisp.py +++ b/aiida_fleur/workflows/ssdisp.py @@ -33,7 +33,7 @@ from aiida_fleur.workflows.scf import FleurScfWorkChain 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 @@ -454,7 +454,6 @@ def get_results(self): Generates results of the workchain. """ t_energydict = [] - htr_to_ev = 27.21138602 try: calculation = self.ctx.f_t if not calculation.is_finished_ok: @@ -476,7 +475,7 @@ def get_results(self): minenergy = min(t_energydict) if e_u == 'Htr' or 'htr': - t_energydict = [htr_to_ev * (x - minenergy) for x in t_energydict] + t_energydict = [HTR_TO_EV * (x - minenergy) for x in t_energydict] else: t_energydict = [(x - minenergy) for x in t_energydict] diff --git a/setup.json b/setup.json index 5efcfd6bd..33012f06a 100644 --- a/setup.json +++ b/setup.json @@ -1,5 +1,5 @@ { - "version": "1.1.2", + "version": "1.1.3", "name": "aiida-fleur", "url": "https://github.com/JuDFTteam/aiida-fleur", "license": "MIT License, see LICENSE.txt file.", diff --git a/tests/calculation/test_inpgen.py b/tests/calculation/test_inpgen.py index 8946ba7aa..e7a27fc40 100644 --- a/tests/calculation/test_inpgen.py +++ b/tests/calculation/test_inpgen.py @@ -54,9 +54,9 @@ def test_fleurinpgen_default_calcinfo(aiida_profile, fixture_sandbox, generate_c input_written = handle.read() aiida_in_text = """A Fleur input generator calculation with aiida\n&input cartesian=F / - 0.000000000 5.130606447 5.130606447 - 5.130606447 0.000000000 5.130606447 - 5.130606447 5.130606447 0.000000000 + 0.000000000 5.130606429 5.130606429 + 5.130606429 0.000000000 5.130606429 + 5.130606429 5.130606429 0.000000000 1.0000000000 1.000000000 1.000000000 1.000000000 @@ -117,9 +117,9 @@ def test_fleurinpgen_with_parameters(aiida_profile, fixture_sandbox, generate_ca input_written = handle.read() aiida_in_text = """A Fleur input generator calculation with aiida\n&input cartesian=F / - 0.000000000 5.130606447 5.130606447 - 5.130606447 0.000000000 5.130606447 - 5.130606447 5.130606447 0.000000000 + 0.000000000 5.130606429 5.130606429 + 5.130606429 0.000000000 5.130606429 + 5.130606429 5.130606429 0.000000000 1.0000000000 1.000000000 1.000000000 1.000000000 diff --git a/tests/conftest.py b/tests/conftest.py index d98cb10ba..9cdef667f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -321,14 +321,13 @@ def generate_film_structure(): def _generate_film_structure(): """Return a `StructureData` representing bulk silicon.""" from aiida.orm import StructureData - - bohr_a_0 = 0.52917721092 # A - a = 7.497 * bohr_a_0 + from aiida_fleur.common.constants import BOHR_A + a = 7.497 * BOHR_A cell = [[0.7071068 * a, 0.0, 0.0], [0.0, 1.0 * a, 0.0], [0.0, 0.0, 0.7071068 * a]] structure = StructureData(cell=cell) - structure.append_atom(position=(0., 0., -1.99285 * bohr_a_0), symbols='Fe') + structure.append_atom(position=(0., 0., -1.99285 * BOHR_A), symbols='Fe') structure.append_atom(position=(0.5 * 0.7071068 * a, 0.5 * a, 0.0), symbols='Pt') - structure.append_atom(position=(0., 0., 2.65059 * bohr_a_0), symbols='Pt') + structure.append_atom(position=(0., 0., 2.65059 * BOHR_A), symbols='Pt') structure.pbc = (True, True, False) return structure diff --git a/tests/parsers/test_fleur_parser.py b/tests/parsers/test_fleur_parser.py index b7737651e..813c20266 100644 --- a/tests/parsers/test_fleur_parser.py +++ b/tests/parsers/test_fleur_parser.py @@ -28,7 +28,7 @@ def test_parse_xmlout_file(): 'creator_target_architecture': 'GEN', 'creator_target_structure': ' ', 'density_convergence_units': 'me/bohr^3', - 'energy': -23635.691764717936, + 'energy': -23635.691961010132, 'energy_core_electrons': -496.172547773, 'energy_hartree': -868.5956587197, 'energy_hartree_units': 'Htr', diff --git a/tests/tools/test_StructureData_util.py b/tests/tools/test_StructureData_util.py index b7f1a6050..3b784736f 100644 --- a/tests/tools/test_StructureData_util.py +++ b/tests/tools/test_StructureData_util.py @@ -287,9 +287,9 @@ def test_center_film_wf(generate_film_structure, generate_structure): structure_film = move_atoms_incell(structure_film, [0.0, 0.0, 1.1242]) centered_film = center_film_wf(structure_film) - assert [x.position for x in centered_film.sites] == [(0.0, 0.0, -1.2286013142), - (1.4026317387, 1.9836207751, -0.1740305093), - (0.0, 0.0, 1.2286013141)] + assert [x.position for x in centered_film.sites] == [(0.0, 0.0, -1.2286013139), + (1.4026317384, 1.9836207747, -0.1740305094), + (0.0, 0.0, 1.2286013138)] with pytest.raises(TypeError): center_film(structure_bulk) @@ -297,20 +297,19 @@ def test_center_film_wf(generate_film_structure, generate_structure): def test_get_layers(generate_film_structure): from aiida_fleur.tools.StructureData_util import get_layers - + from aiida_fleur.common.constants import BOHR_A structure = generate_film_structure() - assert get_layers(structure) == ([[([0.0, 0.0, -1.054570804781922], 'Fe')], - [([1.4026317387182539, 1.9836207751336201, 0.0], 'Pt')], - [([0.0, 0.0, 1.4026318234924429], 'Pt')]], [-1.0545708048, 0.0, - 1.4026318235], [1, 1, 1]) - - bohr_a_0 = 0.52917721092 - structure.append_atom(position=(1.0, 0., -1.99285 * bohr_a_0), symbols='Fe') - assert get_layers(structure) == ([[([0.0, 0.0, -1.054570804781922], 'Fe'), ([1.0, 0.0, -1.054570804781922], 'Fe')], - [([1.4026317387182539, 1.9836207751336201, 0.0], 'Pt')], - [([0.0, 0.0, 1.4026318234924429], 'Pt')]], [-1.0545708048, 0.0, - 1.4026318235], [2, 1, 1]) + assert get_layers(structure) == ([[([0.0, 0.0, -1.05457080454278], 'Fe')], + [([1.402631738400183, 1.9836207746838, 0.0], 'Pt')], + [([0.0, 0.0, 1.402631823174372], 'Pt')]], [-1.0545708045, 0.0, + 1.4026318232], [1, 1, 1]) + + structure.append_atom(position=(1.0, 0., -1.99285 * BOHR_A), symbols='Fe') + assert get_layers(structure) == ([[([0.0, 0.0, -1.05457080454278], 'Fe'), ([1.0, 0.0, -1.05457080454278], 'Fe')], + [([1.402631738400183, 1.9836207746838, 0.0], 'Pt')], + [([0.0, 0.0, 1.402631823174372], 'Pt')]], [-1.0545708045, 0.0, + 1.4026318232], [2, 1, 1]) create_slab_inputs = [{ diff --git a/tests/tools/test_common_aiida.py b/tests/tools/test_common_aiida.py index 8a62956a0..12cf29423 100644 --- a/tests/tools/test_common_aiida.py +++ b/tests/tools/test_common_aiida.py @@ -25,7 +25,7 @@ def test_create_group(capsys, clear_database): assert captured.out == (f'Group created with PK={pk} and name test_group\n' 'Skipping not-existent-uuid, it does not exist in the DB\n' - 'added nodes: [{para.pk}] to group test_group {pk}\n') + f'added nodes: [{para.pk}] to group test_group {pk}\n') para2 = para.clone() para2.store() group = create_group(name='test_group', nodes=[para2], add_if_exist=False) @@ -40,7 +40,7 @@ def test_create_group(capsys, clear_database): assert captured.out == (f'Group with name test_group and pk {pk} already exists.\n' 'Adding nodes to the existing group test_group\n' - 'added nodes: [{para2.pk}] to group test_group {pk}\n') + f'added nodes: [{para2.pk}] to group test_group {pk}\n') assert isinstance(group, Group) From 1ec52b6436c479a240a036eb9c46e9c9946c78a0 Mon Sep 17 00:00:00 2001 From: broeder-j Date: Tue, 1 Dec 2020 10:20:02 +0100 Subject: [PATCH 20/53] Parse total magnetic moment of the cell now and add to scf and relax wc outdicts. --- aiida_fleur/cmdline/util/options.py | 5 ++ aiida_fleur/cmdline/workflows/__init__.py | 51 ++++++++++++++---- aiida_fleur/common/constants.py | 15 ++++-- aiida_fleur/parsers/fleur.py | 57 ++++++++++++--------- aiida_fleur/tools/StructureData_util.py | 3 +- aiida_fleur/workflows/relax.py | 12 ++++- tests/cmdline/conftest.py | 34 ++++++++++++ tests/cmdline/workflows/test_worklow_cmd.py | 14 ++--- 8 files changed, 145 insertions(+), 46 deletions(-) diff --git a/aiida_fleur/cmdline/util/options.py b/aiida_fleur/cmdline/util/options.py index 25968f419..a498a38cc 100755 --- a/aiida_fleur/cmdline/util/options.py +++ b/aiida_fleur/cmdline/util/options.py @@ -145,3 +145,8 @@ default=False, show_default=True, help='Clean the remote folder of all the launched calculations after completion of the workchain.') + +SHOW = OverridableOption('--show/--no-show', + default=True, + show_default=True, + help='Show the main output of the command.') diff --git a/aiida_fleur/cmdline/workflows/__init__.py b/aiida_fleur/cmdline/workflows/__init__.py index eb0802bb9..1c8925a47 100755 --- a/aiida_fleur/cmdline/workflows/__init__.py +++ b/aiida_fleur/cmdline/workflows/__init__.py @@ -16,6 +16,8 @@ from aiida.cmdline.params.types import ProcessParamType from aiida.cmdline.params import arguments, options from aiida.cmdline.utils import decorators, echo +from aiida import orm +from aiida_fleur.cmdline.util import options as options_fl @click.group('workflow') @@ -31,13 +33,12 @@ def cmd_workflow(): ) #, type=WorkflowParamType(sub_classes=('aiida.node:process.workflow.workchain',))) @click.option('--info/--no-info', default=False, help='Print an info header above each node.') @click.option('-l', '--label', 'label', type=str, help='Print only output dicts with a certain link_label.') +@options_fl.SHOW() @options.DICT_KEYS() @options.DICT_FORMAT() @decorators.with_dbenv() -def workchain_res(process, fmt, keys, label, info): +def workchain_res(process, info, label, show, keys, fmt): """Print data from Dict nodes returned or created by any fleur process.""" - #from aiida.cmdline.utils.echo import echo_dictionary - from aiida.orm import Dict returned_dicts_info = [] returned_dicts = [] @@ -46,7 +47,7 @@ def workchain_res(process, fmt, keys, label, info): except ValueError as exception: echo.echo_critical(str(exception)) for result in results: - if isinstance(result.node, Dict): + if isinstance(result.node, orm.Dict): if label is not None: if label == result.link_label: returned_dicts.append(result.node.get_dict()) @@ -73,12 +74,12 @@ def workchain_res(process, fmt, keys, label, info): ) #, type=WorkflowParamType(sub_classes=('aiida.node:process.workflow.workchain',))) @click.option('--info/--no-info', default=False, help='Print an info header above each node.') @click.option('-l', '--label', 'label', type=str, help='Print only output dicts with a certain link_label.') +@options_fl.SHOW() @options.DICT_KEYS() @options.DICT_FORMAT() @decorators.with_dbenv() -def workchain_inputdict(process, fmt, keys, label, info): - """Print data from Dict nodes inputed into any fleur process.""" - #from aiida.cmdline.utils.echo import echo_dictionary +def workchain_inputdict(process, info, label, show, keys, fmt): + """Print data from Dict nodes input into any fleur process.""" from aiida.orm import Dict returned_dicts_info = [] @@ -88,7 +89,7 @@ def workchain_inputdict(process, fmt, keys, label, info): except ValueError as exception: echo.echo_critical(str(exception)) for result in results: - if isinstance(result.node, Dict): + if isinstance(result.node, orm.Dict): if label is not None: if label == result.link_label: returned_dicts.append(result.node.get_dict()) @@ -107,10 +108,42 @@ def workchain_inputdict(process, fmt, keys, label, info): result = re_dict if info: echo.echo('# Info: {} {} dict:'.format(returned_dicts_info[i].link_label, returned_dicts_info[i].node)) - echo.echo_dictionary(result, fmt=fmt) + if show: + echo.echo_dictionary(result, fmt=fmt) ''' +@cmd_workflow.command('gen_wf_para') +@arguments.ENTRYPOINTSTR('entrypoint', default='fleur.scf') +@options.SHOW() +@options.STORE() +@options.CHECK_EXISTENCE() +@options.KWARGS() +@decorators.with_dbenv() +def gen_wf_para_cmd(entrypoint, show, store, check_existence, kwargs): + """ + Generates a default parameter wf parameter node for given entrypoint. + """ + from aiida_fleur.tools.node_generators import generate_wf_para_node + from aiida.plugins import entry_point + try: + wf = entry_point.load_entry_point('aiida.workflows', prefix) + except ValueError: + echo.echo('here1') + try: + wf = entry_point.load_entry_point('aiida.calculations', prefix) + except ValueError: + echo.echo('here1') + + wf_para = generate_wf_para_node(entrypoint=entrypoint, **kwargs) + if store: + wf_para.store() + echo.echo('Created) + else: + echo.echo('Created) + if show: + echo.echo_dictionary(wf.para.get_dict()) + @cmd_workflow.command('inputls') def inputls_wc(): """ diff --git a/aiida_fleur/common/constants.py b/aiida_fleur/common/constants.py index de18319ea..26e135419 100644 --- a/aiida_fleur/common/constants.py +++ b/aiida_fleur/common/constants.py @@ -13,16 +13,25 @@ Here we collect physical constants which are used throughout the code that way we ensure consitency ''' -# at some point one should import these from masci-tools to ensure, +# at some point one should import these from masci-tools, or better scipy or ase to ensure, # that all judft plugins and tools take the same values, to make roundtrips consistent # NIST https://physics.nist.gov/cgi-bin/cuu/Value?hrev HTR_TO_EV = 27.211386245988 #(53) BOHR_A = 0.5291772108 - +#Scipy bohr 5.29177210903e-11 m +#Scipy htr 27.211386245988 eV # NIST BOHR 0.529177210903 #(80) #https://physics.nist.gov/cgi-bin/cuu/Value?bohrrada0 #Fleur -#htr_eV = 27.21138386 +#htr_eV = 27.21138602 #bohr=0.5291772108 #bohrtocm=0.529177e-8 +#pymatgen uses scipy.constants +#ase: Bohr 0.5291772105638411 +#Hartree 27.211386024367243 +#Rydberg 13.605693012183622 +#1/Bohr +#1.8897261258369282 +#aiida-core units: +#bohr_to_ang = 0.52917720859 diff --git a/aiida_fleur/parsers/fleur.py b/aiida_fleur/parsers/fleur.py index d8af31cbf..b5f90934d 100644 --- a/aiida_fleur/parsers/fleur.py +++ b/aiida_fleur/parsers/fleur.py @@ -22,6 +22,7 @@ import os import re import json +import numpy as np from datetime import date from lxml import etree @@ -140,7 +141,8 @@ def parse(self, **kwargs): mpiprocs = self.node.get_attribute('resources').get('num_mpiprocs_per_machine', 1) kb_used = 0.0 - with output_folder.open('out.xml', 'r') as out_file: # lazy out.xml parsing + with output_folder.open(FleurCalculation._OUTXML_FILE_NAME, + 'r') as out_file: # lazy out.xml parsing outlines = out_file.read() try: line_avail = re.findall(r' Date: Tue, 1 Dec 2020 10:45:45 +0100 Subject: [PATCH 21/53] Update parser version in tests --- tests/parsers/test_fleur_parser.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/parsers/test_fleur_parser.py b/tests/parsers/test_fleur_parser.py index 813c20266..58b584c4f 100644 --- a/tests/parsers/test_fleur_parser.py +++ b/tests/parsers/test_fleur_parser.py @@ -67,7 +67,7 @@ def test_parse_xmlout_file(): } } - expected_parser_info_out = {'parser_info': 'AiiDA Fleur Parser v0.3.0', 'parser_warnings': [], 'unparsed': []} + expected_parser_info_out = {'parser_info': 'AiiDA Fleur Parser v0.3.1', 'parser_warnings': [], 'unparsed': []} simple_out.pop('outputfile_path', None) # otherwise test will fail on different installations # also this should go away any way... @@ -93,7 +93,7 @@ def test_parse_xmlout_file_broken_xmlout_file(): 'last_iteration_parsed': 15, 'parser_info': - 'AiiDA Fleur Parser v0.3.0', + 'AiiDA Fleur Parser v0.3.1', '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!' @@ -123,7 +123,7 @@ def test_parse_xmlout_file_broken_first_xmlout_file(): 'last_iteration_parsed': 1, 'parser_info': - 'AiiDA Fleur Parser v0.3.0', + 'AiiDA Fleur Parser v0.3.1', '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.', @@ -212,7 +212,7 @@ def test_parse_xmlout_file_fortran_garbage_in_xmlout_file(): expected_parser_info_out = { 'parser_info': - 'AiiDA Fleur Parser v0.3.0', + 'AiiDA Fleur Parser v0.3.1', 'parser_warnings': [ 'Could not convert: "**" to float, ValueError', 'Could not convert: " !#@)!(U$*(Y" to float, ValueError' @@ -252,7 +252,7 @@ def test_parse_xmlout_file_empty_file(): expected_parser_info_out = { 'parser_info': - 'AiiDA Fleur Parser v0.3.0', + 'AiiDA Fleur Parser v0.3.1', 'parser_warnings': [ 'The out.xml file is broken I try to repair it.', 'Skipping the parsing of the xml file. Repairing was not possible.' From 5269b2f3c11017cf584d59e770b6bdffe9c102be Mon Sep 17 00:00:00 2001 From: broeder-j Date: Tue, 1 Dec 2020 13:01:06 +0100 Subject: [PATCH 22/53] Bugfix mag mom parser xpath --- aiida_fleur/parsers/fleur.py | 8 ++++---- aiida_fleur/workflows/relax.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/aiida_fleur/parsers/fleur.py b/aiida_fleur/parsers/fleur.py index b5f90934d..6d2b67ada 100644 --- a/aiida_fleur/parsers/fleur.py +++ b/aiida_fleur/parsers/fleur.py @@ -716,10 +716,10 @@ def parse_simple_outnode(iteration_node, fleurmode): 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' + 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' diff --git a/aiida_fleur/workflows/relax.py b/aiida_fleur/workflows/relax.py index cfbe37035..0c5954c06 100644 --- a/aiida_fleur/workflows/relax.py +++ b/aiida_fleur/workflows/relax.py @@ -500,9 +500,9 @@ def get_results_final_scf(self): #if jspin ==2 try: total_mag = scf_out_d['total_magnetic_moment_cell'] + self.ctx.total_magnetic_moment = total_mag except KeyError: self.report('ERROR: Could not parse total magnetic moment cell of final scf run') - self.ctx.total_magnetic_moment = total_mag def return_results(self): """ From 8ef374cf6cd2ea2100a4b178dcc32473c8838f03 Mon Sep 17 00:00:00 2001 From: broeder-j Date: Tue, 1 Dec 2020 13:40:57 +0100 Subject: [PATCH 23/53] Fix fleur parser, from previous commits --- aiida_fleur/parsers/fleur.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/aiida_fleur/parsers/fleur.py b/aiida_fleur/parsers/fleur.py index 6d2b67ada..87b561365 100644 --- a/aiida_fleur/parsers/fleur.py +++ b/aiida_fleur/parsers/fleur.py @@ -718,7 +718,7 @@ def parse_simple_outnode(iteration_node, fleurmode): # 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_spin_charges_mt_spheres_xpath = 'allElectronCharges/spinDependentCharge/@mtSpheres' all_total_charge_xpath = 'allElectronCharges/totalCharge/@value' # energy @@ -969,21 +969,23 @@ def write_simple_outnode(value, value_type, value_name, dict_out): # Total charges, total magentic moment - total_c = eval_xpath(iteration_node, all_spin_charges_total_xpath) + total_c = eval_xpath2(iteration_node, all_spin_charges_total_xpath) write_simple_outnode(total_c, 'list_floats', 'spind_dependent_charge_total', simple_data) total_magentic_moment_cell = None if len(total_c) == 2: - total_magentic_moment_cell = np.abs(total_c[0] - total_c[1]) + 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_magentic_moment_cell', simple_data) - total_c_i = eval_xpath(iteration_node, all_spin_charges_interstitial_xpath) + total_c_i = eval_xpath2(iteration_node, all_spin_charges_interstitial_xpath) write_simple_outnode(total_c_i, 'list_floats', 'spind_dependent_charge_intersitial', simple_data) - total_c_mt = eval_xpath(iteration_node, all_spin_charges_mt_spheres_xpath) + total_c_mt = eval_xpath2(iteration_node, all_spin_charges_mt_spheres_xpath) write_simple_outnode(total_c_i, 'list_floats', 'spind_dependent_charge_mt', simple_data) - total_c = eval_xpath(iteration_node, all_total_charge_xpath) + total_c = eval_xpath2(iteration_node, all_total_charge_xpath) write_simple_outnode(total_c, 'float', 'total_charge', simple_data) # orbital magnetic moments From d2db1a53884dac7400b8297170f5b6e216afc3e9 Mon Sep 17 00:00:00 2001 From: broeder-j Date: Tue, 1 Dec 2020 21:00:17 +0100 Subject: [PATCH 24/53] Again fleur parser bugfix... --- aiida_fleur/parsers/fleur.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aiida_fleur/parsers/fleur.py b/aiida_fleur/parsers/fleur.py index 87b561365..1ca6827a5 100644 --- a/aiida_fleur/parsers/fleur.py +++ b/aiida_fleur/parsers/fleur.py @@ -983,9 +983,9 @@ def write_simple_outnode(value, value_type, value_name, dict_out): write_simple_outnode(total_c_i, 'list_floats', 'spind_dependent_charge_intersitial', simple_data) total_c_mt = eval_xpath2(iteration_node, all_spin_charges_mt_spheres_xpath) - write_simple_outnode(total_c_i, 'list_floats', 'spind_dependent_charge_mt', simple_data) + write_simple_outnode(total_c_mt, 'list_floats', 'spind_dependent_charge_mt', simple_data) - total_c = eval_xpath2(iteration_node, all_total_charge_xpath) + total_c = eval_xpath(iteration_node, all_total_charge_xpath) write_simple_outnode(total_c, 'float', 'total_charge', simple_data) # orbital magnetic moments From 3a2944240662dcb02b7e578d665ff8d05d80a02d Mon Sep 17 00:00:00 2001 From: broeder-j Date: Tue, 1 Dec 2020 21:05:50 +0100 Subject: [PATCH 25/53] Tipo correction --- aiida_fleur/parsers/fleur.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aiida_fleur/parsers/fleur.py b/aiida_fleur/parsers/fleur.py index 1ca6827a5..60ce80058 100644 --- a/aiida_fleur/parsers/fleur.py +++ b/aiida_fleur/parsers/fleur.py @@ -980,10 +980,10 @@ def write_simple_outnode(value, value_type, value_name, dict_out): write_simple_outnode(total_magentic_moment_cell, 'float', 'total_magentic_moment_cell', simple_data) total_c_i = eval_xpath2(iteration_node, all_spin_charges_interstitial_xpath) - write_simple_outnode(total_c_i, 'list_floats', 'spind_dependent_charge_intersitial', simple_data) + write_simple_outnode(total_c_i, 'list_floats', 'spin_dependent_charge_intersitial', simple_data) total_c_mt = eval_xpath2(iteration_node, all_spin_charges_mt_spheres_xpath) - write_simple_outnode(total_c_mt, 'list_floats', 'spind_dependent_charge_mt', simple_data) + 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) From d900701261d1cb46e02b52c5f9dc7b7070cb50f6 Mon Sep 17 00:00:00 2001 From: broeder-j Date: Tue, 1 Dec 2020 21:30:32 +0100 Subject: [PATCH 26/53] Further tipos --- aiida_fleur/parsers/fleur.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aiida_fleur/parsers/fleur.py b/aiida_fleur/parsers/fleur.py index 60ce80058..f632ddcc4 100644 --- a/aiida_fleur/parsers/fleur.py +++ b/aiida_fleur/parsers/fleur.py @@ -970,14 +970,14 @@ def write_simple_outnode(value, value_type, value_name, dict_out): # Total charges, total magentic moment total_c = eval_xpath2(iteration_node, all_spin_charges_total_xpath) - write_simple_outnode(total_c, 'list_floats', 'spind_dependent_charge_total', simple_data) + 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_magentic_moment_cell', simple_data) + 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_intersitial', simple_data) From 2049dfb442a8e658b82590e899af4cc86ce7dadf Mon Sep 17 00:00:00 2001 From: broeder-j Date: Sat, 5 Dec 2020 09:41:26 +0100 Subject: [PATCH 27/53] Add first implementation of node generators This is meant to make the overall plugin more userfriendly and make it easier to generate dictionary inputs from the cmdline. Fixed also typo in fleur parser --- aiida_fleur/cmdline/workflows/__init__.py | 10 +- aiida_fleur/common/defaults.py | 27 ++++++ aiida_fleur/common/node_generators.py | 97 +++++++++++++++++++ aiida_fleur/parsers/fleur.py | 2 +- aiida_fleur/workflows/banddos.py | 4 +- aiida_fleur/workflows/create_magnetic_film.py | 4 +- aiida_fleur/workflows/dmi.py | 4 +- aiida_fleur/workflows/eos.py | 5 +- aiida_fleur/workflows/mae.py | 4 +- aiida_fleur/workflows/mae_conv.py | 15 ++- aiida_fleur/workflows/optimize_para.py | 2 + aiida_fleur/workflows/relax.py | 6 +- aiida_fleur/workflows/scf.py | 6 +- aiida_fleur/workflows/ssdisp.py | 4 +- aiida_fleur/workflows/ssdisp_conv.py | 4 +- setup.json | 1 - 16 files changed, 168 insertions(+), 27 deletions(-) create mode 100644 aiida_fleur/common/defaults.py create mode 100644 aiida_fleur/common/node_generators.py diff --git a/aiida_fleur/cmdline/workflows/__init__.py b/aiida_fleur/cmdline/workflows/__init__.py index 1c8925a47..e4e92b550 100755 --- a/aiida_fleur/cmdline/workflows/__init__.py +++ b/aiida_fleur/cmdline/workflows/__init__.py @@ -127,23 +127,25 @@ def gen_wf_para_cmd(entrypoint, show, store, check_existence, kwargs): from aiida_fleur.tools.node_generators import generate_wf_para_node from aiida.plugins import entry_point try: - wf = entry_point.load_entry_point('aiida.workflows', prefix) + wf = entry_point.load_entry_point('aiida.workflows', prefix) except ValueError: echo.echo('here1') try: - wf = entry_point.load_entry_point('aiida.calculations', prefix) + wf = entry_point.load_entry_point('aiida.calculations', prefix) except ValueError: echo.echo('here1') wf_para = generate_wf_para_node(entrypoint=entrypoint, **kwargs) if store: wf_para.store() - echo.echo('Created) + echo.echo('Created wf para node') else: - echo.echo('Created) + echo.echo('Created wf para node') if show: echo.echo_dictionary(wf.para.get_dict()) + + @cmd_workflow.command('inputls') def inputls_wc(): """ diff --git a/aiida_fleur/common/defaults.py b/aiida_fleur/common/defaults.py new file mode 100644 index 000000000..fc45bee68 --- /dev/null +++ b/aiida_fleur/common/defaults.py @@ -0,0 +1,27 @@ +# -*- 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/ # +############################################################################### +''' +Here default values are collected. +Meaning is to centralize the defaults throughout the plugin more, to allow in the +end that the user might be able to specify them in a file he provides +''' +default_options = { + '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': {} +} diff --git a/aiida_fleur/common/node_generators.py b/aiida_fleur/common/node_generators.py new file mode 100644 index 000000000..23e8dc655 --- /dev/null +++ b/aiida_fleur/common/node_generators.py @@ -0,0 +1,97 @@ +# -*- 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 contains functions to generate certain node content, mostly dictionaries +with defaults. +These functions are usefull to expose one the commandline and to have defaults in one place +''' +# Todo: some of these can be moved to aiida-jutools +# since for kkr they might be the same +#todo, we allow kwargs for the nodes, maybe they should be validated +from aiida import orm +from aiida.plugins import WorkflowFactory +from aiida.orm import QueryBuilder + + +def generate_wf_option_node(computer=None, check_existence=True, **kwargs): + """Create a option node for a certain workflow or calculation entry point. + + :param wf_entry_point: string the entry point to create the node for, default='fleur.scf' + :param computer: dict {computername, queue} to provide computer dependend defaults + :param kwargs: dict, further key word argument by which the node content will be updated + + :returns: AiiDA Dict node + """ + option_node_dict = generate_wf_option_dict(computer=computer, **kwargs) + option_node = orm.Dict(dict=option_node_dict) + if check_existence: + duplicate = QueryBuilder().append(orm.Dict, filters={'extras._aiida_hash': option_node._get_hash()}).first() # pylint: disable=protected-access + if duplicate: + option_node = duplicate[0] + + return option_node + + +def generate_wf_option_dict(computer=None, protocol_file=None, **kwargs): + """Create a option dict for a certain workflow or calculation entry point. + + :param computer: dict {computername, queue} to provide computer dependend defaults + :param kwargs: dict, further key word argument by which the node content will be updated + :param protocol_file: str, path to json file containing a set of default options + + :returns: python dict + """ + # options usually do not differe between workflows, but depend on system size and machine. + # Also per project, therefore it would be nice to allow to provide a file and with a set of defaults. + # and this function should read them + from aiida_fleur.common.defaults import default_options + + default_wf_dict = default_options.deepcopy() + #todo better rekursive merge? + default_wf_dict.update(kwargs) + + return default_wf_dict + + +def generate_wf_para_dict(wf_entry_point='fleur.scf', **kwargs): + """Create a wf parameter dict for a certain workflow or calculation entry point. + + :param wf_entry_point: string the entry point to create the node for, default='fleur.scf' + :param kwargs: dict, further key word argument by which the node content will be updated + + :returns: python dict + """ + + wf = WorkflowFactory(wf_entry_point) + default_wf_dict = wf._default_wf_para + #todo better rekursive merge? + default_wf_dict.update(kwargs) + + return default_wf_dict + + +def generate_wf_para_node(wf_entry_point='fleur.scf', check_existence=True, **kwargs): + """Create a wf parameter node for a certain workflow or calculation entry point. + + :param wf_entry_point: string the entry point to create the node for, default='fleur.scf' + :param kwargs: dict, further key word argument by which the node content will be updated + + :returns: AiiDA Dict node + """ + wf_para_node_dict = generate_wf_para_dict(wf_entry_point=wf_entry_point, check_existence=check_existence, **kwargs) + wf_para_node = orm.Dict(dict=wf_para_node_dict) + if check_existence: + duplicate = QueryBuilder().append(orm.Dict, filters={'extras._aiida_hash': wf_para_node._get_hash()}).first() # pylint: disable=protected-access + if duplicate: + wf_para_node = duplicate[0] + + return wf_para_node diff --git a/aiida_fleur/parsers/fleur.py b/aiida_fleur/parsers/fleur.py index f632ddcc4..2605bf859 100644 --- a/aiida_fleur/parsers/fleur.py +++ b/aiida_fleur/parsers/fleur.py @@ -980,7 +980,7 @@ def write_simple_outnode(value, value_type, value_name, dict_out): 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_intersitial', simple_data) + 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) diff --git a/aiida_fleur/workflows/banddos.py b/aiida_fleur/workflows/banddos.py index 8d098a86c..aea1751be 100644 --- a/aiida_fleur/workflows/banddos.py +++ b/aiida_fleur/workflows/banddos.py @@ -60,7 +60,7 @@ class FleurBandDosWorkChain(WorkChain): 'import_sys_environment': False, 'environment_variables': {} } - _wf_default = { + _default_wf_para = { 'fleur_runmax': 4, 'kpath': 'auto', # 'nkpts' : 800, @@ -120,7 +120,7 @@ def start(self): inputs = self.inputs - wf_default = copy.deepcopy(self._wf_default) + wf_default = copy.deepcopy(self._default_wf_para) if 'wf_parameters' in inputs: wf_dict = inputs.wf_parameters.get_dict() else: diff --git a/aiida_fleur/workflows/create_magnetic_film.py b/aiida_fleur/workflows/create_magnetic_film.py index 25224d5df..ea4719dba 100644 --- a/aiida_fleur/workflows/create_magnetic_film.py +++ b/aiida_fleur/workflows/create_magnetic_film.py @@ -34,7 +34,7 @@ class FleurCreateMagneticWorkChain(WorkChain): """ _workflowversion = '0.1.2' - _wf_default = { + _default_wf_para = { 'lattice': 'fcc', 'miller': [[-1, 1, 0], [0, 0, 1], [1, 1, 0]], 'host_symbol': 'Pt', @@ -133,7 +133,7 @@ def start(self): self.ctx.substrate = None # initialize the dictionary using defaults if no wf paramters are given - wf_default = copy.deepcopy(self._wf_default) + wf_default = copy.deepcopy(self._default_wf_para) if 'wf_parameters' in self.inputs: wf_dict = self.inputs.wf_parameters.get_dict() else: diff --git a/aiida_fleur/workflows/dmi.py b/aiida_fleur/workflows/dmi.py index c7d24a086..f4be09837 100644 --- a/aiida_fleur/workflows/dmi.py +++ b/aiida_fleur/workflows/dmi.py @@ -58,7 +58,7 @@ class FleurDMIWorkChain(WorkChain): 'environment_variables': {} } - _wf_default = { + _default_wf_para = { 'serial': False, 'only_even_MPI': False, 'beta': { @@ -124,7 +124,7 @@ def start(self): self.ctx.q_vectors = [] # initialize the dictionary using defaults if no wf paramters are given - wf_default = copy.deepcopy(self._wf_default) + wf_default = copy.deepcopy(self._default_wf_para) if 'wf_parameters' in self.inputs: wf_dict = self.inputs.wf_parameters.get_dict() else: diff --git a/aiida_fleur/workflows/eos.py b/aiida_fleur/workflows/eos.py index b663ea196..a7476361a 100644 --- a/aiida_fleur/workflows/eos.py +++ b/aiida_fleur/workflows/eos.py @@ -55,7 +55,8 @@ class FleurEosWorkChain(WorkChain): _workflowversion = '0.4.0' - _wf_default = {'points': 9, 'step': 0.002, 'guess': 1.00} + _default_wf_para = {'points': 9, 'step': 0.002, 'guess': 1.00} + _default_options = FleurScfWorkChain._default_options @classmethod def define(cls, spec): @@ -101,7 +102,7 @@ def start(self): # TODO get all successful from convergence, if all True this # initialize the dictionary using defaults if no wf paramters are given - wf_default = self._wf_default + wf_default = self._default_wf_para if 'wf_parameters' in self.inputs: wf_dict = self.inputs.wf_parameters.get_dict() else: diff --git a/aiida_fleur/workflows/mae.py b/aiida_fleur/workflows/mae.py index 58d0c3880..18cccfa4e 100644 --- a/aiida_fleur/workflows/mae.py +++ b/aiida_fleur/workflows/mae.py @@ -55,7 +55,7 @@ class FleurMaeWorkChain(WorkChain): 'environment_variables': {} } - _wf_default = { + _default_wf_para = { 'sqa_ref': [0.7, 0.7], 'use_soc_ref': False, 'sqas_theta': [0.0, 1.57079, 1.57079], @@ -116,7 +116,7 @@ def start(self): self.ctx.fleuroutuuid = None # initialize the dictionary using defaults if no wf paramters are given - wf_default = copy.deepcopy(self._wf_default) + wf_default = copy.deepcopy(self._default_wf_para) if 'wf_parameters' in self.inputs: wf_dict = self.inputs.wf_parameters.get_dict() else: diff --git a/aiida_fleur/workflows/mae_conv.py b/aiida_fleur/workflows/mae_conv.py index fb79b12d3..6965996e9 100644 --- a/aiida_fleur/workflows/mae_conv.py +++ b/aiida_fleur/workflows/mae_conv.py @@ -34,7 +34,18 @@ class FleurMaeConvWorkChain(WorkChain): _workflowversion = '0.2.0' - _wf_default = {'sqas': {'label': [0.0, 0.0]}, 'soc_off': []} + _default_wf_para = {'sqas': {'label': [0.0, 0.0]}, 'soc_off': []} + _default_options = { + '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': {} + } @classmethod def define(cls, spec): @@ -68,7 +79,7 @@ def start(self): self.ctx.mae_phis = [] # initialize the dictionary using defaults if no wf paramters are given - wf_default = copy.deepcopy(self._wf_default) + wf_default = copy.deepcopy(self._default_wf_para) if 'wf_parameters' in self.inputs: wf_dict = self.inputs.wf_parameters.get_dict() else: diff --git a/aiida_fleur/workflows/optimize_para.py b/aiida_fleur/workflows/optimize_para.py index 163774c8b..6c1a9ad2d 100644 --- a/aiida_fleur/workflows/optimize_para.py +++ b/aiida_fleur/workflows/optimize_para.py @@ -50,6 +50,8 @@ class fleur_optimize_parameters_wc(WorkChain): _workflowversion = '0.1.0' + _default_wf_para = {} + def __init__(self, *args, **kwargs): super(fleur_optimize_parameters_wc, self).__init__(*args, **kwargs) diff --git a/aiida_fleur/workflows/relax.py b/aiida_fleur/workflows/relax.py index 0c5954c06..ef55d563c 100644 --- a/aiida_fleur/workflows/relax.py +++ b/aiida_fleur/workflows/relax.py @@ -38,7 +38,7 @@ class FleurRelaxWorkChain(WorkChain): _workflowversion = '0.2.2' - _wf_default = { + _default_wf_para = { 'relax_iter': 5, # Stop if not converged after so many relaxation steps 'film_distance_relaxation': False, # Do not relax the z coordinates 'force_criterion': 0.001, # Converge the force until lower this value in atomic units @@ -48,6 +48,8 @@ class FleurRelaxWorkChain(WorkChain): 'atoms_off': [] # Species to be switched off, '49' is reserved } + _default_options = FleurScfWorkChain._default_options + @classmethod def define(cls, spec): super(FleurRelaxWorkChain, cls).define(spec) @@ -114,7 +116,7 @@ def start(self): self.ctx.total_magnetic_moment = None # initialize the dictionary using defaults if no wf paramters are given - wf_default = copy.deepcopy(self._wf_default) + wf_default = copy.deepcopy(self._default_wf_para) if 'wf_parameters' in self.inputs: wf_dict = self.inputs.wf_parameters.get_dict() else: diff --git a/aiida_fleur/workflows/scf.py b/aiida_fleur/workflows/scf.py index 70e0d4d2c..f0b18a253 100644 --- a/aiida_fleur/workflows/scf.py +++ b/aiida_fleur/workflows/scf.py @@ -61,7 +61,7 @@ class FleurScfWorkChain(WorkChain): """ _workflowversion = '0.4.2' - _wf_default = { + _default_wf_para = { 'fleur_runmax': 4, 'density_converged': 0.00002, 'energy_converged': 0.002, @@ -144,7 +144,7 @@ def start(self): self.ctx.abort = False self.ctx.reached_conv = True - wf_default = self._wf_default + wf_default = self._default_wf_para if 'wf_parameters' in self.inputs: wf_dict = self.inputs.wf_parameters.get_dict() else: @@ -201,7 +201,7 @@ def validate_input(self): """ extra_keys = [] for key in self.ctx.wf_dict.keys(): - if key not in self._wf_default.keys(): + if key not in self._default_wf_para.keys(): extra_keys.append(key) if extra_keys: error = 'ERROR: input wf_parameters for SCF contains extra keys: {}'.format(extra_keys) diff --git a/aiida_fleur/workflows/ssdisp.py b/aiida_fleur/workflows/ssdisp.py index 840c4f219..09d9dd74e 100644 --- a/aiida_fleur/workflows/ssdisp.py +++ b/aiida_fleur/workflows/ssdisp.py @@ -56,7 +56,7 @@ class FleurSSDispWorkChain(WorkChain): 'environment_variables': {} } - _wf_default = { + _default_wf_para = { 'beta': { 'all': 1.57079 }, @@ -113,7 +113,7 @@ def start(self): self.ctx.energy_dict = [] # initialize the dictionary using defaults if no wf paramters are given - wf_default = copy.deepcopy(self._wf_default) + wf_default = copy.deepcopy(self._default_wf_para) if 'wf_parameters' in self.inputs: wf_dict = self.inputs.wf_parameters.get_dict() else: diff --git a/aiida_fleur/workflows/ssdisp_conv.py b/aiida_fleur/workflows/ssdisp_conv.py index 7efab8441..cd37fade3 100644 --- a/aiida_fleur/workflows/ssdisp_conv.py +++ b/aiida_fleur/workflows/ssdisp_conv.py @@ -33,7 +33,7 @@ class FleurSSDispConvWorkChain(WorkChain): _workflowversion = '0.2.0' - _wf_default = { + _default_wf_para = { 'beta': { 'all': 1.57079 }, @@ -76,7 +76,7 @@ def start(self): self.ctx.energy_dict = [] # initialize the dictionary using defaults if no wf paramters are given - wf_default = copy.deepcopy(self._wf_default) + wf_default = copy.deepcopy(self._default_wf_para) if 'wf_parameters' in self.inputs: wf_dict = self.inputs.wf_parameters.get_dict() else: diff --git a/setup.json b/setup.json index 33012f06a..a9a07007e 100644 --- a/setup.json +++ b/setup.json @@ -76,7 +76,6 @@ "aiida.workflows": [ "fleur.scf = aiida_fleur.workflows.scf:FleurScfWorkChain", "fleur.dos = aiida_fleur.workflows.dos:fleur_dos_wc", - "fleur.band = aiida_fleur.workflows.band:FleurBandWorkChain", "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", From 9c81e1c4f158c2a00a5cf016cb3e05b94fb2a25b Mon Sep 17 00:00:00 2001 From: broeder-j Date: Mon, 7 Dec 2020 09:20:04 +0100 Subject: [PATCH 28/53] Migrate to aiida-core 1.5.0, fixed tests --- tests/cmdline/conftest.py | 23 +++++++++++---------- tests/cmdline/visualization/test_plot.py | 10 ++++++--- tests/cmdline/workflows/test_worklow_cmd.py | 9 +++++++- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/tests/cmdline/conftest.py b/tests/cmdline/conftest.py index 8697d6c37..dbf830f8a 100644 --- a/tests/cmdline/conftest.py +++ b/tests/cmdline/conftest.py @@ -19,29 +19,30 @@ def import_with_migrate(temp_dir): We want to be able to run the test with several aiida versions, therefore imports have to be migrate, but we also do not want to use verdi """ + # This function has some deep aiida imports which might change in the future _DEFAULT_IMPORT_KWARGS = {'group': None} def _import_with_migrate(filename, tempdir=temp_dir, import_kwargs=None, try_migration=True): from click import echo from aiida.tools.importexport import import_data from aiida.tools.importexport import EXPORT_VERSION, IncompatibleArchiveVersionError - #from aiida.tools.importexport import detect_archive_type, - #from aiida.tools.importexport.archive.migrators import get_migrator - #from aiida.tools.importexport.common.config import ExportFileFormat + # these are only availbale after aiida >= 1.5.0, maybe rely on verdi import instead + from aiida.tools.importexport import detect_archive_type + from aiida.tools.importexport.archive.migrators import get_migrator + from aiida.tools.importexport.common.config import ExportFileFormat if import_kwargs is None: import_kwargs = _DEFAULT_IMPORT_KWARGS archive_path = filename + try: import_data(archive_path, **import_kwargs) except IncompatibleArchiveVersionError as exception: - raise ValueError - #if try_migration: - # echo(f'incompatible version detected for {archive}, trying migration') - # migrator = get_migrator(ExportFileFormat.TAR_GZIPPED)(archive_path) - # archive_path = migrator.migrate( - # EXPORT_VERSION, None, out_compression='none', work_dir=tempdir - # ) - # import_data(archive_path, **import_kwargs) + #raise ValueError + if try_migration: + echo(f'incompatible version detected for {archive_path}, trying migration') + migrator = get_migrator(detect_archive_type(archive_path))(archive_path) + archive_path = migrator.migrate(EXPORT_VERSION, None, out_compression='none', work_dir=tempdir) + import_data(archive_path, **import_kwargs) return _import_with_migrate diff --git a/tests/cmdline/visualization/test_plot.py b/tests/cmdline/visualization/test_plot.py index cfb9dbffc..36477dd8f 100644 --- a/tests/cmdline/visualization/test_plot.py +++ b/tests/cmdline/visualization/test_plot.py @@ -14,14 +14,18 @@ ''' import os -from aiida.tools.importexport import import_data +import pytest +import aiida +from packaging import version file_path = '../../files/exports/fleur_scf_fleurinp_Si.tar.gz' thisfilefolder = os.path.dirname(os.path.abspath(__file__)) EXPORTFILE_FILE = os.path.abspath(os.path.join(thisfilefolder, file_path)) -def test_cmd_plot(run_cli_command, temp_dir): +@pytest.mark.skipif(version.parse(aiida.__version__) < version.parse('1.5.0'), + reason='archive import and migration works only with aiida-core > 1.5.0') +def test_cmd_plot(run_cli_command, temp_dir, import_with_migrate): """Test invoking the plot command in all variants. If this test hangs, --no-show is not working @@ -29,7 +33,7 @@ def test_cmd_plot(run_cli_command, temp_dir): from aiida_fleur.cmdline.visualization import cmd_plot # import an an aiida export, this does not migrate - import_data(EXPORTFILE_FILE, group=None) + import_with_migrate(EXPORTFILE_FILE) process_uuid = '7f9f4cfb-4170-48ea-801d-4269f88792e0' options = [process_uuid, '--no-show'] diff --git a/tests/cmdline/workflows/test_worklow_cmd.py b/tests/cmdline/workflows/test_worklow_cmd.py index 37ffd780e..f8909b7aa 100644 --- a/tests/cmdline/workflows/test_worklow_cmd.py +++ b/tests/cmdline/workflows/test_worklow_cmd.py @@ -14,12 +14,17 @@ ''' import os -#from aiida.tools.importexport import import_data +import pytest +import aiida +from packaging import version + file_path = '../../files/exports/fleur_scf_fleurinp_Si.tar.gz' thisfilefolder = os.path.dirname(os.path.abspath(__file__)) EXPORTFILE_FILE = os.path.abspath(os.path.join(thisfilefolder, file_path)) +@pytest.mark.skipif(version.parse(aiida.__version__) < version.parse('1.5.0'), + reason='archive import and migration works only with aiida-core > 1.5.0') def test_workchain_res(run_cli_command, import_with_migrate): """Test invoking the workchain res command in all variants.""" from aiida_fleur.cmdline.workflows import workchain_res @@ -47,6 +52,8 @@ def test_workchain_res(run_cli_command, import_with_migrate): run_cli_command(workchain_res, options=options, raises=KeyError) +@pytest.mark.skipif(version.parse(aiida.__version__) < version.parse('1.5.0'), + reason='archive import and migration works only with aiida-core > 1.5.0') def test_workchain_inputdict(run_cli_command, import_with_migrate): """Test invoking the workchain inputdict command in all variants.""" from aiida_fleur.cmdline.workflows import workchain_inputdict From 71c183e2f3bb33107a73c6ff6743aeaf36917edd Mon Sep 17 00:00:00 2001 From: broeder-j Date: Mon, 7 Dec 2020 10:00:00 +0100 Subject: [PATCH 29/53] Fix create_slab test --- tests/tools/test_StructureData_util.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/tools/test_StructureData_util.py b/tests/tools/test_StructureData_util.py index 3b784736f..1475b2984 100644 --- a/tests/tools/test_StructureData_util.py +++ b/tests/tools/test_StructureData_util.py @@ -483,11 +483,11 @@ def test_create_slap(generate_structure): [1.9197949109214756, 3.3251823258281643, 2.351070692679364e-16], [0.0, 0.0, 9.405035885099004]] sites_should = [(3.839589821842953, 2.216788217218776, 3.135011961699669), (0.0, 0.0, 0.0), (1.9197949109214758, 1.1083941086093878, 6.270023923399337)] - - assert film_struc.cell == cell_should - assert film_struc.sites[0].position == sites_should[0] - assert film_struc.sites[1].position == sites_should[1] - assert film_struc.sites[2].position == sites_should[2] + # since this depends on pymatgen we round here the last digits. + assert np.round(film_struc.cell, 8) == np.round(cell_should, 8) + assert np.round(film_struc.sites[0].position, 8) == np.round(sites_should[0], 8) + assert np.round(film_struc.sites[1].position, 8) == np.round(sites_should[1], 8) + assert np.round(film_struc.sites[2].position, 8) == np.round(sites_should[2], 8) def test_create_all_slabs(generate_structure): From 9f644e89e29ce5898e2b1b1387e97fc96751708b Mon Sep 17 00:00:00 2001 From: Jens Broeder Date: Mon, 7 Dec 2020 11:01:32 +0100 Subject: [PATCH 30/53] Persist Kind/Species information through relaxation wc (#102) In the relaxation case also parse information on the atomtypes and the species names. The relaxation wc presists these species names. This is important when one relaxes magnetic systems or any structure, which had several kinds at the beginning. Relaxed to issue # 101. * Add fleur parser test for relax * Make CI stay on ubuntu 18.04 for now, since 20.04 causes problems Verifying the rabbitmq install. We will switch back to ubuntu-latest if github is finished with its migration --- .github/workflows/ci.yml | 6 +- aiida_fleur/parsers/fleur.py | 13 +- aiida_fleur/workflows/relax.py | 21 +- tests/files/outxml/all_test/Fe_relax_out.xml | 474 +++++++++++++++++++ tests/files/relaxxml/Fe_relax.xml | 13 + tests/parsers/test_fleur_parser.py | 12 + tests/tools/test_StructureData_util.py | 8 +- 7 files changed, 530 insertions(+), 17 deletions(-) create mode 100644 tests/files/outxml/all_test/Fe_relax_out.xml create mode 100644 tests/files/relaxxml/Fe_relax.xml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b6ea4063a..440750b8e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,7 @@ jobs: docs: - runs-on: ubuntu-latest + runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v1 @@ -37,7 +37,7 @@ jobs: pre-commit: - runs-on: ubuntu-latest + runs-on: ubuntu-18.04 timeout-minutes: 30 steps: @@ -69,7 +69,7 @@ jobs: tests: - runs-on: ubuntu-latest + runs-on: ubuntu-18.04 timeout-minutes: 30 strategy: diff --git a/aiida_fleur/parsers/fleur.py b/aiida_fleur/parsers/fleur.py index 2605bf859..e810febb3 100644 --- a/aiida_fleur/parsers/fleur.py +++ b/aiida_fleur/parsers/fleur.py @@ -314,7 +314,7 @@ def parse_xmlout_file(outxmlfile): # FIXME: This is global, look for a different way to do this, python logging? parser_info_out = {'parser_warnings': [], 'unparsed': []} - parser_version = '0.3.1' + parser_version = '0.3.2' parser_info_out['parser_info'] = 'AiiDA Fleur Parser v{}'.format(parser_version) #parsed_data = {} @@ -756,6 +756,7 @@ def parse_simple_outnode(iteration_node, fleurmode): 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' @@ -763,7 +764,6 @@ def parse_simple_outnode(iteration_node, fleurmode): film_lat_xpath = '/fleurOutput/inputData/cell/filmLattice/bravaisMatrix/' bulk_lat_xpath = '/fleurOutput/inputData/cell/bulkLattice/bravaisMatrix/' - kmax_xpath = '/fleurOutput/inputData/calculationSetup/cutoffs/@Kmax' ################################################### @@ -1029,15 +1029,20 @@ def write_simple_outnode(value, value_type, value_name, dict_out): 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: - element = get_xml_attribute(a_type, 'species').split('-')[0] + 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([element] + pos) + 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) diff --git a/aiida_fleur/workflows/relax.py b/aiida_fleur/workflows/relax.py index ef55d563c..718f67af5 100644 --- a/aiida_fleur/workflows/relax.py +++ b/aiida_fleur/workflows/relax.py @@ -36,7 +36,7 @@ class FleurRelaxWorkChain(WorkChain): This workflow performs structure optimization. """ - _workflowversion = '0.2.2' + _workflowversion = '0.3.0' _default_wf_para = { 'relax_iter': 5, # Stop if not converged after so many relaxation steps @@ -449,6 +449,7 @@ def get_results_relax(self): film = relax_out['film'] total_energy = relax_out['energy'] total_energy_units = relax_out['energy_units'] + atomtype_info = relax_out['relax_atomtype_info'] except KeyError: return self.exit_codes.ERROR_NO_RELAX_OUTPUT @@ -456,6 +457,7 @@ def get_results_relax(self): self.ctx.total_energy_units = total_energy_units self.ctx.final_cell = cell self.ctx.final_atom_positions = atom_positions + self.ctx.atomtype_info = atomtype_info if film == 'True': self.ctx.pbc = (True, True, False) @@ -463,17 +465,24 @@ def get_results_relax(self): 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: np_cell = np.array(self.ctx.final_cell) * BOHR_A structure = StructureData(cell=np_cell.tolist()) - - for atom in self.ctx.final_atom_positions: - np_pos = np.array(atom[1:]) + #self.report('############ {}'.format(atomtype_info)) + for i, atom in enumerate(self.ctx.final_atom_positions): + species_name = atomtype_info[i][0] + element = atomtype_info[i][1] + np_pos = np.array(atom) pos_abs = np_pos @ np_cell if self.ctx.pbc == (True, True, True): - structure.append_atom(position=(pos_abs[0], pos_abs[1], pos_abs[2]), symbols=atom[0]) + structure.append_atom(position=(pos_abs[0], pos_abs[1], pos_abs[2]), + 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), symbols=atom[0]) + structure.append_atom(position=(pos_abs[0], pos_abs[1], atom[3] * BOHR_A), + symbols=element, + name=species_name) structure.pbc = self.ctx.pbc self.ctx.final_structure = structure diff --git a/tests/files/outxml/all_test/Fe_relax_out.xml b/tests/files/outxml/all_test/Fe_relax_out.xml new file mode 100644 index 000000000..14207dadb --- /dev/null +++ b/tests/files/outxml/all_test/Fe_relax_out.xml @@ -0,0 +1,474 @@ + + + + + + GEN + + + + + + + + + + + A Fleur input generator calculation with aiida + + + + + + + + + + .0000000000 .0000000000 .0000000000 + + + + + + + 0.416667 0.416667 0.416667 + 0.416667 0.416667 0.250000 + 0.416667 0.416667 0.083333 + 0.416667 0.250000 0.250000 + 0.416667 0.250000 0.083333 + 0.416667 0.083333 0.083333 + 0.250000 0.250000 0.250000 + 0.250000 0.250000 0.083333 + 0.250000 0.083333 0.083333 + 0.083333 0.083333 0.083333 + + + + + + + + 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 + + + + + 5.354555983000000 .000000000000000 .000000000000000 + .000000000000000 5.354555983000000 .000000000000000 + .000000000000000 .000000000000000 5.354555983000000 + + + + + + + + + + + + + + + + + + + + + + + + + .0000000000 .0000000000 .0000000000 + + + + + 1.000/2.000 1.000/2.000 1.000/2.000 + + + + + + + + + + + + + + + + + + + + + + + + + + 0.416667 0.416667 0.416667 + 0.416667 0.416667 0.250000 + 0.416667 0.416667 0.083333 + 0.416667 0.250000 0.250000 + 0.416667 0.250000 0.083333 + 0.416667 0.083333 0.083333 + 0.250000 0.250000 0.250000 + 0.250000 0.250000 0.083333 + 0.250000 0.083333 0.083333 + 0.083333 0.083333 0.083333 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/files/relaxxml/Fe_relax.xml b/tests/files/relaxxml/Fe_relax.xml new file mode 100644 index 000000000..5ef8a9cc3 --- /dev/null +++ b/tests/files/relaxxml/Fe_relax.xml @@ -0,0 +1,13 @@ + + + + 0.0000000000 -0.0000000000 -0.0000000000 + 0.0000000000 0.0000000000 -0.0000000000 + + + + 0.0000000000 0.0000000000 0.0000000000 0.0000000000 -0.0000000000 -0.0000000000 + 2.6772779915 2.6772779915 2.6772779915 0.0000000000 0.0000000000 -0.0000000000 + + + diff --git a/tests/parsers/test_fleur_parser.py b/tests/parsers/test_fleur_parser.py index 58b584c4f..3cf501f15 100644 --- a/tests/parsers/test_fleur_parser.py +++ b/tests/parsers/test_fleur_parser.py @@ -288,6 +288,18 @@ def test_fleurparse_all_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 aiida.orm import Dict + + filename = os.path.abspath('./files/relaxxml/Fe_relax.xml') + with open(filename, 'r') as relaxfile: + result = parse_relax_file(relaxfile) + assert isinstance(result, Dict) + assert result.get_dict() != {} + + # parse_dos_file, test for different dos files with spin and without @pytest.mark.skip(reason='Test is not implemented') def test_parse_dos_file(): diff --git a/tests/tools/test_StructureData_util.py b/tests/tools/test_StructureData_util.py index 1475b2984..7c7f92f9f 100644 --- a/tests/tools/test_StructureData_util.py +++ b/tests/tools/test_StructureData_util.py @@ -484,10 +484,10 @@ def test_create_slap(generate_structure): sites_should = [(3.839589821842953, 2.216788217218776, 3.135011961699669), (0.0, 0.0, 0.0), (1.9197949109214758, 1.1083941086093878, 6.270023923399337)] # since this depends on pymatgen we round here the last digits. - assert np.round(film_struc.cell, 8) == np.round(cell_should, 8) - assert np.round(film_struc.sites[0].position, 8) == np.round(sites_should[0], 8) - assert np.round(film_struc.sites[1].position, 8) == np.round(sites_should[1], 8) - assert np.round(film_struc.sites[2].position, 8) == np.round(sites_should[2], 8) + assert (np.round(film_struc.cell, 8) == np.round(cell_should, 8)).all() + assert (np.round(film_struc.sites[0].position, 8) == np.round(sites_should[0], 8)).all() + assert (np.round(film_struc.sites[1].position, 8) == np.round(sites_should[1], 8)).all() + assert (np.round(film_struc.sites[2].position, 8) == np.round(sites_should[2], 8)).all() def test_create_all_slabs(generate_structure): From 0c631b0921d2e445e809418ec31220af62a599f9 Mon Sep 17 00:00:00 2001 From: broeder-j Date: Mon, 7 Dec 2020 11:39:28 +0100 Subject: [PATCH 31/53] Change parser version in parser tests --- tests/parsers/test_fleur_parser.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/parsers/test_fleur_parser.py b/tests/parsers/test_fleur_parser.py index 3cf501f15..30a6b4b4d 100644 --- a/tests/parsers/test_fleur_parser.py +++ b/tests/parsers/test_fleur_parser.py @@ -67,7 +67,7 @@ def test_parse_xmlout_file(): } } - expected_parser_info_out = {'parser_info': 'AiiDA Fleur Parser v0.3.1', 'parser_warnings': [], 'unparsed': []} + 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... @@ -93,7 +93,7 @@ def test_parse_xmlout_file_broken_xmlout_file(): 'last_iteration_parsed': 15, 'parser_info': - 'AiiDA Fleur Parser v0.3.1', + '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!' @@ -123,7 +123,7 @@ def test_parse_xmlout_file_broken_first_xmlout_file(): 'last_iteration_parsed': 1, 'parser_info': - 'AiiDA Fleur Parser v0.3.1', + '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.', @@ -212,7 +212,7 @@ def test_parse_xmlout_file_fortran_garbage_in_xmlout_file(): expected_parser_info_out = { 'parser_info': - 'AiiDA Fleur Parser v0.3.1', + 'AiiDA Fleur Parser v0.3.2', 'parser_warnings': [ 'Could not convert: "**" to float, ValueError', 'Could not convert: " !#@)!(U$*(Y" to float, ValueError' @@ -252,7 +252,7 @@ def test_parse_xmlout_file_empty_file(): expected_parser_info_out = { 'parser_info': - 'AiiDA Fleur Parser v0.3.1', + '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.' From 37da74b11d25ea60d5db947f3fa569123801b54d Mon Sep 17 00:00:00 2001 From: Jens Broeder Date: Mon, 7 Dec 2020 11:59:44 +0100 Subject: [PATCH 32/53] Update ci.yml one codecov action fails, but it should be fine for CI and no red cross. --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 440750b8e..28bfc4963 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -122,6 +122,7 @@ jobs: ./run_all_cov.sh - name: Upload report to Codecov - uses: codecov/codecov-action@v1.0.15 + uses: codecov/codecov-action@v1 with: file: ./tests/coverage.xml + fail_ci_if_error: False From 81dd1723724091923d20e6de584001cf4306d267 Mon Sep 17 00:00:00 2001 From: broeder-j Date: Mon, 7 Dec 2020 12:06:24 +0100 Subject: [PATCH 33/53] Delete old, non-used get_scheduler_extras function. --- aiida_fleur/tools/common_fleur_wf.py | 63 ---------------------------- tests/tools/test_common_fleur_wf.py | 5 --- 2 files changed, 68 deletions(-) diff --git a/aiida_fleur/tools/common_fleur_wf.py b/aiida_fleur/tools/common_fleur_wf.py index 34e5cbf08..76026de72 100644 --- a/aiida_fleur/tools/common_fleur_wf.py +++ b/aiida_fleur/tools/common_fleur_wf.py @@ -194,69 +194,6 @@ def get_inputs_inpgen(structure, inpgencode, options, label='', description='', return inputs -def get_scheduler_extras(code, resources, extras=None, project='jara0172'): - """ - This is a utility function with the goal to make prepare the right resource - and scheduler extras for a given computer. - Since this is user dependend you might want to create your own. - - return: dict, custom scheduler commands - """ - if extras is None: - extras = {} - nnodes = resources.get('num_machines', 1) - - # TODO memory has to be done better... - mem_per_node = 120000 # max recommend 126000 MB on claix jara-clx nodes - mem_per_process = mem_per_node / 24 - if not extras: - # use defaults # TODO add other things, span, pinnning... openmp - extras = { - 'lsf': ('#BSUB -P {} \n#BSUB -M {} \n' - '#BSUB -a intelmpi'.format(project, mem_per_process)), - 'torque': '', - 'direct': '' - } - - # get the scheduler type from the computer the code is run on. - com = code.computer - # com_name = com.get_name() - scheduler_type = com.get_scheduler_type() - - default_per_machine = com.get_default_mpiprocs_per_machine() - if not default_per_machine: - default_per_machine = 24 # claix, lsf does can not have default mpiprocs... #TODO this better - tot_num_mpiprocs = resources.get('tot_num_mpiprocs', default_per_machine * nnodes) - - if scheduler_type == 'lsf': - new_resources = {'tot_num_mpiprocs': tot_num_mpiprocs} # only this needs to be given - elif scheduler_type == 'torque': - # {'num_machines', 1} # on iff003 currently we do not do multinode mpi, - new_resources = resources - # like this it will get stuck on iff003 - else: - new_resources = resources - scheduler_extras = extras.get(scheduler_type, '') - - return new_resources, scheduler_extras - - -# test -############################### -# codename = 'inpgen@local_mac'#'inpgen_v0.28@iff003'#'inpgen_iff@local_iff' -# codename2 = 'fleur_v0.28@iff003'#'fleur_mpi_v0.28@iff003'# 'fleur_iff_0.28@local_iff'' -# codename2 = 'fleur_max_1.3_dev@iff003' -# codename2 = 'fleur_mpi_max_1.3_dev@iff003' -# codename4 = 'fleur_mpi_v0.28@claix' -############################### -# code = Code.get_from_string(codename) -# code2 = Code.get_from_string(codename2) -# code4 = Code.get_from_string(codename4) -# print(get_scheduler_extras(code, {'num_machines' : 1})) -# print(get_scheduler_extras(code2, {'num_machines' : 2})) -# print(get_scheduler_extras(code4, {'num_machines' : 1})) - - def test_and_get_codenode(codenode, expected_code_type, use_exceptions=False): """ Pass a code node and an expected code (plugin) type. Check that the diff --git a/tests/tools/test_common_fleur_wf.py b/tests/tools/test_common_fleur_wf.py index c8ca0e499..ec48f3557 100644 --- a/tests/tools/test_common_fleur_wf.py +++ b/tests/tools/test_common_fleur_wf.py @@ -160,11 +160,6 @@ def test_get_inputs_inpgen(fixture_code, generate_structure): assert get_inputs_inpgen(**inputs) == returns -@pytest.mark.skip(reason='Test is not implemented') -def test_get_scheduler_extras(): - from aiida_fleur.tools.common_fleur_wf import get_scheduler_extras - - # test_and_get_codenode From ecf8e95f59a3c77c2598fd9cd1ce00bdb67c5b21 Mon Sep 17 00:00:00 2001 From: broeder-j Date: Mon, 7 Dec 2020 12:57:16 +0100 Subject: [PATCH 34/53] Add tests for common_fleur_wf and utils --- aiida_fleur/tools/common_fleur_wf.py | 27 ++----------- aiida_fleur/tools/common_fleur_wf_util.py | 10 ----- aiida_fleur/tools/xml_util.py | 6 +-- tests/tools/test_common_fleur_wf.py | 39 ++++++++++++++++-- tests/tools/test_common_fleur_wf_util.py | 48 ++++++++++++++++++----- 5 files changed, 82 insertions(+), 48 deletions(-) diff --git a/aiida_fleur/tools/common_fleur_wf.py b/aiida_fleur/tools/common_fleur_wf.py index 76026de72..7fc82206a 100644 --- a/aiida_fleur/tools/common_fleur_wf.py +++ b/aiida_fleur/tools/common_fleur_wf.py @@ -290,6 +290,9 @@ def determine_favorable_reaction(reaction_list, workchain_dict): workchain_dict = {'Be12W' : uuid_wc or output, 'Be2W' : uuid, ...} return dictionary that ranks the reactions after their enthalpy + + TODO: refactor aiida part out of this, leaving an aiida independent part and one + more universal """ from aiida.engine import WorkChain from aiida_fleur.tools.common_fleur_wf_util import get_enhalpy_of_equation @@ -321,7 +324,7 @@ def determine_favorable_reaction(reaction_list, workchain_dict): except (AttributeError, KeyError, ValueError): # TODO: Check this ouputnode = None formenergy = None - print(('WARNING: ouput node of {} not found. I skip'.format(n))) + print(('WARNING: output node of {} not found. I skip'.format(n))) continue formenergy = ouputnode.get('formation_energy') # TODO is this value per atom? @@ -338,28 +341,6 @@ def determine_favorable_reaction(reaction_list, workchain_dict): return energy_sorted_reactions -# test -# reaction_list = ['1*Be12W->1*Be12W', '2*Be12W->1*Be2W+1*Be22W', '11*Be12W->5*W+6*Be22W', '1*Be12W->12*Be+1*W', '1*Be12W->1*Be2W+10*Be'] -# workchain_dict = {'Be12W' : '4f685bc5-b5fb-46d3-aad6-e0f512c3313d', -# 'Be2W' : '045d3071-f442-46b4-8d6b-3c85d72b24d4', -# 'Be22W' : '1e32880a-bdc9-4081-a5da-be04860aa1bc', -# 'W' : 'f8b12b23-0b71-45a1-9040-b51ccf379439', -# 'Be' : 0.0} -# reac_list = determine_favorable_reaction(reaction_list, workchain_dict) -# print reac_list -# {'products': {'Be12W': 1}, 'educts': {'Be12W': 1}} -# 0.0 -# {'products': {'Be2W': 1, 'Be22W': 1}, 'educts': {'Be12W': 2}} -# 0.114321037514 -# {'products': {'Be22W': 6, 'W': 5}, 'educts': {'Be12W': 11}} -# -0.868053153884 -# {'products': {'Be': 12, 'W': 1}, 'educts': {'Be12W': 1}} -# -0.0946046496213 -# {'products': {'Be': 10, 'Be2W': 1}, 'educts': {'Be12W': 1}} -# 0.180159355144 -# [['11*Be12W->5*W+6*Be22W', -0.8680531538839534], ['1*Be12W->12*Be+1*W', -0.0946046496213127], ['1*Be12W->1*Be12W', 0.0], ['2*Be12W->1*Be2W+1*Be22W', 0.11432103751404535], ['1*Be12W->1*Be2W+10*Be', 0.1801593551436103]] - - def performance_extract_calcs(calcs): """ Extracts some runtime and system data from given fleur calculations diff --git a/aiida_fleur/tools/common_fleur_wf_util.py b/aiida_fleur/tools/common_fleur_wf_util.py index 4599728d9..54d9eb23c 100644 --- a/aiida_fleur/tools/common_fleur_wf_util.py +++ b/aiida_fleur/tools/common_fleur_wf_util.py @@ -568,13 +568,3 @@ def check_eos_energies(energylist): print('annormly detected') return abnormality, abnormalityindexlist - - -#total_energy = [ -1, -2, -3 ,-2,-4,-3,-2,-1] -#check_eos_energies(total_energy) -#(True, [3]) -#total_energy = [ -1, -2, -3 ,-2,-2,-3,-2,-1] -#check_eos_energies(total_energy) -#(False, []) -#total_energy = [ -1, -2, -3 ,-4,-5,-3,-2,-1] -#(False, []) diff --git a/aiida_fleur/tools/xml_util.py b/aiida_fleur/tools/xml_util.py index 4841950f8..ad26089b2 100644 --- a/aiida_fleur/tools/xml_util.py +++ b/aiida_fleur/tools/xml_util.py @@ -586,7 +586,7 @@ def get_inpgen_para_from_xml(inpxmlfile): atom_jri_xpath = 'mtSphere/@gridPoints' atom_lmax_xpath = 'atomicCutoffs/@lmax' atom_lnosph_xpath = 'atomicCutoffs/@lnonsphr' - atom_ncst_xpath = '@coreStates' + #atom_ncst_xpath = '@coreStates' atom_econfig_xpath = 'electronConfig' # converting todo atom_bmu_xpath = '@magMom' atom_lo_xpath = 'lo' # converting todo @@ -654,7 +654,7 @@ def get_inpgen_para_from_xml(inpxmlfile): 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_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) @@ -667,7 +667,7 @@ def get_inpgen_para_from_xml(inpxmlfile): 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, 'ncst', atom_ncst) + #atom_dict = set_dict_or_not(atom_dict, 'ncst', atom_ncst) 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: diff --git a/tests/tools/test_common_fleur_wf.py b/tests/tools/test_common_fleur_wf.py index ec48f3557..92646476a 100644 --- a/tests/tools/test_common_fleur_wf.py +++ b/tests/tools/test_common_fleur_wf.py @@ -17,6 +17,7 @@ # 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 @@ -161,9 +162,14 @@ def test_get_inputs_inpgen(fixture_code, generate_structure): # test_and_get_codenode - - def test_test_and_get_codenode_inpgen(fixture_code): + """Tests for test_and_get_code_node function + + test interface for cases: + if code exists and is right, + if code is wrong, + if code does not exists + """ from aiida_fleur.tools.common_fleur_wf import test_and_get_codenode from aiida.orm import Code from aiida.common.exceptions import NotExistent @@ -193,6 +199,7 @@ def test_test_and_get_codenode_inpgen(fixture_code): def test_get_kpoints_mesh_from_kdensity(generate_structure): + """Test genration of a kpoints mesh node from a given density""" from aiida_fleur.tools.common_fleur_wf import get_kpoints_mesh_from_kdensity from aiida.orm import KpointsData @@ -201,10 +208,36 @@ def test_get_kpoints_mesh_from_kdensity(generate_structure): assert isinstance(b, KpointsData) -@pytest.mark.skip(reason='Test is not implemented') def test_determine_favorable_reaction(): + """Test favorable reaction function + + which returns a list which ranks which reaction is energetically the most favorable. + We only test the way if the formation energies are provided + not when they have to be taken from the node + """ from aiida_fleur.tools.common_fleur_wf import determine_favorable_reaction + reaction_list = [ + '1*Be12W->1*Be12W', '2*Be12W->1*Be2W+1*Be22W', '11*Be12W->5*W+6*Be22W', '1*Be12W->12*Be+1*W', + '1*Be12W->1*Be2W+10*Be' + ] + workchain_dict = { + 'Be12W': -0.21, #'4f685bc5-b5fb-46d3-aad6-e0f512c3313d', + 'Be2W': -0.3, #'045d3071-f442-46b4-8d6b-3c85d72b24d4', + 'Be22W': -0.1, #'1e32880a-bdc9-4081-a5da-be04860aa1bc', + 'W': 0.0, #'f8b12b23-0b71-45a1-9040-b51ccf379439', + 'Be': 0.0 + } + best_order = [['11*Be12W->5*W+6*Be22W', -1.71], ['1*Be12W->12*Be+1*W', -0.21], + ['2*Be12W->1*Be2W+1*Be22W', -0.019999999999999962], ['1*Be12W->1*Be12W', 0.0], + ['1*Be12W->1*Be2W+10*Be', 0.09]] + + reac_list = determine_favorable_reaction(reaction_list, workchain_dict) + + assert len(reac_list) == len(reaction_list) + for i, reaction in enumerate(reac_list): + assert reaction == best_order[i] + # @pytest.mark.skip(reason="There seems to be now way to add outputs to CalcJobNode") diff --git a/tests/tools/test_common_fleur_wf_util.py b/tests/tools/test_common_fleur_wf_util.py index 797f9630a..e375dff32 100644 --- a/tests/tools/test_common_fleur_wf_util.py +++ b/tests/tools/test_common_fleur_wf_util.py @@ -27,6 +27,12 @@ def test_get_natoms_element_Be2W(): assert get_natoms_element('Be2W') == {'Be': 2, 'W': 1} +def test_convert_frac_formula_BeW(): + from aiida_fleur.tools.common_fleur_wf_util import convert_frac_formula + + assert convert_frac_formula('Be0.5W0.5') == 'BeW' + + def test_ucell_to_atompr(): from aiida_fleur.tools.common_fleur_wf_util import ucell_to_atompr @@ -60,12 +66,6 @@ def test_get_atomprocent_Be24W2(): assert get_atomprocent('Be24W2') == {'Be': 24. / 26., 'W': 2. / 26.} -#@pytest.mark.skip(reason='The function is not implemented') -#def test_get_weight_procent(): -# from aiida_fleur.tools.common_fleur_wf_util import get_weight_procent -# pass - - def test_determine_formation_energy(): from aiida_fleur.tools.common_fleur_wf_util import determine_formation_energy @@ -77,9 +77,15 @@ def test_determine_formation_energy(): assert form_en_dict == form_en_dict_exp -@pytest.mark.skip(reason='Test is not implemented') def test_determine_convex_hull(): from aiida_fleur.tools.common_fleur_wf_util import determine_convex_hull + from pyhull.convex_hull import ConvexHull + + formation_en_grid = [[0.5, -1.0], [0.5, -0.5]] + + hull = determine_convex_hull(formation_en_grid) + + assert isinstance(hull, ConvexHull) def test_inpgen_dict_set_mesh(generate_kpoints_mesh): @@ -125,10 +131,20 @@ def test_convert_eq_to_dict(): assert convert_eq_to_dict('1*Be12Ti->10*Be+1*Be2Ti+5*Be') == res_dict -@pytest.mark.skip(reason='Test is not implemented') def test_get_enhalpy_of_equation(): from aiida_fleur.tools.common_fleur_wf_util import get_enhalpy_of_equation + reaction_list = [ + '1*Be12W->1*Be12W', '2*Be12W->1*Be2W+1*Be22W', '11*Be12W->5*W+6*Be22W', '1*Be12W->12*Be+1*W', + '1*Be12W->1*Be2W+10*Be' + ] + formenergydict = {'Be12W': -0.21, 'Be2W': -0.3, 'Be22W': -0.1, 'W': 0.0, 'Be': 0.0} + results = [0.0, -0.019999999999999962, -1.71, -0.21, 0.09] + + for i, reaction in enumerate(reaction_list): + result = get_enhalpy_of_equation(reaction, formenergydict) + assert result == results[i] + @pytest.mark.parametrize('test_input,expected', [('C7H16+O2 -> CO2+H2O', '1*C7H16+11*O2 ->7* CO2+8*H2O'), ('Be12W->Be2W+W+Be', None), ('Be12WO->Be2WO+W+Be+O2', None), @@ -138,6 +154,20 @@ def test_balance_equation(test_input, expected): assert balance_equation(test_input) == expected -@pytest.mark.skip(reason='Test is not implemented') def test_check_eos_energies(): from aiida_fleur.tools.common_fleur_wf_util import check_eos_energies + + energylist = [-1, -2, -3, -2, -4, -3, -2, -1] + abnormality, abnormalityindexlist = check_eos_energies(energylist) + assert abnormality + assert abnormalityindexlist == [3] + + energylist = [-1, -2, -3, -2, -2, -3, -2, -1] + abnormality, abnormalityindexlist = check_eos_energies(energylist) + assert not abnormality + assert abnormalityindexlist == [] + + energylist = [-1, -2, -3, -4, -5, -3, -2, -1] + abnormality, abnormalityindexlist = check_eos_energies(energylist) + assert not abnormality + assert abnormalityindexlist == [] From 6f31e96a7464ad996530856b2e3e0780ac790b49 Mon Sep 17 00:00:00 2001 From: broeder-j Date: Mon, 7 Dec 2020 17:07:34 +0100 Subject: [PATCH 35/53] Add tests for merge_parameter, atoms lists --- aiida_fleur/tools/merge_parameter.py | 10 +- tests/tools/test_merge_parameter.py | 257 +++++++++++++++++++++++++++ 2 files changed, 265 insertions(+), 2 deletions(-) create mode 100644 tests/tools/test_merge_parameter.py diff --git a/aiida_fleur/tools/merge_parameter.py b/aiida_fleur/tools/merge_parameter.py index 8e6ce1001..6bebdcdd6 100644 --- a/aiida_fleur/tools/merge_parameter.py +++ b/aiida_fleur/tools/merge_parameter.py @@ -13,8 +13,6 @@ This module, contains a method to merge Dict nodes used by the FLEUR inpgen. This might also be of interest for other all-electron codes """ -# TODO this should be made an inline calculation or calcfunction to -# keep the proverance! # Shall we allow for a python dictionary also instead of forcing paramteraData? # but then we can not keep the provenace... @@ -43,6 +41,8 @@ def merge_parameter(Dict1, Dict2, overwrite=True, merge=True): :param merge: bool, default True returns: AiiDA Dict Node + + #TODO be more carefull how to merge ids in atom namelists, i.e species labels """ from aiida.common.exceptions import InputValidationError @@ -65,6 +65,9 @@ def merge_parameter(Dict1, Dict2, overwrite=True, merge=True): dict1 = Dict1.get_dict() dict2 = Dict2.get_dict() + if dict1 == dict2: + return Dict(dict=dict1) + for key in list(dict1.keys()): if 'atom' in key: val = dict1.pop(key) @@ -148,6 +151,8 @@ def merge_parameters_wf(*Dicts, overwrite=Bool(True)): return paremeter_data_new ''' +''' +#TODO this has to moved into cmdline if __name__ == '__main__': import argparse #Dict = DataFactory('dict') @@ -162,3 +167,4 @@ def merge_parameters_wf(*Dicts, overwrite=Bool(True)): required=False) args = parser.parse_args() merge_parameter(Dict1=args.para1, Dict2=args.para1, overwrite=args.overwrite) + ''' diff --git a/tests/tools/test_merge_parameter.py b/tests/tools/test_merge_parameter.py new file mode 100644 index 000000000..9fb0e5c7f --- /dev/null +++ b/tests/tools/test_merge_parameter.py @@ -0,0 +1,257 @@ +# -*- 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/ # +############################################################################### +'''Contains tests merge_parameters.''' +import pytest +from aiida import orm + +PARAMETERS1 = { + 'atom': { + 'element': 'Si', + 'rmt': 2.1, + 'jri': 981, + 'lmax': 12, + 'lnonsph': 6 + }, #'econfig': '[He] 2s2 2p6 | 3s2 3p2', 'lo': ''}, + 'comp': { + 'kmax': 5.0, + }, + 'kpt': { + 'div1': 17, + 'tkb': 0.0005 + } +} +PARAMETERS2 = { + 'atom1': { + 'element': 'Si', + 'id': 16.1, + 'rmt': 2.1, + 'jri': 981, + 'lmax': 12, + 'lnonsph': 6 + }, #'econfig': '[He] 2s2 2p6 | 3s2 3p2', 'lo': ''}, + 'atom2': { + 'element': 'Si', + 'z': 16, + 'id': 16.4, + 'rmt': 2.1, + 'jri': 981, + 'lmax': 12, + 'lnonsph': 6 + }, + 'comp': { + 'kmax': 5.0, + } +} +PARAMETERS3 = PARAMETERS1.copy() +PARAMETERS3['atom']['element'] = 'Fe' + + +def test_merge_parameter(): + """Test if merge_parameter merges atom keys in dicts right + """ + from aiida_fleur.tools.merge_parameter import merge_parameter + from aiida.common.exceptions import InputValidationError + + dict1 = orm.Dict(dict=PARAMETERS1).store() + dict2 = orm.Dict(dict=PARAMETERS2).store() + dict3 = orm.Dict(dict=PARAMETERS3).store() + # otherwise we we can still change the dicts and therefore the PARAMETERS + + result = merge_parameter(dict2, dict2) + assert isinstance(result, orm.Dict) + assert result.get_dict() == PARAMETERS2 + + res_exp = { + 'kpt': { + 'tkb': 0.0005, + 'div1': 17 + }, + 'comp': { + 'kmax': 5.0 + }, + 'atom0': { + 'jri': 981, + 'rmt': 2.1, + 'lmax': 12, + 'element': 'Fe', + 'lnonsph': 6 + }, + 'atom1': { + 'id': 16.1, + 'jri': 981, + 'rmt': 2.1, + 'lmax': 12, + 'element': 'Si', + 'lnonsph': 6 + }, + 'atom2': { + 'z': 16, + 'id': 16.4, + 'jri': 981, + 'rmt': 2.1, + 'lmax': 12, + 'element': 'Si', + 'lnonsph': 6 + } + } + + result1 = merge_parameter(dict1, dict2) + assert isinstance(result1, orm.Dict) + assert result1.get_dict() == res_exp + + res_exp = { + 'kpt': { + 'tkb': 0.0005, + 'div1': 17 + }, + 'atom': { + 'jri': 981, + 'rmt': 2.1, + 'lmax': 12, + 'element': 'Fe', + 'lnonsph': 6 + }, + 'comp': { + 'kmax': 5.0 + } + } + result2 = merge_parameter(dict1, dict3) + assert isinstance(result2, orm.Dict) + assert result2.get_dict() == res_exp + + # wrong input + with pytest.raises(InputValidationError): + merge_parameter(dict2, 'string') + with pytest.raises(InputValidationError): + merge_parameter(123123, dict2) + + +def test_merge_parameters(): + """Test if merge_parameters works for a given set + """ + from aiida_fleur.tools.merge_parameter import merge_parameters + + dict1 = orm.Dict(dict=PARAMETERS1).store() + dict2 = orm.Dict(dict=PARAMETERS2).store() + dict3 = orm.Dict(dict=PARAMETERS3).store() + + # overwrite seems broken... + res_exp = { + 'kpt': { + 'tkb': 0.0005, + 'div1': 17 + }, + 'comp': { + 'kmax': 5.0 + }, + 'atom0': { + 'jri': 981, + 'rmt': 2.1, + 'lmax': 12, + 'element': 'Fe', + 'lnonsph': 6 + }, + 'atom1': { + 'jri': 981, + 'rmt': 2.1, + 'lmax': 12, + 'element': 'Fe', + 'lnonsph': 6 + } + } + result1 = merge_parameters([dict1, dict1], overwrite=False) + assert isinstance(result1, orm.Dict) + assert result1.get_dict() == res_exp + + res_exp = { + 'kpt': { + 'tkb': 0.0005, + 'div1': 17 + }, + 'comp': { + 'kmax': 5.0 + }, + 'atom0': { + 'jri': 981, + 'rmt': 2.1, + 'lmax': 12, + 'element': 'Fe', + 'lnonsph': 6 + }, + 'atom1': { + 'jri': 981, + 'rmt': 2.1, + 'lmax': 12, + 'element': 'Fe', + 'lnonsph': 6 + } + } + result2 = merge_parameters([dict1, dict1], overwrite=True) + assert isinstance(result2, orm.Dict) + assert result2.get_dict() == res_exp + + res_exp = { + 'kpt': { + 'tkb': 0.0005, + 'div1': 17 + }, + 'comp': { + 'kmax': 5.0 + }, + 'atom0': { + 'jri': 981, + 'rmt': 2.1, + 'lmax': 12, + 'element': 'Fe', + 'lnonsph': 6 + }, + 'atom1': { + 'id': 16.1, + 'jri': 981, + 'rmt': 2.1, + 'lmax': 12, + 'element': 'Si', + 'lnonsph': 6 + }, + 'atom2': { + 'z': 16, + 'id': 16.4, + 'jri': 981, + 'rmt': 2.1, + 'lmax': 12, + 'element': 'Si', + 'lnonsph': 6 + }, + 'atom3': { + 'jri': 981, + 'rmt': 2.1, + 'lmax': 12, + 'element': 'Fe', + 'lnonsph': 6 + } + } + result3 = merge_parameters([dict1, dict2, dict3]) + assert isinstance(result3, orm.Dict) + assert result3.get_dict() == res_exp + + +def test_merge_parameter_cf(): + """Test calcfunction of merge_parameter + """ + from aiida_fleur.tools.merge_parameter import merge_parameter_cf + + dict1 = orm.Dict(dict=PARAMETERS1) + + result = merge_parameter_cf(dict1, dict1) + assert isinstance(result, orm.Dict) + assert result.get_dict() == PARAMETERS1 + assert result.is_stored From 4ae2641f6a5b017812049865bdcba0408035d15e Mon Sep 17 00:00:00 2001 From: broeder-j Date: Mon, 7 Dec 2020 22:59:55 +0100 Subject: [PATCH 36/53] Implement relax type None in relax wc, which will skip relaxation --- aiida_fleur/workflows/relax.py | 45 ++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/aiida_fleur/workflows/relax.py b/aiida_fleur/workflows/relax.py index 718f67af5..610375d23 100644 --- a/aiida_fleur/workflows/relax.py +++ b/aiida_fleur/workflows/relax.py @@ -45,7 +45,9 @@ 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, '49' is reserved + 'relaxation_type': 'atoms' # others include None and maybe in the future volume + # None would run an scf only } _default_options = FleurScfWorkChain._default_options @@ -64,13 +66,11 @@ def define(cls, spec): spec.outline( cls.start, - cls.converge_scf, - cls.check_failure, - while_(cls.condition)( + if_(cls.should_relax)(cls.converge_scf, cls.check_failure, while_(cls.condition)( cls.generate_new_fleurinp, cls.converge_scf, cls.check_failure, - ), + )), cls.get_results_relax, if_(cls.should_run_final_scf)(cls.run_final_scf, cls.get_results_final_scf), cls.return_results, @@ -169,6 +169,19 @@ def start(self): self.report('Error: Wrong input: inpgen missing for final scf.') return self.exit_codes.ERROR_INPGEN_MISSING + def should_relax(self): + """ + Should we run a relaxation or only a final scf + This allows to call the workchain to run an scf only and makes + logic of other higher workflows a lot easier + """ + relaxtype = self.ctx.wf_dict.get('relaxation_type', 'atoms') + if relaxtype is None: + self.ctx.reached_relax = True + return False + else: + return True + def converge_scf(self): """ Submits :class:`aiida_fleur.workflows.scf.FleurScfWorkChain`. @@ -436,6 +449,24 @@ def get_results_relax(self): Creates a new structure data node which is an optimized structure. """ + + if self.ctx.wf_dict.get('relaxation_type', 'atoms') is None: + input_scf = AttributeDict(self.exposed_inputs(FleurScfWorkChain, namespace='scf')) + if 'structure' in input_scf: + structure = input_scf.structure + elif 'fleurinp' in input_scf: + structure = input_scf.fleurinp.get_structuredata_ncf() + 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 + + return + try: relax_out = self.ctx.scf_res.outputs.last_fleur_calc_output except NotExistent: @@ -508,6 +539,10 @@ def get_results_final_scf(self): self.ctx.total_energy_last = total_energy self.ctx.total_energy_units = total_energy_units + if self.ctx.wf_dict.get('relaxation_type', 'atoms') is None: + # we need this for run through + self.ctx.scf_res = self.ctx.scf_final_res + #if jspin ==2 try: total_mag = scf_out_d['total_magnetic_moment_cell'] From 0dca0f7530282e36be8770f474140e194e690bdc Mon Sep 17 00:00:00 2001 From: broeder-j Date: Tue, 8 Dec 2020 15:55:30 +0100 Subject: [PATCH 37/53] Move set_kpoints of fleurinp to fleurimpmodifier also add some further tests for it. For the new fleur version >= 32 this prob has to be overworked, to account for the new kpoint features of fleur. --- aiida_fleur/data/fleurinp.py | 102 -------------------------- aiida_fleur/data/fleurinpmodifier.py | 103 +++++++++++++++++++++++---- tests/data/test_fleurinp.py | 19 ++--- tests/data/test_fleurinpmodifier.py | 72 +++++++++++++++++-- 4 files changed, 169 insertions(+), 127 deletions(-) diff --git a/aiida_fleur/data/fleurinp.py b/aiida_fleur/data/fleurinp.py index 627b563da..738351005 100644 --- a/aiida_fleur/data/fleurinp.py +++ b/aiida_fleur/data/fleurinp.py @@ -943,108 +943,6 @@ def get_parameterdata(fleurinp): return fleurinp.get_parameterdata_ncf() - @cf - def set_kpointsdata(self, fleurinp_orgi, KpointsDataNode): - """ - This calc function writes the all the 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. The output :class:`~aiida_fleur.data.fleurinp.FleurinpData` is stored in - the database. - - Currently it is the users resposibility to provide a full - :class:`~aiida.orm.KpointsData` node with weights. - - :param KpointsDataNode: :class:`~aiida.orm.KpointsData` node to be written into ``inp.xml`` - :returns: modified :class:`~aiida_fleur.data.fleurinp.FleurinpData` node - """ - from aiida.orm import KpointsData - #from aiida.common.exceptions import InputValidationError - - # all hardcoded xpaths used and attributes names: - fleurinp = fleurinp_orgi.copy() - 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' - - #### method layout: ##### - # Check if Kpoints node - # get kpoints, weights, inp.xml - - # replace the kpoints tag.(delete old write new) - # kpoint list posScale, wightScale, count - # - # 17.000000 0.000000 0.000000 - - # add new inp.xml to fleurinpdata - - if not isinstance(KpointsDataNode, KpointsData): - raise InputValidationError('The node given is not a valid KpointsData node.') - - if 'inp.xml' in fleurinp.files: - # read in inpxml - if fleurinp._schema_file_path: # Schema there, parse with schema - xmlschema_doc = etree.parse(fleurinp._schema_file_path) - xmlschema = etree.XMLSchema(xmlschema_doc) - parser = etree.XMLParser(schema=xmlschema, - attribute_defaults=True, - remove_blank_text=True, - encoding='utf-8') - with fleurinp.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') - parser = etree.XMLParser(attribute_defaults=True, remove_blank_text=True, encoding='utf-8') - with self.open(path='inp.xml', mode='r') as inpxmlfile: - tree = etree.parse(inpxmlfile, parser) - #root = tree.getroot() - else: - raise InputValidationError('No inp.xml file yet specified, to add kpoints to.') - - #cell_k = KpointsDataNode.cell - - # TODO: shall we check if cell is the same as cell from structure? - # or is that to narrow? - - 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 - - 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(tree, kpointlist_xpath, new_kpo) - # use _write_new_fleur_xmlinp_file(fleurinp, inp_file_xmltree, fleur_change_dic): - - # TODO this should be sourced out to other methods. - # somehow directly writing inp.xml does not work, create new one - inpxmlfile = os.path.join(fleurinp._get_folder_pathsubfolder.abspath, 'temp_inp.xml') - - # write new inp.xml, schema evaluation will be done when the file gets added - new_tree.write(inpxmlfile, pretty_print=True) - print(('wrote tree to' + str(inpxmlfile))) - - # delete old inp.xml file, not needed anymore - # TODO maybe do some checks before - fleurinp.del_file('inp.xml') - - # now the new inp.xml file is added and with that the inpxml_dict will - # be overwritten, and the file validated - fleurinp._add_path(str(inpxmlfile), 'inp.xml') - - # remove temporary inp.xml file - os.remove(inpxmlfile) - - return fleurinp - def get_tag(self, xpath): """ Tries to evaluate an xpath expression for ``inp.xml`` file. If it fails it logs it. diff --git a/aiida_fleur/data/fleurinpmodifier.py b/aiida_fleur/data/fleurinpmodifier.py index c2fd569e8..98066bbc8 100644 --- a/aiida_fleur/data/fleurinpmodifier.py +++ b/aiida_fleur/data/fleurinpmodifier.py @@ -23,6 +23,7 @@ from lxml import etree 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 @@ -40,6 +41,7 @@ def __init__(self, original): self._original = original self._tasks = [] + self._other_nodes = {} @staticmethod def apply_modifications(fleurinp_tree_copy, nmmp_lines_copy, modification_tasks, schema_tree=None): @@ -153,6 +155,10 @@ 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,\ @@ -179,8 +185,9 @@ def set_nmmpmat1(fleurinp_tree_copy, nmmp_lines_copy, species_name, orbital,\ '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, + 'set_nmmpmat': set_nmmpmat1 } workingtree = fleurinp_tree_copy @@ -237,8 +244,10 @@ def get_avail_actions(self): '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_nmmpmat': self.set_nmmpmat } return outside_actions @@ -455,6 +464,22 @@ def set_kpath(self, kpath, count, gamma='F'): """ self._tasks.append(('set_kpath', kpath, count, gamma)) + def set_kpointsdata(self, kpointsdata_uuid): + """ + Appends a :py:func:`set_kpointsdata_f()` to + the list of tasks that will be done on the FleurinpData. + + :param kpointsdata_uuid: an aiida.orm.KpointsData or node uuid, + since the node is self cannot be be serialized in tasks. + """ + 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)) + def add_num_to_att(self, xpathn, attributename, set_val, mode='abs', occ=None): """ Appends a :py:func:`~aiida_fleur.tools.xml_util.add_num_to_att()` to @@ -552,16 +577,20 @@ def freeze(self): :return: stored :class:`~aiida_fleur.data.fleurinp.FleurinpData` with applied changes """ - modifications = DataFactory('dict')(dict={'tasks': self._tasks}) + modifications = orm.Dict(dict={'tasks': self._tasks}) + #print(self._tasks) modifications.description = 'Fleurinpmodifier Tasks and inputs of these.' modifications.label = 'Fleurinpdata modifications' # This runs in a inline calculation to keep provenance - out = modify_fleurinpdata(original=self._original, - modifications=modifications, - metadata={ - 'label': 'fleurinp modifier', - 'description': 'This calcfunction modified an Fleurinpdataobject' - }) + print(self._original) + inputs = dict(original=self._original, + modifications=modifications, + metadata={ + 'label': 'fleurinp modifier', + 'description': 'This calcfunction modified an Fleurinpdataobject' + }, + **self._other_nodes) + out = modify_fleurinpdata(**inputs) return out def undo(self, revert_all=False): @@ -574,20 +603,21 @@ def undo(self, revert_all=False): self._tasks = [] else: if self._tasks: - self._tasks.pop() + task = self._tasks.pop() + #TODO delete nodes from other nodes #del self._tasks[-1] return self._tasks @cf -def modify_fleurinpdata(original, modifications): +def modify_fleurinpdata(original, modifications, **kwargs): """ A CalcFunction that performs the modification of the given FleurinpData and stores the result in a database. :param original: a FleurinpData to be modified :param modifications: a python dictionary of modifications in the form of {'task': ...} - + :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 """ @@ -645,3 +675,52 @@ def modify_fleurinpdata(original, modifications): 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`` + :returns: 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/tests/data/test_fleurinp.py b/tests/data/test_fleurinp.py index d7b6b9f1b..0f6ec26bb 100644 --- a/tests/data/test_fleurinp.py +++ b/tests/data/test_fleurinp.py @@ -117,10 +117,10 @@ def test_fleurinp_structuredata_extraction(create_fleurinp, inpxmlfilepath): 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 - # Therefore this test might let two much through + # # What todo here, may test inpxml are with latnam definded, + # # which does not work here. + # # But if something else fails also None return. T + # # Therefore this test might let two much through # Input Modification tests @@ -145,8 +145,9 @@ def test_fleurinp_single_value_modification(create_fleurinp, inpxmlfilepath): @pytest.mark.parametrize('inpxmlfilepath', inpxmlfilelist) -def test_fleurinp_first_species_modification(create_fleurinp, inpxmlfilepath): - """ - Decrease the rmt of the first species by 10%, check if rmt was set - """ - return +def test_get_tag(create_fleurinp, inpxmlfilepath): + + fleurinp_tmp = create_fleurinp(inpxmlfilepath) + tag = fleurinp_tmp.get_tag('/fleurInput/atomSpecies/species') + + assert tag != [] diff --git a/tests/data/test_fleurinpmodifier.py b/tests/data/test_fleurinpmodifier.py index 07c294bab..270884d7b 100644 --- a/tests/data/test_fleurinpmodifier.py +++ b/tests/data/test_fleurinpmodifier.py @@ -14,6 +14,7 @@ from __future__ import absolute_import import os import pytest +from aiida_fleur.data.fleurinpmodifier import FleurinpModifier # Collect the input files file_path1 = '../files/inpxml/FePt/inp.xml' @@ -23,7 +24,7 @@ def test_fleurinp_modifier1(create_fleurinp): - from aiida_fleur.data.fleurinpmodifier import FleurinpModifier + """Tests if fleurinp_modifier with various modifations on species""" fleurinp_tmp = create_fleurinp(inpxmlfilefolder) fm = FleurinpModifier(fleurinp_tmp) @@ -43,15 +44,25 @@ def test_fleurinp_modifier1(create_fleurinp): fm.show(validate=True) fm.freeze() + fm = FleurinpModifier(fleurinp_tmp) + fm.set_inpchanges({'dos': True, 'Kmax': 3.9}) + fm.undo(revert_all=True) + changes = fm.changes() + assert changes == [] + def test_fleurinp_modifier2(create_fleurinp, inpxml_etree): - from aiida_fleur.data.fleurinpmodifier import FleurinpModifier + """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) fm = FleurinpModifier(fleurinp_tmp) + actions = fm.get_avail_actions() + 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) @@ -66,6 +77,11 @@ def test_fleurinp_modifier2(create_fleurinp, inpxml_etree): 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') + + 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.set_species1 fm.show() @@ -76,12 +92,60 @@ def test_fleurinp_modifier2(create_fleurinp, inpxml_etree): inpxmlfilefolder2 = os.path.abspath(os.path.join(inpxmlfilefolder2, file_path2)) -def test_fleurinp_modifier3(create_fleurinp): - from aiida_fleur.data.fleurinpmodifier import FleurinpModifier +def test_fleurinp_modifier_set_nmmpmat(create_fleurinp): + """Tests if set_nmmpmat works on fleurinp modifier works, with right interface""" 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('As-2', orbital=1, spin=1, denmat=[[1, -2, 3], [4, -5, 6], [7, -8, 9]]) + + # Does not validate + # Found invalid diagonal element for species Ga-1, spin 1 and l=2 + with pytest.raises(ValueError): + fm.show(validate=True, display=False) new_fleurinp = fm.freeze() assert 'n_mmp_mat' 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 + + fleurinp_tmp = create_fleurinp(inpxmlfilefolder) + fleurinp_tmp.store() # needed? + struc = fleurinp_tmp.get_structuredata_ncf() + + kps = KpointsData() + kps.set_cell(struc.cell) + kps.pbc = struc.pbc + kpoints_pos = [[0.0, 0.0, 0.0], [0.0, 0.5, 0.0], [0.5, 0.0, 0.0], [0.5, 0.0, 0.5], [0.5, 0.5, 0.5], [1.0, 1.0, 1.0]] + kpoints_weight = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0] + # Fleur renormalizes + kps.set_kpoints(kpoints_pos, cartesian=False, weights=kpoints_weight) + + kps.store() # needed, because node has to be loaded... + #print(fleurinp_tmp) + fm = FleurinpModifier(fleurinp_tmp) + fm.set_kpointsdata(kps) + + fm.show(validate=True, display=False) + fm.freeze() + + # check if kpoint node is input into modification + # uuid of node show also work + fm = FleurinpModifier(fleurinp_tmp) + fm.set_kpointsdata(kps.uuid) + fm.freeze() + + +def test_fleurinpmodifier_error_messages(create_fleurinp): + """Test error interface of fleurinpmodifier""" + fleurinp_tmp = create_fleurinp(inpxmlfilefolder) + + fm = FleurinpModifier(fleurinp_tmp) + fm._tasks.append(('not_existent', 1, 2, 3)) # task does not exists. + with pytest.raises(ValueError): + fm.freeze() + + fm = FleurinpModifier(fleurinp_tmp) From 75e82dcc96c5ead99f841b062b61c9541928d08e Mon Sep 17 00:00:00 2001 From: broeder-j Date: Thu, 10 Dec 2020 13:36:11 +0100 Subject: [PATCH 38/53] Add significant figures to inpgen settings --- aiida_fleur/calculation/fleurinputgen.py | 34 +++++++++++++++--------- aiida_fleur/tools/common_fleur_wf.py | 5 ++-- aiida_fleur/workflows/scf.py | 15 +++++++++-- 3 files changed, 37 insertions(+), 17 deletions(-) diff --git a/aiida_fleur/calculation/fleurinputgen.py b/aiida_fleur/calculation/fleurinputgen.py index e3a2d5f40..62102006b 100644 --- a/aiida_fleur/calculation/fleurinputgen.py +++ b/aiida_fleur/calculation/fleurinputgen.py @@ -37,7 +37,7 @@ class FleurinputgenCalculation(CalcJob): For more information about produced files and the FLEUR-code family, go to http://www.flapw.de/. """ - __version__ = '1.2.1' + __version__ = '1.2.2' # Default input and output files _INPUT_FILE = 'aiida.in' # will be shown with inputcat @@ -51,7 +51,10 @@ class FleurinputgenCalculation(CalcJob): _ERROR_FILE_NAME = 'out.error' _STRUCT_FILE_NAME = 'struct.xsf' - _settings_keys = ['additional_retrieve_list', 'remove_from_retrieve_list', 'cmdline'] + _settings_keys = [ + 'additional_retrieve_list', 'remove_from_retrieve_list', 'cmdline', 'significant_figures_cell', + 'significant_figures_positions' + ] # TODO switch all these to init_internal_params? _OUTPUT_SUBFOLDER = './fleur_inp_out/' _PREFIX = 'aiida' @@ -136,7 +139,7 @@ def define(cls, spec): '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.')) - spec.exit_code(309, 'ERROR_FLEURINPDATE_NOT_VALID', message='During parsing: FleurinpData failed validation.') + spec.exit_code(309, 'ERROR_FLEURINPDATA_NOT_VALID', message='During parsing: FleurinpData failed validation.') def prepare_for_submission(self, folder): """ @@ -301,14 +304,18 @@ def prepare_for_submission(self, folder): scaling_factor_card = '' cell_parameters_card = '' - + # We allow to set the significant figures format, because sometimes + # inpgen has numerical problems which are not there with less precise formatting + sf_c = str(settings_dict.get('significant_figures_cell', 9)) + sf_p = str(settings_dict.get('significant_figure_positions', 10)) if not own_lattice: cell = structure.cell for vector in cell: scaled = [a * scaling_pos for a in vector] # scaling_pos=1./bohr_to_ang - cell_parameters_card += ('{0:18.9f} {1:18.9f} {2:18.9f}' '\n'.format(scaled[0], scaled[1], scaled[2])) - scaling_factor_card += ('{0:18.9f} {1:18.9f} {2:18.9f}' - '\n'.format(scaling_factors[0], scaling_factors[1], scaling_factors[2])) + reg_string = '{0:18.' + sf_c + 'f} {1:18.' + sf_c + 'f} {2:18.' + sf_c + 'f}\n' + cell_parameters_card += (reg_string.format(scaled[0], scaled[1], scaled[2])) + reg_string = '{0:18.' + sf_c + 'f} {1:18.' + sf_c + 'f} {2:18.' + sf_c + 'f}\n' + scaling_factor_card += (reg_string.format(scaling_factors[0], scaling_factors[1], scaling_factors[2])) #### ATOMIC_POSITIONS #### @@ -350,6 +357,7 @@ def prepare_for_submission(self, folder): if site_symbol != kind_name: # This is an important fact, if user renames it becomes a new specie! try: + # Kind names can be more then numbers now, this might need to be reworked head = kind_name.rstrip('0123456789') kind_namet = int(kind_name[len(head):]) if int(kind_name[len(head)]) > 4: @@ -359,13 +367,13 @@ def prepare_for_submission(self, folder): else: atomic_number_name = '{}.{}'.format(atomic_number, kind_namet) # append a label to the detached atom - atomic_positions_card_listtmp.append(' {0:7} {1:18.10f} {2:18.10f} {3:18.10f} {4}' - '\n'.format(atomic_number_name, vector_rel[0], vector_rel[1], - vector_rel[2], kind_namet)) + reg_string = ' {0:7} {1:18.' + sf_p + 'f} {2:18.' + sf_p + 'f} {3:18.' + sf_p + 'f} {4}\n' + atomic_positions_card_listtmp.append( + reg_string.format(atomic_number_name, vector_rel[0], vector_rel[1], vector_rel[2], kind_namet)) else: - atomic_positions_card_listtmp.append(' {0:7} {1:18.10f} {2:18.10f} {3:18.10f}' - '\n'.format(atomic_number_name, vector_rel[0], vector_rel[1], - vector_rel[2])) + reg_string = ' {0:7} {1:18.' + sf_p + 'f} {2:18.' + sf_p + 'f} {3:18.' + sf_p + 'f}\n' + atomic_positions_card_listtmp.append( + reg_string.format(atomic_number_name, vector_rel[0], vector_rel[1], vector_rel[2])) # TODO check format # we write it later, since we do not know what natoms is before the loop... atomic_positions_card_list.append(' {0:3}\n'.format(natoms)) diff --git a/aiida_fleur/tools/common_fleur_wf.py b/aiida_fleur/tools/common_fleur_wf.py index 7fc82206a..5b874b1d3 100644 --- a/aiida_fleur/tools/common_fleur_wf.py +++ b/aiida_fleur/tools/common_fleur_wf.py @@ -139,7 +139,7 @@ def get_inputs_fleur(code, return inputs -def get_inputs_inpgen(structure, inpgencode, options, label='', description='', params=None, **kwargs): +def get_inputs_inpgen(structure, inpgencode, options, label='', description='', settings=None, params=None, **kwargs): ''' Assembles the input dictionary for Fleur Calculation. @@ -167,7 +167,8 @@ def get_inputs_inpgen(structure, inpgencode, options, label='', description='', inputs.code = inpgencode if params: inputs.parameters = params - + if settings: + inputs.settings = settings if description: inputs.metadata.description = description else: diff --git a/aiida_fleur/workflows/scf.py b/aiida_fleur/workflows/scf.py index f0b18a253..5d606fe94 100644 --- a/aiida_fleur/workflows/scf.py +++ b/aiida_fleur/workflows/scf.py @@ -107,7 +107,7 @@ def define(cls, spec): spec.input('remote_data', valid_type=RemoteData, required=False) spec.input('options', valid_type=Dict, required=False) spec.input('settings', valid_type=Dict, required=False) - + spec.input('settings_inpgen', valid_type=Dict, required=False) spec.outline(cls.start, cls.validate_input, if_(cls.fleurinpgen_needed)(cls.run_fleurinpgen), cls.run_fleur, cls.inspect_fleur, cls.get_res, while_(cls.condition)(cls.run_fleur, cls.inspect_fleur, cls.get_res), cls.return_results) @@ -304,6 +304,11 @@ def run_fleurinpgen(self): else: params = None + if 'settings_inpgen' in self.inputs: + settings = self.inputs.settings_inpgen + else: + settings = None + # If given kpt_dist has prio over given calc_parameters kpt_dist = self.ctx.wf_dict.get('kpoints_distance', None) if kpt_dist is not None: @@ -330,7 +335,13 @@ def run_fleurinpgen(self): 'queue_name': self.ctx.options.get('queue_name', '') } - inputs_build = get_inputs_inpgen(structure, inpgencode, options, label, description, params=params) + inputs_build = get_inputs_inpgen(structure, + inpgencode, + options, + label, + description, + settings=settings, + params=params) # Launch inpgen self.report('INFO: run inpgen') From 780fe63c89dbdb016dc2b2cb675f7178520dd560 Mon Sep 17 00:00:00 2001 From: broeder-j Date: Thu, 10 Dec 2020 20:43:27 +0100 Subject: [PATCH 39/53] Fix docs build --- aiida_fleur/data/fleurinp.py | 8 ++++++-- aiida_fleur/data/fleurinpmodifier.py | 10 ++++------ docs/source/user_guide/data/fleurinp_data.rst | 9 ++++----- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/aiida_fleur/data/fleurinp.py b/aiida_fleur/data/fleurinp.py index 738351005..31b10839a 100644 --- a/aiida_fleur/data/fleurinp.py +++ b/aiida_fleur/data/fleurinp.py @@ -431,8 +431,12 @@ def _set_inp_dict(self): parser = etree.XMLParser(attribute_defaults=True, encoding='utf-8') # dtd_validation=True with self.open(path='inp.xml', mode='r') as inpxmlfile: - tree_x = etree.parse(inpxmlfile, parser) - + try: + tree_x = etree.parse(inpxmlfile, parser) + except etree.XMLSyntaxError: + # 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) # 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) diff --git a/aiida_fleur/data/fleurinpmodifier.py b/aiida_fleur/data/fleurinpmodifier.py index 98066bbc8..e24c6be27 100644 --- a/aiida_fleur/data/fleurinpmodifier.py +++ b/aiida_fleur/data/fleurinpmodifier.py @@ -466,11 +466,10 @@ def set_kpath(self, kpath, count, gamma='F'): def set_kpointsdata(self, kpointsdata_uuid): """ - Appends a :py:func:`set_kpointsdata_f()` to + Appends a :py:func:`~aiida_fleur.data.fleurinpmodifier.set_kpointsdata_f()` to the list of tasks that will be done on the FleurinpData. - :param kpointsdata_uuid: an aiida.orm.KpointsData or node uuid, - since the node is self cannot be be serialized in tasks. + :param kpointsdata_uuid: an :class:`aiida.orm.KpointsData` or node uuid, since the node is self cannot be be serialized in tasks. """ from aiida.orm import KpointsData, load_node @@ -680,13 +679,12 @@ def modify_fleurinpdata(original, modifications, **kwargs): 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 + ``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`` - :returns: modified xml tree + :return: modified xml tree """ # TODO: check on weights, # also fleur allows for several kpoint sets, lists, paths and meshes, diff --git a/docs/source/user_guide/data/fleurinp_data.rst b/docs/source/user_guide/data/fleurinp_data.rst index ac5a8fdaf..5a17e4271 100644 --- a/docs/source/user_guide/data/fleurinp_data.rst +++ b/docs/source/user_guide/data/fleurinp_data.rst @@ -111,11 +111,10 @@ User Methods * :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.fleurinp.FleurinpData.set_kpointsdata()` - - A CalcFunction that writes kpoints - of a :py:class:`~aiida.orm.KpointsData` node in the - inp.xml file returns a new - :py:class:`~aiida_fleur.data.fleurinp.FleurinpData` instance. It replaces old kpoints. + * :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: From c011e39cc1ea25188e095e7e07739471b6e75585 Mon Sep 17 00:00:00 2001 From: broeder-j Date: Thu, 10 Dec 2020 21:21:35 +0100 Subject: [PATCH 40/53] Add first full parser tests for inpgen and fleur using data regression --- setup.json | 3 +- tests/conftest.py | 21 +- .../fixtures/fleur/complex_errorout/out.error | 18 + .../fixtures/fleur/complex_errorout/out.xml | 140 + .../fixtures/fleur/complex_errorout/shell.out | 8 + .../fleur/complex_errorout/usage.json | 31 + .../fixtures/fleur/default/JUDFT_WARN_ONLY | 1 + .../fleur/default/_scheduler-stderr.txt | 0 .../fleur/default/_scheduler-stdout.txt | 0 tests/parsers/fixtures/fleur/default/cdn1 | Bin 0 -> 36530 bytes tests/parsers/fixtures/fleur/default/inp.xml | 368 +++ .../fixtures/fleur/default/juDFT_times.json | 434 +++ .../parsers/fixtures/fleur/default/out.error | 12 + tests/parsers/fixtures/fleur/default/out.xml | 1013 +++++++ .../parsers/fixtures/fleur/default/shell.out | 58 + .../fleur/mt_overlap_errorout/out.error | 16 + .../fleur/mt_overlap_errorout/out.xml | 19 + .../fleur/mt_overlap_errorout/relax.xml | 14 + .../fleur/mt_overlap_errorout/shell.out | 6 + tests/parsers/fixtures/fleur/relax/inp.xml | 151 ++ tests/parsers/fixtures/fleur/relax/out.error | 10 + tests/parsers/fixtures/fleur/relax/out.xml | 2317 +++++++++++++++++ tests/parsers/fixtures/fleur/relax/relax.xml | 14 + tests/parsers/fixtures/fleur/relax/shell.out | 214 ++ .../fixtures/inpgen/broken_inpxml/inp.xml | 111 + .../parsers/fixtures/inpgen/broken_inpxml/out | 687 +++++ .../fixtures/inpgen/broken_inpxml/out.error | 0 tests/parsers/fixtures/inpgen/default/inp.xml | 319 +++ tests/parsers/fixtures/inpgen/default/out | 687 +++++ .../parsers/fixtures/inpgen/default/out.error | 0 tests/parsers/fixtures/inpgen/no_inpxml/out | 687 +++++ .../fixtures/inpgen/no_inpxml/out.error | 2 + .../fixtures/inpgen/no_otherfiles/inp.xml | 319 +++ .../fixtures/inpgen/no_otherfiles/out.error | 0 .../fixtures/inpgen/novalid_inpxml/inp.xml | 319 +++ .../fixtures/inpgen/novalid_inpxml/out | 687 +++++ .../fixtures/inpgen/novalid_inpxml/out.error | 0 tests/parsers/test_fleur_parser.py | 191 +- ...st_fleur_parser_MT_overlap_erroroutput.yml | 8 + .../test_fleur_parser_relax.yml | 21 + tests/parsers/test_inpgen_parser.py | 114 +- .../test_inpgen_parser_default.yml | 353 +++ 42 files changed, 9365 insertions(+), 8 deletions(-) create mode 100644 tests/parsers/fixtures/fleur/complex_errorout/out.error create mode 100644 tests/parsers/fixtures/fleur/complex_errorout/out.xml create mode 100644 tests/parsers/fixtures/fleur/complex_errorout/shell.out create mode 100644 tests/parsers/fixtures/fleur/complex_errorout/usage.json create mode 100644 tests/parsers/fixtures/fleur/default/JUDFT_WARN_ONLY create mode 100644 tests/parsers/fixtures/fleur/default/_scheduler-stderr.txt create mode 100644 tests/parsers/fixtures/fleur/default/_scheduler-stdout.txt create mode 100644 tests/parsers/fixtures/fleur/default/cdn1 create mode 100644 tests/parsers/fixtures/fleur/default/inp.xml create mode 100644 tests/parsers/fixtures/fleur/default/juDFT_times.json create mode 100644 tests/parsers/fixtures/fleur/default/out.error create mode 100644 tests/parsers/fixtures/fleur/default/out.xml create mode 100644 tests/parsers/fixtures/fleur/default/shell.out create mode 100644 tests/parsers/fixtures/fleur/mt_overlap_errorout/out.error create mode 100644 tests/parsers/fixtures/fleur/mt_overlap_errorout/out.xml create mode 100644 tests/parsers/fixtures/fleur/mt_overlap_errorout/relax.xml create mode 100644 tests/parsers/fixtures/fleur/mt_overlap_errorout/shell.out create mode 100644 tests/parsers/fixtures/fleur/relax/inp.xml create mode 100644 tests/parsers/fixtures/fleur/relax/out.error create mode 100644 tests/parsers/fixtures/fleur/relax/out.xml create mode 100644 tests/parsers/fixtures/fleur/relax/relax.xml create mode 100644 tests/parsers/fixtures/fleur/relax/shell.out create mode 100644 tests/parsers/fixtures/inpgen/broken_inpxml/inp.xml create mode 100644 tests/parsers/fixtures/inpgen/broken_inpxml/out create mode 100644 tests/parsers/fixtures/inpgen/broken_inpxml/out.error create mode 100644 tests/parsers/fixtures/inpgen/default/inp.xml create mode 100644 tests/parsers/fixtures/inpgen/default/out create mode 100644 tests/parsers/fixtures/inpgen/default/out.error create mode 100644 tests/parsers/fixtures/inpgen/no_inpxml/out create mode 100644 tests/parsers/fixtures/inpgen/no_inpxml/out.error create mode 100644 tests/parsers/fixtures/inpgen/no_otherfiles/inp.xml create mode 100644 tests/parsers/fixtures/inpgen/no_otherfiles/out.error create mode 100644 tests/parsers/fixtures/inpgen/novalid_inpxml/inp.xml create mode 100644 tests/parsers/fixtures/inpgen/novalid_inpxml/out create mode 100644 tests/parsers/fixtures/inpgen/novalid_inpxml/out.error create mode 100644 tests/parsers/test_fleur_parser/test_fleur_parser_MT_overlap_erroroutput.yml create mode 100644 tests/parsers/test_fleur_parser/test_fleur_parser_relax.yml create mode 100644 tests/parsers/test_inpgen_parser/test_inpgen_parser_default.yml diff --git a/setup.json b/setup.json index a9a07007e..b92d1529b 100644 --- a/setup.json +++ b/setup.json @@ -57,7 +57,8 @@ "pytest>=2.9", "pytest-timeout", "pytest-cov>= 2.5.0", - "pgtest" + "pgtest", + "pytest-regressions>=1.0" ] }, "entry_points": { diff --git a/tests/conftest.py b/tests/conftest.py index 9cdef667f..70437ad91 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -84,7 +84,7 @@ def _generate_calc_job(folder, entry_point_name, inputs=None): @pytest.fixture -def generate_calc_job_node(): +def generate_calc_job_node(fixture_localhost): """Fixture to generate a mock `CalcJobNode` for testing parsers.""" def flatten_inputs(inputs, prefix=''): @@ -97,7 +97,13 @@ def flatten_inputs(inputs, prefix=''): flat_inputs.append((prefix + key, value)) return flat_inputs - def _generate_calc_job_node(entry_point_name, computer, test_name=None, inputs=None, attributes=None): + def _generate_calc_job_node(entry_point_name, + computer=None, + test_name=None, + inputs=None, + attributes=None, + store=False, + retrieve_list=None): """Fixture to generate a mock `CalcJobNode` for testing parsers. :param entry_point_name: entry point name of the calculation class @@ -111,6 +117,9 @@ def _generate_calc_job_node(entry_point_name, computer, test_name=None, inputs=N from aiida.common import LinkType from aiida.plugins.entry_point import format_entry_point_string + if computer is None: + computer = fixture_localhost + entry_point = format_entry_point_string('aiida.calculations', entry_point_name) node = orm.CalcJobNode(computer=computer, process_type=entry_point) @@ -121,6 +130,8 @@ def _generate_calc_job_node(entry_point_name, computer, test_name=None, inputs=N node.set_option('withmpi', True) node.set_option('max_wallclock_seconds', 1800) + if retrieve_list is not None: + node.set_attribute('retrieve_list', retrieve_list) if attributes: node.set_attribute_many(attributes) @@ -129,12 +140,12 @@ def _generate_calc_job_node(entry_point_name, computer, test_name=None, inputs=N input_node.store() node.add_incoming(input_node, link_type=LinkType.INPUT_CALC, link_label=link_label) - # node.store() + if store: # needed if test_name is not None + node.store() if test_name is not None: basepath = os.path.dirname(os.path.abspath(__file__)) - filepath = os.path.join(basepath, 'parsers', 'fixtures', entry_point_name[len('quantumespresso.'):], - test_name) + filepath = os.path.join(basepath, 'parsers', 'fixtures', entry_point_name[len('fleur.'):], test_name) retrieved = orm.FolderData() retrieved.put_object_from_tree(filepath) diff --git a/tests/parsers/fixtures/fleur/complex_errorout/out.error b/tests/parsers/fixtures/fleur/complex_errorout/out.error new file mode 100644 index 000000000..cc8ea98a2 --- /dev/null +++ b/tests/parsers/fixtures/fleur/complex_errorout/out.error @@ -0,0 +1,18 @@ +/O warning : failed to load external entity "relax.xml" +**************juDFT-Error***************** +Error message:Allocation of memmory failed for mat datatype +Hint:You probably run out of memory +***************************************** + Last kown location: + Last timer:Setup of H&S matrices + Timerstack: + Timer:eigen + Timer:gen. of hamil. and diag. (total) + Timer:Iteration + Timer:Total Run + ***************************************** +Rank:0 used 2.093GB/ 3412936 kB + % Total % Received % Xferd Average Speed Time Time Time Current + Dload Upload Total Spent Left Speed +100 873 100 101 100 772 257 1966 --:--:-- --:--:-- --:--:-- 1969 +juDFT-STOPPED diff --git a/tests/parsers/fixtures/fleur/complex_errorout/out.xml b/tests/parsers/fixtures/fleur/complex_errorout/out.xml new file mode 100644 index 000000000..4542bb57d --- /dev/null +++ b/tests/parsers/fixtures/fleur/complex_errorout/out.xml @@ -0,0 +1,140 @@ + + + + + + GEN + + + + + + + + + + + A Fleur input generator calculation with aiida + + + + + + + + + + .0000000000 .0000000000 .0000000000 + + + + + + + 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 + + + + + 28.345891875000000 .000000000000000 .000000000000000 + .000000000000000 28.345891875000000 .000000000000000 + .000000000000000 .000000000000000 28.345891875000000 + + + + + + + + + + + + + + + + + + + + + .0000000000 .0000000000 .0000000000 + + + + + -1.000/15.000 .0000000000 -.0266666667 + + + + + 1.000/30.000 .0577350267 -.0266666667 + 1.000/30.000 -.0577350267 -.0266666667 + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.000000 0.000000 0.000000 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/parsers/fixtures/fleur/complex_errorout/shell.out b/tests/parsers/fixtures/fleur/complex_errorout/shell.out new file mode 100644 index 000000000..3d16af8e6 --- /dev/null +++ b/tests/parsers/fixtures/fleur/complex_errorout/shell.out @@ -0,0 +1,8 @@ +iffcluster0105: Using InfiniBand for MPI communication. + Welcome to FLEUR (www.flapw.de) + MaX-Release 4.0 (www.max-centre.eu) + stars are always ordered + -------------------------------------------------------- + Number of OMP-threads: 12 + -------------------------------------------------------- + Usage data send using curl: usage.json diff --git a/tests/parsers/fixtures/fleur/complex_errorout/usage.json b/tests/parsers/fixtures/fleur/complex_errorout/usage.json new file mode 100644 index 000000000..688d6fe67 --- /dev/null +++ b/tests/parsers/fixtures/fleur/complex_errorout/usage.json @@ -0,0 +1,31 @@ +{ + "url":"www.flapw.de/collect.pl", + "calculation-id":"F6674BD6CD51AA09", + "data": { + "githash":"9cddd6d3ed47288c8d096c3f755728090cc6dc36", + "XC-treatment":2, + "MPI-PE":1, + "OMP":12, + "A-Types":3, + "Atoms":4, + "Real":false, + "Spins":1, + "Noco":false, + "SOC":false, + "SpinSpiral":false, + "PlaneWaves":47937, + "LOs":0, + "nkpt":1, + "gpu_per_node":0, + "Runtime":39.00, + "Error":"Allocation of memmory failed for mat datatype", + "cpu_model":"44", + "cpu_modelname":"Intel(R) Xeon(R) CPU X5670 @ 2.93GHz", + "VmPeak":3412936, + "VmSize":2194828, + "VmData":1614740, + "VmStk":350524, + "VmExe":12428, + "VmSwap":0 + } +} diff --git a/tests/parsers/fixtures/fleur/default/JUDFT_WARN_ONLY b/tests/parsers/fixtures/fleur/default/JUDFT_WARN_ONLY new file mode 100644 index 000000000..65c71eb10 --- /dev/null +++ b/tests/parsers/fixtures/fleur/default/JUDFT_WARN_ONLY @@ -0,0 +1 @@ +/n diff --git a/tests/parsers/fixtures/fleur/default/_scheduler-stderr.txt b/tests/parsers/fixtures/fleur/default/_scheduler-stderr.txt new file mode 100644 index 000000000..e69de29bb diff --git a/tests/parsers/fixtures/fleur/default/_scheduler-stdout.txt b/tests/parsers/fixtures/fleur/default/_scheduler-stdout.txt new file mode 100644 index 000000000..e69de29bb diff --git a/tests/parsers/fixtures/fleur/default/cdn1 b/tests/parsers/fixtures/fleur/default/cdn1 new file mode 100644 index 0000000000000000000000000000000000000000..2a3a43854c1b3f90322d09de3c9544cd3a10845c GIT binary patch literal 36530 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 N>$1=!82)>9{1;lB2Sxw@ literal 0 HcmV?d00001 diff --git a/tests/parsers/fixtures/fleur/default/inp.xml b/tests/parsers/fixtures/fleur/default/inp.xml new file mode 100644 index 000000000..4d34c362a --- /dev/null +++ b/tests/parsers/fixtures/fleur/default/inp.xml @@ -0,0 +1,368 @@ + + + + Si, alpha silicon, bulk, delta project + + + + + + + + + + .0000000000 .0000000000 .0000000000 + + + + + + + 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 + + + + + + + + + + + 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 + + + + + .000000000000000 5.167355275200000 5.167355275200000 + 5.167355275200000 .000000000000000 5.167355275200000 + 5.167355275200000 5.167355275200000 .000000000000000 + + + + + + + + + + + + + + + 1.000/8.000 1.000/8.000 1.000/8.000 + -1.000/8.000 -1.000/8.000 -1.000/8.000 + + + + + + + + + + + + + + + + + diff --git a/tests/parsers/fixtures/fleur/default/juDFT_times.json b/tests/parsers/fixtures/fleur/default/juDFT_times.json new file mode 100644 index 000000000..c3ad98c14 --- /dev/null +++ b/tests/parsers/fixtures/fleur/default/juDFT_times.json @@ -0,0 +1,434 @@ +{ + "timername" : "Total Run", + "totaltime" : 4.00000, + "subtimers": [ + { + "timername" : "Initialization", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 1, + "subtimers": [ + { + "timername" : "r_inpXML", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 1 + }, + { + "timername" : "postprocessInput", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 1, + "subtimers": [ + { + "timername" : "strgn", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 1, + "subtimers": [ + { + "timername" : "writeStars", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 1 + } + ] + }, + { + "timername" : "stepf", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 1 + } + ] + } + ] + }, + { + "timername" : "generation of start-density", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 1, + "subtimers": [ + { + "timername" : "qpw_to_nmt", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 1 + }, + { + "timername" : "cdntot", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 1, + "subtimers": [ + { + "timername" : "MT", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 1 + } + ] + } + ] + }, + { + "timername" : "Qfix", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 1 + }, + { + "timername" : "Open file/memory for IO of eig", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 1 + }, + { + "timername" : "Iteration", + "totaltime" : 4.00000, + "mintime" : 0.0000E+00, + "maxtime" : 1.00000, + "ncalls" : 11, + "subtimers": [ + { + "timername" : "generation of potential", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 11, + "subtimers": [ + { + "timername" : "psqpw", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 11 + }, + { + "timername" : "interstitial", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 11 + }, + { + "timername" : "MT-spheres", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 11 + }, + { + "timername" : "den-pot integrals", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 11 + }, + { + "timername" : "Vxc in interstitial", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 11 + }, + { + "timername" : "Vxc in MT", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 11 + } + ] + }, + { + "timername" : "gen. of hamil. and diag. (tota", + "totaltime" : 3.00000, + "mintime" : 0.0000E+00, + "maxtime" : 1.00000, + "ncalls" : 11, + "subtimers": [ + { + "timername" : "eigen", + "totaltime" : 3.00000, + "mintime" : 0.0000E+00, + "maxtime" : 1.00000, + "ncalls" : 11, + "subtimers": [ + { + "timername" : "Updating energy parameters", + "totaltime" : 1.00000, + "mintime" : 0.0000E+00, + "maxtime" : 1.00000, + "ncalls" : 11 + }, + { + "timername" : "tlmplm", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 11 + }, + { + "timername" : "Setup of H&S matrices", + "totaltime" : 2.00000, + "mintime" : 0.0000E+00, + "maxtime" : 1.00000, + "ncalls" : 660, + "subtimers": [ + { + "timername" : "Interstitial part", + "totaltime" : 1.00000, + "mintime" : 0.0000E+00, + "maxtime" : 1.00000, + "ncalls" : 660 + }, + { + "timername" : "MT part", + "totaltime" : 1.00000, + "mintime" : 0.0000E+00, + "maxtime" : 1.00000, + "ncalls" : 660, + "subtimers": [ + { + "timername" : "fjgj coefficients", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 660 + }, + { + "timername" : "spherical setup", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 660 + }, + { + "timername" : "non-spherical setup", + "totaltime" : 1.00000, + "mintime" : 0.0000E+00, + "maxtime" : 1.00000, + "ncalls" : 660 + }, + { + "timername" : "LO setup", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 660 + } + ] + }, + { + "timername" : "Matrix redistribution", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 660 + } + ] + }, + { + "timername" : "Diagonalization", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 660 + }, + { + "timername" : "EV output", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 660, + "subtimers": [ + { + "timername" : "IO (write)", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 660 + } + ] + } + ] + } + ] + }, + { + "timername" : "determination of fermi energy", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 11, + "subtimers": [ + { + "timername" : "IO (read)", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 660 + }, + { + "timername" : "IO (write)", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 660 + } + ] + }, + { + "timername" : "generation of new charge densi", + "totaltime" : 1.00000, + "mintime" : 0.0000E+00, + "maxtime" : 1.00000, + "ncalls" : 11, + "subtimers": [ + { + "timername" : "cdnval", + "totaltime" : 1.00000, + "mintime" : 0.0000E+00, + "maxtime" : 1.00000, + "ncalls" : 11, + "subtimers": [ + { + "timername" : "IO (read)", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 660 + }, + { + "timername" : "pwden", + "totaltime" : 1.00000, + "mintime" : 0.0000E+00, + "maxtime" : 1.00000, + "ncalls" : 660 + }, + { + "timername" : "abcof", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 660 + }, + { + "timername" : "cdnval: rhomt", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 660 + }, + { + "timername" : "cdnval: rhonmt", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 660 + }, + { + "timername" : "cdnval: rho(n)mtlo", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 660 + }, + { + "timername" : "cdnmt", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 11 + } + ] + }, + { + "timername" : "cdntot", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 22, + "subtimers": [ + { + "timername" : "MT", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 22 + } + ] + }, + { + "timername" : "cdngen: cdncore", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 11, + "subtimers": [ + { + "timername" : "qpw_to_nmt", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 11 + } + ] + } + ] + }, + { + "timername" : "determination of total energy", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 11 + }, + { + "timername" : "Charge Density Mixing", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 11, + "subtimers": [ + { + "timername" : "Reading of distances", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 11 + }, + { + "timername" : "Mixing", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 11 + }, + { + "timername" : "Postprocessing", + "totaltime" : 0.0000E+00, + "mintime" : 0.0000E+00, + "maxtime" : 0.0000E+00, + "ncalls" : 11 + } + ] + } + ] + } + ] +} diff --git a/tests/parsers/fixtures/fleur/default/out.error b/tests/parsers/fixtures/fleur/default/out.error new file mode 100644 index 000000000..af9803b54 --- /dev/null +++ b/tests/parsers/fixtures/fleur/default/out.error @@ -0,0 +1,12 @@ +I/O warning : failed to load external entity "relax.xml" + + ***************************************** + Run finished successfully + Stop message: + all done + ***************************************** +Rank:0 used 0.676GB/ 712964 kB + % Total % Received % Xferd Average Speed Time Time Time Current + Dload Upload Total Spent Left Speed + 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0 100 780 100 40 100 740 181 3352 --:--:-- --:--:-- --:--:-- 3363 +OK diff --git a/tests/parsers/fixtures/fleur/default/out.xml b/tests/parsers/fixtures/fleur/default/out.xml new file mode 100644 index 000000000..0fe9c6b19 --- /dev/null +++ b/tests/parsers/fixtures/fleur/default/out.xml @@ -0,0 +1,1013 @@ + + + + + + GEN + + + + + + + + + + Si, alpha silicon, bulk, delta project + + + + + + + + + + .0000000000 .0000000000 .0000000000 + + + + + + + 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 + + + + + + + + 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 + + + + + .000000000000000 5.167355275200000 5.167355275200000 + 5.167355275200000 .000000000000000 5.167355275200000 + 5.167355275200000 5.167355275200000 .000000000000000 + + + + + + + + + + + + + + + 1.000/8.000 1.000/8.000 1.000/8.000 + -1.000/8.000 -1.000/8.000 -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/parsers/fixtures/fleur/default/shell.out b/tests/parsers/fixtures/fleur/default/shell.out new file mode 100644 index 000000000..39ea32f54 --- /dev/null +++ b/tests/parsers/fixtures/fleur/default/shell.out @@ -0,0 +1,58 @@ + Welcome to FLEUR (www.flapw.de) + MaX-Release 4.0 (www.max-centre.eu) + stars are always ordered + -------------------------------------------------------- + Number of OMP-threads: 6 + -------------------------------------------------------- + Iteration: 1 Distance: 8.14211820818857 + Test for time of next iteration: + Time provided (min): 10 + Time used (min): 1 + Time per iter (min): 1 + Iteration: 2 Distance: 7.69204733499305 + Test for time of next iteration: + Time provided (min): 10 + Time used (min): 1 + Time per iter (min): 1 + Iteration: 3 Distance: 0.856944507774482 + Test for time of next iteration: + Time provided (min): 10 + Time used (min): 1 + Time per iter (min): 1 + Iteration: 4 Distance: 0.501438298333166 + Test for time of next iteration: + Time provided (min): 10 + Time used (min): 1 + Time per iter (min): 1 + Iteration: 5 Distance: 0.205605182835176 + Test for time of next iteration: + Time provided (min): 10 + Time used (min): 1 + Time per iter (min): 1 + Iteration: 6 Distance: 1.852288794887397E-002 + Test for time of next iteration: + Time provided (min): 10 + Time used (min): 1 + Time per iter (min): 1 + Iteration: 7 Distance: 1.291466956872122E-002 + Test for time of next iteration: + Time provided (min): 10 + Time used (min): 1 + Time per iter (min): 1 + Iteration: 8 Distance: 1.303177341130901E-003 + Test for time of next iteration: + Time provided (min): 10 + Time used (min): 1 + Time per iter (min): 1 + Iteration: 9 Distance: 1.207653876674686E-003 + Test for time of next iteration: + Time provided (min): 10 + Time used (min): 1 + Time per iter (min): 1 + Iteration: 10 Distance: 1.741120625902552E-004 + Test for time of next iteration: + Time provided (min): 10 + Time used (min): 1 + Time per iter (min): 1 + Iteration: 11 Distance: 3.295348349644261E-005 + Usage data send using curl: usage.json diff --git a/tests/parsers/fixtures/fleur/mt_overlap_errorout/out.error b/tests/parsers/fixtures/fleur/mt_overlap_errorout/out.error new file mode 100644 index 000000000..e11603cde --- /dev/null +++ b/tests/parsers/fixtures/fleur/mt_overlap_errorout/out.error @@ -0,0 +1,16 @@ +**************juDFT-Error***************** +Error message:Overlapping MT-spheres during relaxation: +2 1 olap: 6.8200E-03 +Treat as an error: writing rescaled displacements to relax.xml is not implemented +***************************************** + Last kown location: + Last timer:postprocessInput + Timerstack: + Timer:Initialization + Timer:Total Run + ***************************************** +Rank:0 used 0.171GB/ 178892 kB + % Total % Received % Xferd Average Speed Time Time Time Current + Dload Upload Total Spent Left Speed +100 697 100 101 100 596 295 1745 --:--:-- --:--:-- --:--:-- 1747 +juDFT-STOPPED diff --git a/tests/parsers/fixtures/fleur/mt_overlap_errorout/out.xml b/tests/parsers/fixtures/fleur/mt_overlap_errorout/out.xml new file mode 100644 index 000000000..4f4396841 --- /dev/null +++ b/tests/parsers/fixtures/fleur/mt_overlap_errorout/out.xml @@ -0,0 +1,19 @@ + + + + + + GEN + + + + + + + + + + + diff --git a/tests/parsers/fixtures/fleur/mt_overlap_errorout/relax.xml b/tests/parsers/fixtures/fleur/mt_overlap_errorout/relax.xml new file mode 100644 index 000000000..99be634d9 --- /dev/null +++ b/tests/parsers/fixtures/fleur/mt_overlap_errorout/relax.xml @@ -0,0 +1,14 @@ + + + + 0.0000000000 0.0000000000 0.0246059526 + + + + 0.0000000000 0.0000000000 -0.7558904500 0.0000000000 0.0000000000 0.0283825580 + + + 0.0000000000 0.0000000000 -0.7416991710 0.0000000000 0.0000000000 0.0208293472 + + + diff --git a/tests/parsers/fixtures/fleur/mt_overlap_errorout/shell.out b/tests/parsers/fixtures/fleur/mt_overlap_errorout/shell.out new file mode 100644 index 000000000..20176599a --- /dev/null +++ b/tests/parsers/fixtures/fleur/mt_overlap_errorout/shell.out @@ -0,0 +1,6 @@ +iffcluster0111: Using InfiniBand for MPI communication. + Welcome to FLEUR (www.flapw.de) + MaX-Release 4.0 (www.max-centre.eu) + Attention: Overlapping MT-spheres. Reduced displacement by 10% + 2 1 6.819998745005051E-003 + Usage data send using curl: usage.json diff --git a/tests/parsers/fixtures/fleur/relax/inp.xml b/tests/parsers/fixtures/fleur/relax/inp.xml new file mode 100644 index 000000000..0c4cd2a6f --- /dev/null +++ b/tests/parsers/fixtures/fleur/relax/inp.xml @@ -0,0 +1,151 @@ + + + A Fleur input generator calculation with aiida + + + + + + + + + + .0000000000 .0000000000 .0000000000 + + + + + + + 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 + + + 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 + + + + + 28.345891875000000 .000000000000000 .000000000000000 + .000000000000000 28.345891875000000 .000000000000000 + .000000000000000 .000000000000000 28.345891875000000 + + + + + + + + + + + + + + + + .0000000000 .0000000000 -.7558904500 + .0000000000 .0000000000 .7558904500 + + + + + + + + + + + + + + + + + + + diff --git a/tests/parsers/fixtures/fleur/relax/out.error b/tests/parsers/fixtures/fleur/relax/out.error new file mode 100644 index 000000000..8fd25387b --- /dev/null +++ b/tests/parsers/fixtures/fleur/relax/out.error @@ -0,0 +1,10 @@ + ***************************************** + Run finished successfully + Stop message: + Structural relaxation: new displacements generated + ***************************************** +Rank:0 used 2.394GB/ 4967856 kB + % Total % Received % Xferd Average Speed Time Time Time Current + Dload Upload Total Spent Left Speed +100 807 100 74 100 733 191 1897 --:--:-- --:--:-- --:--:-- 1894 +OK diff --git a/tests/parsers/fixtures/fleur/relax/out.xml b/tests/parsers/fixtures/fleur/relax/out.xml new file mode 100644 index 000000000..82e5d56bb --- /dev/null +++ b/tests/parsers/fixtures/fleur/relax/out.xml @@ -0,0 +1,2317 @@ + + + + + + GEN + + + + + + + + + + + A Fleur input generator calculation with aiida + + + + + + + + + + .0000000000 .0000000000 .0000000000 + + + + + + + 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 + + + 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 + + + + + 28.345891875000000 .000000000000000 .000000000000000 + .000000000000000 28.345891875000000 .000000000000000 + .000000000000000 .000000000000000 4.630000000000000 + + + + + + + + + + + + + + + + .0000000000 .0000000000 -.7416991710 + .0000000000 .0000000000 .7416991710 + + + + + + + + + + + + + + + + + + + + + + + + + 0.000000 0.000000 0.000000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/parsers/fixtures/fleur/relax/relax.xml b/tests/parsers/fixtures/fleur/relax/relax.xml new file mode 100644 index 000000000..99be634d9 --- /dev/null +++ b/tests/parsers/fixtures/fleur/relax/relax.xml @@ -0,0 +1,14 @@ + + + + 0.0000000000 0.0000000000 0.0246059526 + + + + 0.0000000000 0.0000000000 -0.7558904500 0.0000000000 0.0000000000 0.0283825580 + + + 0.0000000000 0.0000000000 -0.7416991710 0.0000000000 0.0000000000 0.0208293472 + + + diff --git a/tests/parsers/fixtures/fleur/relax/shell.out b/tests/parsers/fixtures/fleur/relax/shell.out new file mode 100644 index 000000000..333823046 --- /dev/null +++ b/tests/parsers/fixtures/fleur/relax/shell.out @@ -0,0 +1,214 @@ +iffcluster0107: Using InfiniBand for MPI communication. + Welcome to FLEUR (www.flapw.de) + MaX-Release 4.0 (www.max-centre.eu) + nop = 16 + -------------------------------------------------------- + Number of OMP-threads: 12 + -------------------------------------------------------- + Iteration: 1 Distance: 0.163237983230284 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 4 + Time per iter (min): 4 + Iteration: 2 Distance: 0.152278301128890 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 8 + Time per iter (min): 4 + Iteration: 3 Distance: 1.280699031885719E-002 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 11 + Time per iter (min): 4 + Iteration: 4 Distance: 1.544701303213275E-002 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 15 + Time per iter (min): 4 + Iteration: 5 Distance: 4.020987856830026E-003 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 19 + Time per iter (min): 4 + Iteration: 6 Distance: 1.945253362703284E-004 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 23 + Time per iter (min): 4 + Iteration: 7 Distance: 2.468709825867029E-004 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 26 + Time per iter (min): 4 + Iteration: 8 Distance: 1.028491110414090E-004 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 30 + Time per iter (min): 4 + Iteration: 9 Distance: 9.929615259078387E-005 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 34 + Time per iter (min): 4 + Iteration: 10 Distance: 3.876139978531418E-005 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 38 + Time per iter (min): 4 + Iteration: 11 Distance: 5.192625893104915E-006 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 41 + Time per iter (min): 4 + Iteration: 12 Distance: 9.189232451067283E-006 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 45 + Time per iter (min): 4 + Iteration: 13 Distance: 2.783739128832825E-005 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 49 + Time per iter (min): 4 + Iteration: 14 Distance: 4.212128520807909E-005 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 53 + Time per iter (min): 4 + Iteration: 15 Distance: 7.731771252447340E-005 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 57 + Time per iter (min): 4 + Iteration: 16 Distance: 7.768845617471034E-005 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 61 + Time per iter (min): 4 + Iteration: 17 Distance: 7.341253721698628E-005 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 65 + Time per iter (min): 4 + Iteration: 18 Distance: 3.526433224843018E-005 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 69 + Time per iter (min): 4 + Iteration: 19 Distance: 6.266528245181366E-005 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 73 + Time per iter (min): 4 + Iteration: 20 Distance: 7.731238834573022E-005 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 77 + Time per iter (min): 4 + Iteration: 21 Distance: 3.388841940099947E-005 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 81 + Time per iter (min): 4 + Iteration: 22 Distance: 3.230813132151605E-005 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 85 + Time per iter (min): 4 + Iteration: 23 Distance: 3.288678618197010E-005 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 89 + Time per iter (min): 4 + Iteration: 24 Distance: 2.973785652738386E-005 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 94 + Time per iter (min): 4 + Iteration: 25 Distance: 2.924083584706251E-005 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 98 + Time per iter (min): 4 + Iteration: 26 Distance: 2.913527221808891E-005 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 102 + Time per iter (min): 4 + Iteration: 27 Distance: 2.999922005973378E-005 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 106 + Time per iter (min): 4 + Iteration: 28 Distance: 3.008806094258066E-005 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 110 + Time per iter (min): 4 + Iteration: 29 Distance: 3.504814074016484E-005 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 115 + Time per iter (min): 4 + Iteration: 30 Distance: 3.338360515741878E-005 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 119 + Time per iter (min): 4 + Iteration: 31 Distance: 3.842254803335153E-005 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 123 + Time per iter (min): 4 + Iteration: 32 Distance: 4.627627754515800E-005 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 128 + Time per iter (min): 4 + Iteration: 33 Distance: 4.311438958929168E-005 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 132 + Time per iter (min): 4 + Iteration: 34 Distance: 4.488399890836128E-005 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 137 + Time per iter (min): 5 + Iteration: 35 Distance: 4.932233829957257E-005 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 141 + Time per iter (min): 5 + Iteration: 36 Distance: 3.389671397825875E-005 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 146 + Time per iter (min): 5 + Iteration: 37 Distance: 1.990699684397925E-005 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 150 + Time per iter (min): 5 + Iteration: 38 Distance: 2.212929999310191E-005 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 155 + Time per iter (min): 5 + Iteration: 39 Distance: 2.515473192069385E-005 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 159 + Time per iter (min): 5 + Iteration: 40 Distance: 1.265346941883755E-005 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 164 + Time per iter (min): 5 + Iteration: 41 Distance: 1.531689360592881E-006 + Test for time of next iteration: + Time provided (min): 360 + Time used (min): 168 + Time per iter (min): 5 + Reset of history + Usage data send using curl: usage.json diff --git a/tests/parsers/fixtures/inpgen/broken_inpxml/inp.xml b/tests/parsers/fixtures/inpgen/broken_inpxml/inp.xml new file mode 100644 index 000000000..a1dfec490 --- /dev/null +++ b/tests/parsers/fixtures/inpgen/broken_inpxml/inp.xml @@ -0,0 +1,111 @@ + + + + A Fleur input generator calculation with aiida + + + + + + + + + + .0000000000 .0000000000 .0000000000 + + + + + + + -0.000000 0.333333 0.333333 + -0.333333 0.333333 0.333333 + -0.000000 0.000000 0.333333 + 0.000000 0.000000 0.000000 + + + + + + + + + + + 1 0 0 .0000000000 + 0 1 0 .0000000000 + 0 0 1 .0000000000 + + + -1 0 0 .0000000000 + -1 1 0 .0000000000 + -1 0 1 .0000000000 + + + 1 -1 0 .0000000000 + 0 -1 0 .0000000000 + 0 -1 1 .0000000000 + + + 0 -1 0 .0000000000 + 1 -1 0 .0000000000 + 0 -1 1 .0000000000 + + + -1 1 0 .0000000000 + -1 0 0 .0000000000 + -1 0 1 .0000000000 + + + 0 1 0 .0000000000 + 1 0 0 .0000000000 + 0 0 1 .0000000000 + + + 1 0 -1 .0000000000 + 0 1 -1 .0000000000 + 0 0 -1 .0000000000 + + + 0 0 -1 .0000000000 + 0 1 -1 .0000000000 + 1 0 -1 .0000000000 + + + 0 1 -1 .0000000000 + 1 0 -1 .0000000000 + 0 0 -1 .0000000000 + + + 0 1 -1 .0000000000 + 0 0 -1 .0000000000 + 1 0 -1 .0000000000 + + + 1 0 -1 .0000000000 + 0 0 -1 .0000000000 + 0 1 -1 .0000000000 + + + 0 0 -1 .0000000000 + 1 0 -1 .0000000000 + 0 1 -1 .0000000000 + + + -1 0 0 .0000000000 + 0 -1 0 .0000000000 + 0 0 -1 .0000000000 + + + 1 0 0 .0000000000 + 1 -1 0 .0000000000 + 1 0 -1 .0000000000 + + + 0 -1 0 .0000000000 + -1 0 0 .0000000000 + 0 0 -1 .0000000000 + + + 1 -1 0 .0000000000 + diff --git a/tests/parsers/fixtures/inpgen/broken_inpxml/out b/tests/parsers/fixtures/inpgen/broken_inpxml/out new file mode 100644 index 000000000..fca99897b --- /dev/null +++ b/tests/parsers/fixtures/inpgen/broken_inpxml/out @@ -0,0 +1,687 @@ +line: 1>A Fleur input generator calculation with aiida +line: 2>&input cartesian=F / +line: 3>-3.0138120600 3.0138120600 3.0138120600 +line: 4>3.0138120600 -3.0138120600 3.0138120600 +line: 5>3.0138120600 3.0138120600 -3.0138120600 +line: 6>1.0000000000 +line: 7>1.0000000000 1.0000000000 1.0000000000 +line: 8> +line: 9>1 +line: 10>74 0.0000000000 0.0000000000 0.0000000000 +line: 11>&atom +line: 12>econfig="[Kr] 4d10 4f14 | 5s2 5p6 6s2 5d4" element="W" jri=981 lm + + A Fleur input generator calculation with aiida + + film= F cartesian= F + checkinp= F symor= F + +a1 = -3.01381 3.01381 3.01381 +a2 = 3.01381 -3.01381 3.01381 +a3 = 3.01381 3.01381 -3.01381 + +dvac= -3.01381 aa = 1.00000 +scale = 1.00000 1.00000 1.00000 + +natin= 1 Z = 74 + positions: + 0.00000 0.00000 0.00000 + + generators: 0 (excluding identity) + + + Lattice information: + -------------------- + + overall lattice constant a0 = 1.000000 bohr + + real-space primitive lattice vectors in units of a_{x,y,z} + a_1: -3.013812 3.013812 3.013812 + a_2: 3.013812 -3.013812 3.013812 + a_3: 3.013812 3.013812 -3.013812 + + lattice constants a_x, a_y, a_z = 1.000000 1.000000 1.000000 + volume of unit cell (a.u.^3) = 109.498581 + +dbg: lattice matrices + 109.498580847925 +dbg: as : + -3.013812 3.013812 3.013812 + 3.013812 -3.013812 3.013812 + 3.013812 3.013812 -3.013812 +dbg: bs : + 0.000000 0.165903 0.165903 + 0.165903 0.000000 0.165903 + 0.165903 0.165903 0.000000 +dbg: amat : + -3.013812 3.013812 3.013812 + 3.013812 -3.013812 3.013812 + 3.013812 3.013812 -3.013812 +dbg: bmat : + 0.000000 0.521199 0.521199 + 0.521199 0.000000 0.521199 + 0.521199 0.521199 0.000000 +dbg: amatinv : + 0.000000 0.165903 0.165903 + 0.165903 0.000000 0.165903 + 0.165903 0.165903 0.000000 +dbg: aamat : + 27.249189 -9.083063 -9.083063 + -9.083063 27.249189 -9.083063 + -9.083063 -9.083063 27.249189 +dbg: bbmat : + 0.543297 0.271649 0.271649 + 0.271649 0.543297 0.271649 + 0.271649 0.271649 0.543297 + +dbg: lattice vectors : +vector 1 : -3.01381 3.01381 3.01381 length : 5.22008 +vector 2 : 3.01381 -3.01381 3.01381 length : 5.22008 +vector 3 : 3.01381 3.01381 -3.01381 length : 5.22008 +angle between vectors (1,2) =109.47122 +angle between vectors (1,3) =109.47122 +angle between vectors (2,3) =109.47122 + +dbg: reciprocal lattice vectors : +vector 1 : 0.00000 0.52120 0.52120 length : 0.73709 +vector 2 : 0.52120 0.00000 0.52120 length : 0.73709 +vector 3 : 0.52120 0.52120 0.00000 length : 0.73709 +angle between vectors (1,2) = 60.00000 +angle between vectors (1,3) = 60.00000 +angle between vectors (2,3) = 60.00000 + + + Point group of the Bravais lattice has 48 operations + + DBG: symor,zorth,oldfleur : T F F + DBG: optype : 1 -2 -2 3 3 -2 -2 3 2 -4 3 -4 -1 2 2 -3 -3 2 2 -2 -3 4 4 -3 -3 4 2 -3 -2 4 3 -2 -4 2 -4 3 3 -4 -4 3 -2 2 4 -3 -3 2 4 -2 + DBG: invsym,invs,zrfs,invs2 : T T F F + DBG: (before reorder) invsop,zrfsop,invs2op : 13 7 46 + + Space group information: + ------------------------ + 48 operations + space group is symmorphic + has inversion symmetry + + + Operations: (in International notation) + --------------------------------------- + lattice coordinates (scaled) Cartesian coordinates + + operation 1: 1 (inverse = 1) + ( 1 0 0 ) ( 0.000 ) ( 1.00000 0.00000 0.00000 ) ( 0.000 ) + ( 0 1 0 ) ( 0.000 ) ( 0.00000 1.00000 -0.00000 ) ( 0.000 ) + ( 0 0 1 ) ( 0.000 ) ( -0.00000 -0.00000 1.00000 ) ( 0.000 ) + _ + operation 2: 2 (inverse = 2) + ( -1 0 0 ) ( 0.000 ) ( 1.00000 -0.00000 -0.00000 ) ( 0.000 ) + ( -1 1 0 ) ( 0.000 ) ( 0.00000 0.00000 -1.00000 ) ( 0.000 ) + ( -1 0 1 ) ( 0.000 ) ( -0.00000 -1.00000 0.00000 ) ( 0.000 ) + _ + operation 3: 2 (inverse = 3) + ( 1 -1 0 ) ( 0.000 ) ( -0.00000 0.00000 -1.00000 ) ( 0.000 ) + ( 0 -1 0 ) ( 0.000 ) ( 0.00000 1.00000 -0.00000 ) ( 0.000 ) + ( 0 -1 1 ) ( 0.000 ) ( -1.00000 -0.00000 0.00000 ) ( 0.000 ) + + operation 4: 3 (inverse = 5) + ( 0 -1 0 ) ( 0.000 ) ( -0.00000 1.00000 -0.00000 ) ( 0.000 ) + ( 1 -1 0 ) ( 0.000 ) ( 0.00000 0.00000 -1.00000 ) ( 0.000 ) + ( 0 -1 1 ) ( 0.000 ) ( -1.00000 -0.00000 0.00000 ) ( 0.000 ) + + operation 5: 3 (inverse = 4) + ( -1 1 0 ) ( 0.000 ) ( 0.00000 -0.00000 -1.00000 ) ( 0.000 ) + ( -1 0 0 ) ( 0.000 ) ( 1.00000 0.00000 -0.00000 ) ( 0.000 ) + ( -1 0 1 ) ( 0.000 ) ( -0.00000 -1.00000 0.00000 ) ( 0.000 ) + _ + operation 6: 2 (inverse = 6) + ( 0 1 0 ) ( 0.000 ) ( 0.00000 1.00000 0.00000 ) ( 0.000 ) + ( 1 0 0 ) ( 0.000 ) ( 1.00000 0.00000 -0.00000 ) ( 0.000 ) + ( 0 0 1 ) ( 0.000 ) ( -0.00000 -0.00000 1.00000 ) ( 0.000 ) + _ + operation 7: 2 (inverse = 7) + ( 1 0 -1 ) ( 0.000 ) ( -0.00000 -1.00000 0.00000 ) ( 0.000 ) + ( 0 1 -1 ) ( 0.000 ) ( -1.00000 -0.00000 -0.00000 ) ( 0.000 ) + ( 0 0 -1 ) ( 0.000 ) ( 0.00000 0.00000 1.00000 ) ( 0.000 ) + + operation 8: 3 (inverse = 31) + ( 0 0 -1 ) ( 0.000 ) ( -0.00000 -0.00000 1.00000 ) ( 0.000 ) + ( 0 1 -1 ) ( 0.000 ) ( -1.00000 0.00000 0.00000 ) ( 0.000 ) + ( 1 0 -1 ) ( 0.000 ) ( 0.00000 -1.00000 -0.00000 ) ( 0.000 ) + + operation 9: 2 (inverse = 9) + ( 0 1 -1 ) ( 0.000 ) ( -1.00000 -0.00000 0.00000 ) ( 0.000 ) + ( 1 0 -1 ) ( 0.000 ) ( -0.00000 -1.00000 -0.00000 ) ( 0.000 ) + ( 0 0 -1 ) ( 0.000 ) ( 0.00000 0.00000 1.00000 ) ( 0.000 ) + _ + operation 10: 4 (inverse = 33) + ( 0 1 -1 ) ( 0.000 ) ( -1.00000 -0.00000 0.00000 ) ( 0.000 ) + ( 0 0 -1 ) ( 0.000 ) ( -0.00000 0.00000 1.00000 ) ( 0.000 ) + ( 1 0 -1 ) ( 0.000 ) ( 0.00000 -1.00000 -0.00000 ) ( 0.000 ) + + operation 11: 3 (inverse = 37) + ( 1 0 -1 ) ( 0.000 ) ( -0.00000 -1.00000 0.00000 ) ( 0.000 ) + ( 0 0 -1 ) ( 0.000 ) ( 0.00000 -0.00000 1.00000 ) ( 0.000 ) + ( 0 1 -1 ) ( 0.000 ) ( -1.00000 0.00000 -0.00000 ) ( 0.000 ) + _ + operation 12: 4 (inverse = 39) + ( 0 0 -1 ) ( 0.000 ) ( -0.00000 -0.00000 1.00000 ) ( 0.000 ) + ( 1 0 -1 ) ( 0.000 ) ( 0.00000 -1.00000 0.00000 ) ( 0.000 ) + ( 0 1 -1 ) ( 0.000 ) ( -1.00000 0.00000 -0.00000 ) ( 0.000 ) + _ + operation 13: 1 (inverse = 13) + ( -1 0 0 ) ( 0.000 ) ( -1.00000 -0.00000 -0.00000 ) ( 0.000 ) + ( 0 -1 0 ) ( 0.000 ) ( -0.00000 -1.00000 0.00000 ) ( 0.000 ) + ( 0 0 -1 ) ( 0.000 ) ( 0.00000 0.00000 -1.00000 ) ( 0.000 ) + + operation 14: 2 (inverse = 14) + ( 1 0 0 ) ( 0.000 ) ( -1.00000 0.00000 0.00000 ) ( 0.000 ) + ( 1 -1 0 ) ( 0.000 ) ( -0.00000 -0.00000 1.00000 ) ( 0.000 ) + ( 1 0 -1 ) ( 0.000 ) ( 0.00000 1.00000 -0.00000 ) ( 0.000 ) + + operation 15: 2 (inverse = 15) + ( 0 -1 0 ) ( 0.000 ) ( -0.00000 -1.00000 -0.00000 ) ( 0.000 ) + ( -1 0 0 ) ( 0.000 ) ( -1.00000 -0.00000 0.00000 ) ( 0.000 ) + ( 0 0 -1 ) ( 0.000 ) ( 0.00000 0.00000 -1.00000 ) ( 0.000 ) + _ + operation 16: 3 (inverse = 17) + ( 1 -1 0 ) ( 0.000 ) ( -0.00000 0.00000 1.00000 ) ( 0.000 ) + ( 1 0 0 ) ( 0.000 ) ( -1.00000 -0.00000 0.00000 ) ( 0.000 ) + ( 1 0 -1 ) ( 0.000 ) ( 0.00000 1.00000 -0.00000 ) ( 0.000 ) + _ + operation 17: 3 (inverse = 16) + ( 0 1 0 ) ( 0.000 ) ( 0.00000 -1.00000 0.00000 ) ( 0.000 ) + ( -1 1 0 ) ( 0.000 ) ( -0.00000 -0.00000 1.00000 ) ( 0.000 ) + ( 0 1 -1 ) ( 0.000 ) ( 1.00000 0.00000 -0.00000 ) ( 0.000 ) + + operation 18: 2 (inverse = 18) + ( -1 1 0 ) ( 0.000 ) ( 0.00000 -0.00000 1.00000 ) ( 0.000 ) + ( 0 1 0 ) ( 0.000 ) ( -0.00000 -1.00000 0.00000 ) ( 0.000 ) + ( 0 1 -1 ) ( 0.000 ) ( 1.00000 0.00000 -0.00000 ) ( 0.000 ) + + operation 19: 2 (inverse = 19) + ( -1 0 0 ) ( 0.000 ) ( -1.00000 -0.00000 -0.00000 ) ( 0.000 ) + ( 0 0 -1 ) ( 0.000 ) ( -0.00000 0.00000 -1.00000 ) ( 0.000 ) + ( 0 -1 0 ) ( 0.000 ) ( 0.00000 -1.00000 0.00000 ) ( 0.000 ) + _ + operation 20: 2 (inverse = 20) + ( 1 0 0 ) ( 0.000 ) ( -1.00000 0.00000 0.00000 ) ( 0.000 ) + ( 1 0 -1 ) ( 0.000 ) ( -0.00000 1.00000 -0.00000 ) ( 0.000 ) + ( 1 -1 0 ) ( 0.000 ) ( 0.00000 -0.00000 1.00000 ) ( 0.000 ) + _ + operation 21: 3 (inverse = 25) + ( 0 -1 0 ) ( 0.000 ) ( -0.00000 -1.00000 -0.00000 ) ( 0.000 ) + ( 0 0 -1 ) ( 0.000 ) ( 0.00000 -0.00000 -1.00000 ) ( 0.000 ) + ( -1 0 0 ) ( 0.000 ) ( -1.00000 0.00000 0.00000 ) ( 0.000 ) + + operation 22: 4 (inverse = 43) + ( 1 -1 0 ) ( 0.000 ) ( -0.00000 0.00000 1.00000 ) ( 0.000 ) + ( 1 0 -1 ) ( 0.000 ) ( 0.00000 1.00000 0.00000 ) ( 0.000 ) + ( 1 0 0 ) ( 0.000 ) ( -1.00000 -0.00000 -0.00000 ) ( 0.000 ) + + operation 23: 4 (inverse = 26) + ( 0 1 0 ) ( 0.000 ) ( 0.00000 -1.00000 0.00000 ) ( 0.000 ) + ( 0 1 -1 ) ( 0.000 ) ( 1.00000 -0.00000 -0.00000 ) ( 0.000 ) + ( -1 1 0 ) ( 0.000 ) ( -0.00000 0.00000 1.00000 ) ( 0.000 ) + _ + operation 24: 3 (inverse = 44) + ( -1 1 0 ) ( 0.000 ) ( 0.00000 -0.00000 1.00000 ) ( 0.000 ) + ( 0 1 -1 ) ( 0.000 ) ( 1.00000 0.00000 0.00000 ) ( 0.000 ) + ( 0 1 0 ) ( 0.000 ) ( -0.00000 -1.00000 -0.00000 ) ( 0.000 ) + _ + operation 25: 3 (inverse = 21) + ( 0 0 -1 ) ( 0.000 ) ( -0.00000 -0.00000 -1.00000 ) ( 0.000 ) + ( -1 0 0 ) ( 0.000 ) ( -1.00000 0.00000 -0.00000 ) ( 0.000 ) + ( 0 -1 0 ) ( 0.000 ) ( 0.00000 -1.00000 0.00000 ) ( 0.000 ) + + operation 26: 4 (inverse = 23) + ( 1 0 -1 ) ( 0.000 ) ( -0.00000 1.00000 0.00000 ) ( 0.000 ) + ( 1 0 0 ) ( 0.000 ) ( -1.00000 0.00000 -0.00000 ) ( 0.000 ) + ( 1 -1 0 ) ( 0.000 ) ( 0.00000 -0.00000 1.00000 ) ( 0.000 ) + + operation 27: 2 (inverse = 27) + ( 0 0 -1 ) ( 0.000 ) ( -0.00000 -0.00000 -1.00000 ) ( 0.000 ) + ( 0 -1 0 ) ( 0.000 ) ( 0.00000 -1.00000 -0.00000 ) ( 0.000 ) + ( -1 0 0 ) ( 0.000 ) ( -1.00000 0.00000 0.00000 ) ( 0.000 ) + _ + operation 28: 3 (inverse = 45) + ( 1 0 -1 ) ( 0.000 ) ( -0.00000 1.00000 0.00000 ) ( 0.000 ) + ( 1 -1 0 ) ( 0.000 ) ( 0.00000 0.00000 1.00000 ) ( 0.000 ) + ( 1 0 0 ) ( 0.000 ) ( -1.00000 -0.00000 -0.00000 ) ( 0.000 ) + _ + operation 29: 2 (inverse = 29) + ( 0 1 -1 ) ( 0.000 ) ( 1.00000 -0.00000 0.00000 ) ( 0.000 ) + ( 0 1 0 ) ( 0.000 ) ( 0.00000 -1.00000 -0.00000 ) ( 0.000 ) + ( -1 1 0 ) ( 0.000 ) ( -0.00000 0.00000 1.00000 ) ( 0.000 ) + + operation 30: 4 (inverse = 47) + ( 0 1 -1 ) ( 0.000 ) ( 1.00000 -0.00000 0.00000 ) ( 0.000 ) + ( -1 1 0 ) ( 0.000 ) ( 0.00000 0.00000 1.00000 ) ( 0.000 ) + ( 0 1 0 ) ( 0.000 ) ( -0.00000 -1.00000 -0.00000 ) ( 0.000 ) + + operation 31: 3 (inverse = 8) + ( -1 0 1 ) ( 0.000 ) ( 0.00000 -1.00000 -0.00000 ) ( 0.000 ) + ( -1 1 0 ) ( 0.000 ) ( -0.00000 -0.00000 -1.00000 ) ( 0.000 ) + ( -1 0 0 ) ( 0.000 ) ( 1.00000 0.00000 0.00000 ) ( 0.000 ) + _ + operation 32: 2 (inverse = 32) + ( 0 0 1 ) ( 0.000 ) ( 0.00000 0.00000 1.00000 ) ( 0.000 ) + ( 0 1 0 ) ( 0.000 ) ( -0.00000 1.00000 0.00000 ) ( 0.000 ) + ( 1 0 0 ) ( 0.000 ) ( 1.00000 -0.00000 -0.00000 ) ( 0.000 ) + _ + operation 33: 4 (inverse = 10) + ( 0 -1 1 ) ( 0.000 ) ( -1.00000 0.00000 -0.00000 ) ( 0.000 ) + ( 1 -1 0 ) ( 0.000 ) ( -0.00000 -0.00000 -1.00000 ) ( 0.000 ) + ( 0 -1 0 ) ( 0.000 ) ( 0.00000 1.00000 0.00000 ) ( 0.000 ) + + operation 34: 2 (inverse = 34) + ( 0 -1 1 ) ( 0.000 ) ( -1.00000 0.00000 -0.00000 ) ( 0.000 ) + ( 0 -1 0 ) ( 0.000 ) ( -0.00000 1.00000 0.00000 ) ( 0.000 ) + ( 1 -1 0 ) ( 0.000 ) ( 0.00000 -0.00000 -1.00000 ) ( 0.000 ) + _ + operation 35: 4 (inverse = 38) + ( -1 0 1 ) ( 0.000 ) ( 0.00000 -1.00000 -0.00000 ) ( 0.000 ) + ( -1 0 0 ) ( 0.000 ) ( 1.00000 -0.00000 0.00000 ) ( 0.000 ) + ( -1 1 0 ) ( 0.000 ) ( -0.00000 0.00000 -1.00000 ) ( 0.000 ) + + operation 36: 3 (inverse = 40) + ( 0 0 1 ) ( 0.000 ) ( 0.00000 0.00000 1.00000 ) ( 0.000 ) + ( 1 0 0 ) ( 0.000 ) ( 1.00000 -0.00000 0.00000 ) ( 0.000 ) + ( 0 1 0 ) ( 0.000 ) ( -0.00000 1.00000 -0.00000 ) ( 0.000 ) + + operation 37: 3 (inverse = 11) + ( 1 -1 0 ) ( 0.000 ) ( -0.00000 0.00000 -1.00000 ) ( 0.000 ) + ( 0 -1 1 ) ( 0.000 ) ( -1.00000 -0.00000 -0.00000 ) ( 0.000 ) + ( 0 -1 0 ) ( 0.000 ) ( 0.00000 1.00000 0.00000 ) ( 0.000 ) + _ + operation 38: 4 (inverse = 35) + ( 0 -1 0 ) ( 0.000 ) ( -0.00000 1.00000 -0.00000 ) ( 0.000 ) + ( 0 -1 1 ) ( 0.000 ) ( -1.00000 0.00000 0.00000 ) ( 0.000 ) + ( 1 -1 0 ) ( 0.000 ) ( 0.00000 -0.00000 -1.00000 ) ( 0.000 ) + _ + operation 39: 4 (inverse = 12) + ( -1 1 0 ) ( 0.000 ) ( 0.00000 -0.00000 -1.00000 ) ( 0.000 ) + ( -1 0 1 ) ( 0.000 ) ( -0.00000 -1.00000 -0.00000 ) ( 0.000 ) + ( -1 0 0 ) ( 0.000 ) ( 1.00000 0.00000 0.00000 ) ( 0.000 ) + + operation 40: 3 (inverse = 36) + ( 0 1 0 ) ( 0.000 ) ( 0.00000 1.00000 0.00000 ) ( 0.000 ) + ( 0 0 1 ) ( 0.000 ) ( -0.00000 0.00000 1.00000 ) ( 0.000 ) + ( 1 0 0 ) ( 0.000 ) ( 1.00000 -0.00000 -0.00000 ) ( 0.000 ) + _ + operation 41: 2 (inverse = 41) + ( 1 0 0 ) ( 0.000 ) ( 1.00000 0.00000 0.00000 ) ( 0.000 ) + ( 0 0 1 ) ( 0.000 ) ( 0.00000 -0.00000 1.00000 ) ( 0.000 ) + ( 0 1 0 ) ( 0.000 ) ( -0.00000 1.00000 -0.00000 ) ( 0.000 ) + + operation 42: 2 (inverse = 42) + ( -1 0 0 ) ( 0.000 ) ( 1.00000 -0.00000 -0.00000 ) ( 0.000 ) + ( -1 0 1 ) ( 0.000 ) ( 0.00000 -1.00000 0.00000 ) ( 0.000 ) + ( -1 1 0 ) ( 0.000 ) ( -0.00000 0.00000 -1.00000 ) ( 0.000 ) + + operation 43: 4 (inverse = 22) + ( 0 0 1 ) ( 0.000 ) ( 0.00000 0.00000 -1.00000 ) ( 0.000 ) + ( -1 0 1 ) ( 0.000 ) ( -0.00000 1.00000 -0.00000 ) ( 0.000 ) + ( 0 -1 1 ) ( 0.000 ) ( 1.00000 -0.00000 0.00000 ) ( 0.000 ) + _ + operation 44: 3 (inverse = 24) + ( -1 0 1 ) ( 0.000 ) ( 0.00000 1.00000 -0.00000 ) ( 0.000 ) + ( 0 0 1 ) ( 0.000 ) ( -0.00000 0.00000 -1.00000 ) ( 0.000 ) + ( 0 -1 1 ) ( 0.000 ) ( 1.00000 -0.00000 0.00000 ) ( 0.000 ) + _ + operation 45: 3 (inverse = 28) + ( 0 0 1 ) ( 0.000 ) ( 0.00000 0.00000 -1.00000 ) ( 0.000 ) + ( 0 -1 1 ) ( 0.000 ) ( 1.00000 -0.00000 -0.00000 ) ( 0.000 ) + ( -1 0 1 ) ( 0.000 ) ( -0.00000 1.00000 0.00000 ) ( 0.000 ) + + operation 46: 2 (inverse = 46) + ( -1 0 1 ) ( 0.000 ) ( 0.00000 1.00000 -0.00000 ) ( 0.000 ) + ( 0 -1 1 ) ( 0.000 ) ( 1.00000 0.00000 0.00000 ) ( 0.000 ) + ( 0 0 1 ) ( 0.000 ) ( -0.00000 -0.00000 -1.00000 ) ( 0.000 ) + + operation 47: 4 (inverse = 30) + ( 0 -1 1 ) ( 0.000 ) ( 1.00000 0.00000 -0.00000 ) ( 0.000 ) + ( 0 0 1 ) ( 0.000 ) ( 0.00000 -0.00000 -1.00000 ) ( 0.000 ) + ( -1 0 1 ) ( 0.000 ) ( -0.00000 1.00000 0.00000 ) ( 0.000 ) + _ + operation 48: 2 (inverse = 48) + ( 0 -1 1 ) ( 0.000 ) ( 1.00000 0.00000 -0.00000 ) ( 0.000 ) + ( -1 0 1 ) ( 0.000 ) ( 0.00000 1.00000 0.00000 ) ( 0.000 ) + ( 0 0 1 ) ( 0.000 ) ( -0.00000 -0.00000 -1.00000 ) ( 0.000 ) + + Multiplcation table: {R_j|t_j}{R_i|t_i} + operation j= 1 : 1 2 3 4 5 6 7 8 9 10 11 12 + 13 14 15 16 17 18 19 20 21 22 23 24 + 25 26 27 28 29 30 31 32 33 34 35 36 + 37 38 39 40 41 42 43 44 45 46 47 48 + operation j= 2 : 2 1 5 6 3 4 31 32 33 34 35 36 + 14 13 17 18 15 16 20 19 23 24 21 22 + 43 44 45 46 47 48 7 8 9 10 11 12 + 39 40 37 38 42 41 25 26 27 28 29 30 + operation j= 3 : 3 4 1 2 6 5 37 38 39 40 41 42 + 18 17 16 15 14 13 44 43 47 48 45 46 + 26 25 29 30 27 28 33 34 31 32 36 35 + 7 8 9 10 11 12 20 19 23 24 21 22 + operation j= 4 : 4 3 6 5 1 2 33 34 31 32 36 35 + 17 18 14 13 16 15 43 44 45 46 47 48 + 20 19 23 24 21 22 37 38 39 40 41 42 + 9 10 7 8 12 11 26 25 29 30 27 28 + operation j= 5 : 5 6 2 1 4 3 39 40 37 38 42 41 + 16 15 18 17 13 14 26 25 29 30 27 28 + 44 43 47 48 45 46 9 10 7 8 12 11 + 31 32 33 34 35 36 19 20 21 22 23 24 + operation j= 6 : 6 5 4 3 2 1 9 10 7 8 12 11 + 15 16 13 14 18 17 25 26 27 28 29 30 + 19 20 21 22 23 24 39 40 37 38 42 41 + 33 34 31 32 36 35 44 43 47 48 45 46 + operation j= 7 : 7 8 11 12 10 9 1 2 6 5 3 4 + 46 45 48 47 43 44 24 23 22 21 20 19 + 30 29 28 27 26 25 32 31 36 35 34 33 + 41 42 40 39 37 38 17 18 14 13 16 15 + operation j= 8 : 8 7 10 9 11 12 32 31 36 35 34 33 + 45 46 43 44 48 47 23 24 20 19 22 21 + 17 18 14 13 16 15 1 2 6 5 3 4 + 40 39 41 42 38 37 30 29 28 27 26 25 + operation j= 9 : 9 10 12 11 8 7 6 5 1 2 4 3 + 48 47 46 45 44 43 30 29 28 27 26 25 + 24 23 22 21 20 19 40 39 41 42 38 37 + 36 35 32 31 33 34 18 17 16 15 14 13 + operation j=10 : 10 9 8 7 12 11 40 39 41 42 38 37 + 47 48 44 43 46 45 29 30 26 25 28 27 + 18 17 16 15 14 13 6 5 1 2 4 3 + 32 31 36 35 34 33 24 23 22 21 20 19 + operation j=11 : 11 12 7 8 9 10 41 42 40 39 37 38 + 44 43 47 48 45 46 18 17 16 15 14 13 + 29 30 26 25 28 27 36 35 32 31 33 34 + 1 2 6 5 3 4 23 24 20 19 22 21 + operation j=12 : 12 11 9 10 7 8 36 35 32 31 33 34 + 43 44 45 46 47 48 17 18 14 13 16 15 + 23 24 20 19 22 21 41 42 40 39 37 38 + 6 5 1 2 4 3 29 30 26 25 28 27 + operation j=13 : 13 14 18 17 16 15 46 45 48 47 44 43 + 1 2 6 5 4 3 41 42 40 39 38 37 + 36 35 32 31 34 33 28 27 30 29 26 25 + 24 23 22 21 19 20 12 11 8 7 10 9 + operation j=14 : 14 13 16 15 18 17 28 27 30 29 26 25 + 2 1 4 3 6 5 42 41 38 37 40 39 + 12 11 8 7 10 9 46 45 48 47 44 43 + 22 21 24 23 20 19 36 35 32 31 34 33 + operation j=15 : 15 16 17 18 14 13 48 47 46 45 43 44 + 6 5 1 2 3 4 36 35 32 31 34 33 + 41 42 40 39 38 37 22 21 24 23 20 19 + 30 29 28 27 25 26 11 12 10 9 8 7 + operation j=16 : 16 15 14 13 17 18 22 21 24 23 20 19 + 5 6 3 4 1 2 35 36 34 33 32 31 + 11 12 10 9 8 7 48 47 46 45 43 44 + 28 27 30 29 26 25 41 42 40 39 38 37 + operation j=17 : 17 18 15 16 13 14 30 29 28 27 25 26 + 4 3 2 1 5 6 12 11 8 7 10 9 + 42 41 38 37 40 39 24 23 22 21 19 20 + 48 47 46 45 43 44 35 36 34 33 32 31 + operation j=18 : 18 17 13 14 15 16 24 23 22 21 19 20 + 3 4 5 6 2 1 11 12 10 9 8 7 + 35 36 34 33 32 31 30 29 28 27 25 26 + 46 45 48 47 44 43 42 41 38 37 40 39 + operation j=19 : 19 20 24 23 22 21 44 43 47 48 46 45 + 41 42 40 39 38 37 1 2 6 5 4 3 + 32 31 36 35 33 34 26 25 29 30 28 27 + 18 17 16 15 13 14 8 7 12 11 9 10 + operation j=20 : 20 19 22 21 24 23 26 25 29 30 28 27 + 42 41 38 37 40 39 2 1 4 3 6 5 + 8 7 12 11 9 10 44 43 47 48 46 45 + 16 15 18 17 14 13 32 31 36 35 33 34 + operation j=21 : 21 22 23 24 20 19 47 48 44 43 45 46 + 40 39 41 42 37 38 32 31 36 35 33 34 + 1 2 6 5 4 3 16 15 18 17 14 13 + 29 30 26 25 27 28 7 8 9 10 12 11 + operation j=22 : 22 21 20 19 23 24 16 15 18 17 14 13 + 39 40 37 38 41 42 31 32 33 34 36 35 + 7 8 9 10 12 11 47 48 44 43 45 46 + 26 25 29 30 28 27 1 2 6 5 4 3 + operation j=23 : 23 24 21 22 19 20 29 30 26 25 27 28 + 38 37 42 41 39 40 8 7 12 11 9 10 + 2 1 4 3 6 5 18 17 16 15 13 14 + 47 48 44 43 45 46 31 32 33 34 36 35 + operation j=24 : 24 23 19 20 21 22 18 17 16 15 13 14 + 37 38 39 40 42 41 7 8 9 10 12 11 + 31 32 33 34 36 35 29 30 26 25 27 28 + 44 43 47 48 46 45 2 1 4 3 6 5 + operation j=25 : 25 26 30 29 28 27 43 44 45 46 48 47 + 36 35 32 31 34 33 6 5 1 2 3 4 + 40 39 41 42 37 38 20 19 23 24 22 21 + 17 18 14 13 15 16 10 9 11 12 7 8 + operation j=26 : 26 25 28 27 30 29 20 19 23 24 22 21 + 35 36 34 33 32 31 5 6 3 4 1 2 + 10 9 11 12 7 8 43 44 45 46 48 47 + 14 13 17 18 16 15 40 39 41 42 37 38 + operation j=27 : 27 28 29 30 26 25 45 46 43 44 47 48 + 32 31 36 35 33 34 40 39 41 42 37 38 + 6 5 1 2 3 4 14 13 17 18 16 15 + 23 24 20 19 21 22 9 10 7 8 11 12 + operation j=28 : 28 27 26 25 29 30 14 13 17 18 16 15 + 31 32 33 34 36 35 39 40 37 38 41 42 + 9 10 7 8 11 12 45 46 43 44 47 48 + 20 19 23 24 22 21 6 5 1 2 3 4 + operation j=29 : 29 30 27 28 25 26 23 24 20 19 21 22 + 34 33 35 36 31 32 10 9 11 12 7 8 + 5 6 3 4 1 2 17 18 14 13 15 16 + 45 46 43 44 47 48 39 40 37 38 41 42 + operation j=30 : 30 29 25 26 27 28 17 18 14 13 15 16 + 33 34 31 32 35 36 9 10 7 8 11 12 + 39 40 37 38 41 42 23 24 20 19 21 22 + 43 44 45 46 48 47 5 6 3 4 1 2 + operation j=31 : 31 32 35 36 34 33 2 1 4 3 5 6 + 28 27 30 29 25 26 22 21 24 23 19 20 + 48 47 46 45 44 43 8 7 12 11 10 9 + 42 41 38 37 39 40 15 16 13 14 18 17 + operation j=32 : 32 31 34 33 35 36 8 7 12 11 10 9 + 27 28 25 26 30 29 21 22 19 20 24 23 + 15 16 13 14 18 17 2 1 4 3 5 6 + 38 37 42 41 40 39 48 47 46 45 44 43 + operation j=33 : 33 34 36 35 32 31 4 3 2 1 6 5 + 30 29 28 27 26 25 48 47 46 45 44 43 + 22 21 24 23 19 20 38 37 42 41 40 39 + 12 11 8 7 9 10 16 15 18 17 13 14 + operation j=34 : 34 33 32 31 36 35 38 37 42 41 40 39 + 29 30 26 25 28 27 47 48 44 43 46 45 + 16 15 18 17 13 14 4 3 2 1 6 5 + 8 7 12 11 10 9 22 21 24 23 19 20 + operation j=35 : 35 36 31 32 33 34 42 41 38 37 39 40 + 26 25 29 30 27 28 16 15 18 17 13 14 + 47 48 44 43 46 45 12 11 8 7 9 10 + 2 1 4 3 5 6 21 22 19 20 24 23 + operation j=36 : 36 35 33 34 31 32 12 11 8 7 9 10 + 25 26 27 28 29 30 15 16 13 14 18 17 + 21 22 19 20 24 23 42 41 38 37 39 40 + 4 3 2 1 6 5 47 48 44 43 46 45 + operation j=37 : 37 38 41 42 40 39 3 4 5 6 1 2 + 24 23 22 21 20 19 46 45 48 47 43 44 + 28 27 30 29 25 26 34 33 35 36 32 31 + 11 12 10 9 7 8 14 13 17 18 15 16 + operation j=38 : 38 37 40 39 41 42 34 33 35 36 32 31 + 23 24 20 19 22 21 45 46 43 44 48 47 + 14 13 17 18 15 16 3 4 5 6 1 2 + 10 9 11 12 8 7 28 27 30 29 25 26 + operation j=39 : 39 40 42 41 38 37 5 6 3 4 2 1 + 22 21 24 23 19 20 28 27 30 29 25 26 + 46 45 48 47 43 44 10 9 11 12 8 7 + 35 36 34 33 31 32 13 14 15 16 17 18 + operation j=40 : 40 39 38 37 42 41 10 9 11 12 8 7 + 21 22 19 20 24 23 27 28 25 26 30 29 + 13 14 15 16 17 18 5 6 3 4 2 1 + 34 33 35 36 32 31 46 45 48 47 43 44 + operation j=41 : 41 42 37 38 39 40 11 12 10 9 7 8 + 19 20 21 22 23 24 13 14 15 16 17 18 + 27 28 25 26 30 29 35 36 34 33 31 32 + 3 4 5 6 1 2 45 46 43 44 48 47 + operation j=42 : 42 41 39 40 37 38 35 36 34 33 31 32 + 20 19 23 24 21 22 14 13 17 18 15 16 + 45 46 43 44 48 47 11 12 10 9 7 8 + 5 6 3 4 2 1 27 28 25 26 30 29 + operation j=43 : 43 44 48 47 46 45 25 26 27 28 30 29 + 12 11 8 7 10 9 4 3 2 1 5 6 + 38 37 42 41 39 40 19 20 21 22 24 23 + 15 16 13 14 17 18 34 33 35 36 31 32 + operation j=44 : 44 43 46 45 48 47 19 20 21 22 24 23 + 11 12 10 9 8 7 3 4 5 6 2 1 + 34 33 35 36 31 32 25 26 27 28 30 29 + 13 14 15 16 18 17 38 37 42 41 39 40 + operation j=45 : 45 46 47 48 44 43 27 28 25 26 29 30 + 8 7 12 11 9 10 38 37 42 41 39 40 + 4 3 2 1 5 6 13 14 15 16 18 17 + 21 22 19 20 23 24 33 34 31 32 35 36 + operation j=46 : 46 45 44 43 47 48 13 14 15 16 18 17 + 7 8 9 10 12 11 37 38 39 40 42 41 + 33 34 31 32 35 36 27 28 25 26 29 30 + 19 20 21 22 24 23 4 3 2 1 5 6 + operation j=47 : 47 48 45 46 43 44 21 22 19 20 23 24 + 10 9 11 12 7 8 34 33 35 36 31 32 + 3 4 5 6 2 1 15 16 13 14 17 18 + 27 28 25 26 29 30 37 38 39 40 42 41 + operation j=48 : 48 47 43 44 45 46 15 16 13 14 17 18 + 9 10 7 8 11 12 33 34 31 32 35 36 + 37 38 39 40 42 41 21 22 19 20 23 24 + 25 26 27 28 30 29 3 4 5 6 2 1 + + + Space group can be generated using 5 generators: 13 4 9 34 2 + + generators (in lattice coordinates): + +&gen 5 + + -1 0 0 0.00000 + 0 -1 0 0.00000 + 0 0 -1 0.00000 + + 0 -1 0 0.00000 + 1 -1 0 0.00000 + 0 -1 1 0.00000 + + 0 1 -1 0.00000 + 1 0 -1 0.00000 + 0 0 -1 0.00000 + + 0 -1 1 0.00000 + 0 -1 0 0.00000 + 1 -1 0 0.00000 + + -1 0 0 0.00000 + -1 1 0 0.00000 + -1 0 1 0.00000 + +/ ! end generators + + + + Atomic positions: + ----------------- + atom types = 1 + total = 1 + + lattice coordinates (scaled) Cartesian coordinates atom + + atom type 1: atomic identification number = 74.0 representative = 1 + 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 1 + +atoms% 1 atoms 1 + Z( 1)= 74 atoms 1 + 0.000000 0.000000 0.000000 1 + ---------------------------------------------------- + Suggested values for input: + + Atom Z lmax jri rmt dx + W 74 10 841 2.544787 0.015844 +k_max = 3.92960 +G_max =11.78881 + + =============================================== + === modifying atomic input for &(all)atom === + =============================================== + +for atom 1 ( W) changed rmt to 2.100000 +for atom 1 ( W) changed jri to 981 +for atom 1 ( W) changed lmax to 12 +for atom 1 ( W) changed lnonsph to 6 +for atom 1 ( W) set econfig to [Kr] 4d10 4f14 | 5s2 5p6 6s2 5d4 + corestates = 16 with 60.0 electrons + valence st.= 5 with 14.0 electrons +nlod = 2 llod = 1 : 5s5p + nlo( 1) = 2 llo = 0 1 + lonqn = 5 5 +line: 13>&comp +line: 14>gmax=15.0 gmaxxc=12.5 kmax=5.0 / + ---------- + core : 1 -1 2.0 + core : 2 -1 2.0 + core : 2 1 2.0 + core : 2 -2 4.0 + core : 3 -1 2.0 + core : 3 1 2.0 + core : 3 -2 4.0 + core : 3 2 4.0 + core : 3 -3 6.0 + core : 4 -1 2.0 + core : 4 1 2.0 + core : 4 -2 4.0 + core : 4 2 4.0 + core : 4 -3 6.0 + core : 4 3 6.0 + core : 4 -4 8.0 + valence : 5 -1 2.0 5s + valence : 5 1 2.0 5p + valence : 5 -2 4.0 5p + valence : 6 -1 2.0 6s + valence : 5 2 2.0 5d + valence : 5 -3 2.0 5d + ---------- +Valence Electrons = 14 + ---------------------------------------------------- + Suggested values for input: + + Atom Z lmax jri rmt dx + W 74 10 841 2.544787 0.015844 +k_max = 3.92960 +G_max =11.78881 +line: 15>&kpt +line: 16>div1=3 div2=3 div3=3 tkb=0.0005 / + 5.22007561238382 5.22007561238382 5.22007561238382 + -0.333333333333333 -0.333333333333333 -0.333333333333333 + body centered cubic + values accepted unchanged + 3 3 3 nmop(i),i=1,3 + orientation of boundary faces + 1 -1 -0.5740361 ifac,iside,orient for xvec + 2 -1 -0.0454288 ifac,iside,orient for xvec + 3 -1 -0.0256305 ifac,iside,orient for xvec + 4 -1 -0.0469244 ifac,iside,orient for xvec +Bravais lattice vectors + -3.013812 3.013812 3.013812 + 3.013812 -3.013812 3.013812 + 3.013812 3.013812 -3.013812 +reciprocal lattice vectors + 0.000000 1.042398 1.042398 + 1.042398 0.000000 1.042398 + 1.042398 1.042398 0.000000 + 3 3 3 Monkhorst-Pack-parameters + 0 nreg; k-points in irreducible wedge of BZ + Monkhorst-Pack-fractions + 0 nbound; no k-points on boundary of BZ + 1 idim + -0.3333333 + 0.0000000 + 0.3333333 + 2 idim + -0.3333333 + 0.0000000 + 0.3333333 + 3 idim + -0.3333333 + 0.0000000 + 0.3333333 + +k-point count: 4 + +k-point mesh: 3 3 3 +k-point density: 2.035038 2.035038 2.035038 + diff --git a/tests/parsers/fixtures/inpgen/broken_inpxml/out.error b/tests/parsers/fixtures/inpgen/broken_inpxml/out.error new file mode 100644 index 000000000..e69de29bb diff --git a/tests/parsers/fixtures/inpgen/default/inp.xml b/tests/parsers/fixtures/inpgen/default/inp.xml new file mode 100644 index 000000000..2bf34f24c --- /dev/null +++ b/tests/parsers/fixtures/inpgen/default/inp.xml @@ -0,0 +1,319 @@ + + + + A Fleur input generator calculation with aiida + + + + + + + + + + .0000000000 .0000000000 .0000000000 + + + + + + + -0.000000 0.333333 0.333333 + -0.333333 0.333333 0.333333 + -0.000000 0.000000 0.333333 + 0.000000 0.000000 0.000000 + + + + + + + + + + + 1 0 0 .0000000000 + 0 1 0 .0000000000 + 0 0 1 .0000000000 + + + -1 0 0 .0000000000 + -1 1 0 .0000000000 + -1 0 1 .0000000000 + + + 1 -1 0 .0000000000 + 0 -1 0 .0000000000 + 0 -1 1 .0000000000 + + + 0 -1 0 .0000000000 + 1 -1 0 .0000000000 + 0 -1 1 .0000000000 + + + -1 1 0 .0000000000 + -1 0 0 .0000000000 + -1 0 1 .0000000000 + + + 0 1 0 .0000000000 + 1 0 0 .0000000000 + 0 0 1 .0000000000 + + + 1 0 -1 .0000000000 + 0 1 -1 .0000000000 + 0 0 -1 .0000000000 + + + 0 0 -1 .0000000000 + 0 1 -1 .0000000000 + 1 0 -1 .0000000000 + + + 0 1 -1 .0000000000 + 1 0 -1 .0000000000 + 0 0 -1 .0000000000 + + + 0 1 -1 .0000000000 + 0 0 -1 .0000000000 + 1 0 -1 .0000000000 + + + 1 0 -1 .0000000000 + 0 0 -1 .0000000000 + 0 1 -1 .0000000000 + + + 0 0 -1 .0000000000 + 1 0 -1 .0000000000 + 0 1 -1 .0000000000 + + + -1 0 0 .0000000000 + 0 -1 0 .0000000000 + 0 0 -1 .0000000000 + + + 1 0 0 .0000000000 + 1 -1 0 .0000000000 + 1 0 -1 .0000000000 + + + 0 -1 0 .0000000000 + -1 0 0 .0000000000 + 0 0 -1 .0000000000 + + + 1 -1 0 .0000000000 + 1 0 0 .0000000000 + 1 0 -1 .0000000000 + + + 0 1 0 .0000000000 + -1 1 0 .0000000000 + 0 1 -1 .0000000000 + + + -1 1 0 .0000000000 + 0 1 0 .0000000000 + 0 1 -1 .0000000000 + + + -1 0 0 .0000000000 + 0 0 -1 .0000000000 + 0 -1 0 .0000000000 + + + 1 0 0 .0000000000 + 1 0 -1 .0000000000 + 1 -1 0 .0000000000 + + + 0 -1 0 .0000000000 + 0 0 -1 .0000000000 + -1 0 0 .0000000000 + + + 1 -1 0 .0000000000 + 1 0 -1 .0000000000 + 1 0 0 .0000000000 + + + 0 1 0 .0000000000 + 0 1 -1 .0000000000 + -1 1 0 .0000000000 + + + -1 1 0 .0000000000 + 0 1 -1 .0000000000 + 0 1 0 .0000000000 + + + 0 0 -1 .0000000000 + -1 0 0 .0000000000 + 0 -1 0 .0000000000 + + + 1 0 -1 .0000000000 + 1 0 0 .0000000000 + 1 -1 0 .0000000000 + + + 0 0 -1 .0000000000 + 0 -1 0 .0000000000 + -1 0 0 .0000000000 + + + 1 0 -1 .0000000000 + 1 -1 0 .0000000000 + 1 0 0 .0000000000 + + + 0 1 -1 .0000000000 + 0 1 0 .0000000000 + -1 1 0 .0000000000 + + + 0 1 -1 .0000000000 + -1 1 0 .0000000000 + 0 1 0 .0000000000 + + + -1 0 1 .0000000000 + -1 1 0 .0000000000 + -1 0 0 .0000000000 + + + 0 0 1 .0000000000 + 0 1 0 .0000000000 + 1 0 0 .0000000000 + + + 0 -1 1 .0000000000 + 1 -1 0 .0000000000 + 0 -1 0 .0000000000 + + + 0 -1 1 .0000000000 + 0 -1 0 .0000000000 + 1 -1 0 .0000000000 + + + -1 0 1 .0000000000 + -1 0 0 .0000000000 + -1 1 0 .0000000000 + + + 0 0 1 .0000000000 + 1 0 0 .0000000000 + 0 1 0 .0000000000 + + + 1 -1 0 .0000000000 + 0 -1 1 .0000000000 + 0 -1 0 .0000000000 + + + 0 -1 0 .0000000000 + 0 -1 1 .0000000000 + 1 -1 0 .0000000000 + + + -1 1 0 .0000000000 + -1 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 + -1 0 1 .0000000000 + -1 1 0 .0000000000 + + + 0 0 1 .0000000000 + -1 0 1 .0000000000 + 0 -1 1 .0000000000 + + + -1 0 1 .0000000000 + 0 0 1 .0000000000 + 0 -1 1 .0000000000 + + + 0 0 1 .0000000000 + 0 -1 1 .0000000000 + -1 0 1 .0000000000 + + + -1 0 1 .0000000000 + 0 -1 1 .0000000000 + 0 0 1 .0000000000 + + + 0 -1 1 .0000000000 + 0 0 1 .0000000000 + -1 0 1 .0000000000 + + + 0 -1 1 .0000000000 + -1 0 1 .0000000000 + 0 0 1 .0000000000 + + + + + -3.013812060000000 3.013812060000000 3.013812060000000 + 3.013812060000000 -3.013812060000000 3.013812060000000 + 3.013812060000000 3.013812060000000 -3.013812060000000 + + + + + + + + + + + + [Kr] (4d3/2) (4d5/2) (4f5/2) (4f7/2) + (5s1/2) (5p1/2) (5p3/2) (6s1/2) (5d3/2) (5d5/2) + + + + + + + + + + .0000000000 .0000000000 .0000000000 + + + + + + + + + + + + + + + + + diff --git a/tests/parsers/fixtures/inpgen/default/out b/tests/parsers/fixtures/inpgen/default/out new file mode 100644 index 000000000..fca99897b --- /dev/null +++ b/tests/parsers/fixtures/inpgen/default/out @@ -0,0 +1,687 @@ +line: 1>A Fleur input generator calculation with aiida +line: 2>&input cartesian=F / +line: 3>-3.0138120600 3.0138120600 3.0138120600 +line: 4>3.0138120600 -3.0138120600 3.0138120600 +line: 5>3.0138120600 3.0138120600 -3.0138120600 +line: 6>1.0000000000 +line: 7>1.0000000000 1.0000000000 1.0000000000 +line: 8> +line: 9>1 +line: 10>74 0.0000000000 0.0000000000 0.0000000000 +line: 11>&atom +line: 12>econfig="[Kr] 4d10 4f14 | 5s2 5p6 6s2 5d4" element="W" jri=981 lm + + A Fleur input generator calculation with aiida + + film= F cartesian= F + checkinp= F symor= F + +a1 = -3.01381 3.01381 3.01381 +a2 = 3.01381 -3.01381 3.01381 +a3 = 3.01381 3.01381 -3.01381 + +dvac= -3.01381 aa = 1.00000 +scale = 1.00000 1.00000 1.00000 + +natin= 1 Z = 74 + positions: + 0.00000 0.00000 0.00000 + + generators: 0 (excluding identity) + + + Lattice information: + -------------------- + + overall lattice constant a0 = 1.000000 bohr + + real-space primitive lattice vectors in units of a_{x,y,z} + a_1: -3.013812 3.013812 3.013812 + a_2: 3.013812 -3.013812 3.013812 + a_3: 3.013812 3.013812 -3.013812 + + lattice constants a_x, a_y, a_z = 1.000000 1.000000 1.000000 + volume of unit cell (a.u.^3) = 109.498581 + +dbg: lattice matrices + 109.498580847925 +dbg: as : + -3.013812 3.013812 3.013812 + 3.013812 -3.013812 3.013812 + 3.013812 3.013812 -3.013812 +dbg: bs : + 0.000000 0.165903 0.165903 + 0.165903 0.000000 0.165903 + 0.165903 0.165903 0.000000 +dbg: amat : + -3.013812 3.013812 3.013812 + 3.013812 -3.013812 3.013812 + 3.013812 3.013812 -3.013812 +dbg: bmat : + 0.000000 0.521199 0.521199 + 0.521199 0.000000 0.521199 + 0.521199 0.521199 0.000000 +dbg: amatinv : + 0.000000 0.165903 0.165903 + 0.165903 0.000000 0.165903 + 0.165903 0.165903 0.000000 +dbg: aamat : + 27.249189 -9.083063 -9.083063 + -9.083063 27.249189 -9.083063 + -9.083063 -9.083063 27.249189 +dbg: bbmat : + 0.543297 0.271649 0.271649 + 0.271649 0.543297 0.271649 + 0.271649 0.271649 0.543297 + +dbg: lattice vectors : +vector 1 : -3.01381 3.01381 3.01381 length : 5.22008 +vector 2 : 3.01381 -3.01381 3.01381 length : 5.22008 +vector 3 : 3.01381 3.01381 -3.01381 length : 5.22008 +angle between vectors (1,2) =109.47122 +angle between vectors (1,3) =109.47122 +angle between vectors (2,3) =109.47122 + +dbg: reciprocal lattice vectors : +vector 1 : 0.00000 0.52120 0.52120 length : 0.73709 +vector 2 : 0.52120 0.00000 0.52120 length : 0.73709 +vector 3 : 0.52120 0.52120 0.00000 length : 0.73709 +angle between vectors (1,2) = 60.00000 +angle between vectors (1,3) = 60.00000 +angle between vectors (2,3) = 60.00000 + + + Point group of the Bravais lattice has 48 operations + + DBG: symor,zorth,oldfleur : T F F + DBG: optype : 1 -2 -2 3 3 -2 -2 3 2 -4 3 -4 -1 2 2 -3 -3 2 2 -2 -3 4 4 -3 -3 4 2 -3 -2 4 3 -2 -4 2 -4 3 3 -4 -4 3 -2 2 4 -3 -3 2 4 -2 + DBG: invsym,invs,zrfs,invs2 : T T F F + DBG: (before reorder) invsop,zrfsop,invs2op : 13 7 46 + + Space group information: + ------------------------ + 48 operations + space group is symmorphic + has inversion symmetry + + + Operations: (in International notation) + --------------------------------------- + lattice coordinates (scaled) Cartesian coordinates + + operation 1: 1 (inverse = 1) + ( 1 0 0 ) ( 0.000 ) ( 1.00000 0.00000 0.00000 ) ( 0.000 ) + ( 0 1 0 ) ( 0.000 ) ( 0.00000 1.00000 -0.00000 ) ( 0.000 ) + ( 0 0 1 ) ( 0.000 ) ( -0.00000 -0.00000 1.00000 ) ( 0.000 ) + _ + operation 2: 2 (inverse = 2) + ( -1 0 0 ) ( 0.000 ) ( 1.00000 -0.00000 -0.00000 ) ( 0.000 ) + ( -1 1 0 ) ( 0.000 ) ( 0.00000 0.00000 -1.00000 ) ( 0.000 ) + ( -1 0 1 ) ( 0.000 ) ( -0.00000 -1.00000 0.00000 ) ( 0.000 ) + _ + operation 3: 2 (inverse = 3) + ( 1 -1 0 ) ( 0.000 ) ( -0.00000 0.00000 -1.00000 ) ( 0.000 ) + ( 0 -1 0 ) ( 0.000 ) ( 0.00000 1.00000 -0.00000 ) ( 0.000 ) + ( 0 -1 1 ) ( 0.000 ) ( -1.00000 -0.00000 0.00000 ) ( 0.000 ) + + operation 4: 3 (inverse = 5) + ( 0 -1 0 ) ( 0.000 ) ( -0.00000 1.00000 -0.00000 ) ( 0.000 ) + ( 1 -1 0 ) ( 0.000 ) ( 0.00000 0.00000 -1.00000 ) ( 0.000 ) + ( 0 -1 1 ) ( 0.000 ) ( -1.00000 -0.00000 0.00000 ) ( 0.000 ) + + operation 5: 3 (inverse = 4) + ( -1 1 0 ) ( 0.000 ) ( 0.00000 -0.00000 -1.00000 ) ( 0.000 ) + ( -1 0 0 ) ( 0.000 ) ( 1.00000 0.00000 -0.00000 ) ( 0.000 ) + ( -1 0 1 ) ( 0.000 ) ( -0.00000 -1.00000 0.00000 ) ( 0.000 ) + _ + operation 6: 2 (inverse = 6) + ( 0 1 0 ) ( 0.000 ) ( 0.00000 1.00000 0.00000 ) ( 0.000 ) + ( 1 0 0 ) ( 0.000 ) ( 1.00000 0.00000 -0.00000 ) ( 0.000 ) + ( 0 0 1 ) ( 0.000 ) ( -0.00000 -0.00000 1.00000 ) ( 0.000 ) + _ + operation 7: 2 (inverse = 7) + ( 1 0 -1 ) ( 0.000 ) ( -0.00000 -1.00000 0.00000 ) ( 0.000 ) + ( 0 1 -1 ) ( 0.000 ) ( -1.00000 -0.00000 -0.00000 ) ( 0.000 ) + ( 0 0 -1 ) ( 0.000 ) ( 0.00000 0.00000 1.00000 ) ( 0.000 ) + + operation 8: 3 (inverse = 31) + ( 0 0 -1 ) ( 0.000 ) ( -0.00000 -0.00000 1.00000 ) ( 0.000 ) + ( 0 1 -1 ) ( 0.000 ) ( -1.00000 0.00000 0.00000 ) ( 0.000 ) + ( 1 0 -1 ) ( 0.000 ) ( 0.00000 -1.00000 -0.00000 ) ( 0.000 ) + + operation 9: 2 (inverse = 9) + ( 0 1 -1 ) ( 0.000 ) ( -1.00000 -0.00000 0.00000 ) ( 0.000 ) + ( 1 0 -1 ) ( 0.000 ) ( -0.00000 -1.00000 -0.00000 ) ( 0.000 ) + ( 0 0 -1 ) ( 0.000 ) ( 0.00000 0.00000 1.00000 ) ( 0.000 ) + _ + operation 10: 4 (inverse = 33) + ( 0 1 -1 ) ( 0.000 ) ( -1.00000 -0.00000 0.00000 ) ( 0.000 ) + ( 0 0 -1 ) ( 0.000 ) ( -0.00000 0.00000 1.00000 ) ( 0.000 ) + ( 1 0 -1 ) ( 0.000 ) ( 0.00000 -1.00000 -0.00000 ) ( 0.000 ) + + operation 11: 3 (inverse = 37) + ( 1 0 -1 ) ( 0.000 ) ( -0.00000 -1.00000 0.00000 ) ( 0.000 ) + ( 0 0 -1 ) ( 0.000 ) ( 0.00000 -0.00000 1.00000 ) ( 0.000 ) + ( 0 1 -1 ) ( 0.000 ) ( -1.00000 0.00000 -0.00000 ) ( 0.000 ) + _ + operation 12: 4 (inverse = 39) + ( 0 0 -1 ) ( 0.000 ) ( -0.00000 -0.00000 1.00000 ) ( 0.000 ) + ( 1 0 -1 ) ( 0.000 ) ( 0.00000 -1.00000 0.00000 ) ( 0.000 ) + ( 0 1 -1 ) ( 0.000 ) ( -1.00000 0.00000 -0.00000 ) ( 0.000 ) + _ + operation 13: 1 (inverse = 13) + ( -1 0 0 ) ( 0.000 ) ( -1.00000 -0.00000 -0.00000 ) ( 0.000 ) + ( 0 -1 0 ) ( 0.000 ) ( -0.00000 -1.00000 0.00000 ) ( 0.000 ) + ( 0 0 -1 ) ( 0.000 ) ( 0.00000 0.00000 -1.00000 ) ( 0.000 ) + + operation 14: 2 (inverse = 14) + ( 1 0 0 ) ( 0.000 ) ( -1.00000 0.00000 0.00000 ) ( 0.000 ) + ( 1 -1 0 ) ( 0.000 ) ( -0.00000 -0.00000 1.00000 ) ( 0.000 ) + ( 1 0 -1 ) ( 0.000 ) ( 0.00000 1.00000 -0.00000 ) ( 0.000 ) + + operation 15: 2 (inverse = 15) + ( 0 -1 0 ) ( 0.000 ) ( -0.00000 -1.00000 -0.00000 ) ( 0.000 ) + ( -1 0 0 ) ( 0.000 ) ( -1.00000 -0.00000 0.00000 ) ( 0.000 ) + ( 0 0 -1 ) ( 0.000 ) ( 0.00000 0.00000 -1.00000 ) ( 0.000 ) + _ + operation 16: 3 (inverse = 17) + ( 1 -1 0 ) ( 0.000 ) ( -0.00000 0.00000 1.00000 ) ( 0.000 ) + ( 1 0 0 ) ( 0.000 ) ( -1.00000 -0.00000 0.00000 ) ( 0.000 ) + ( 1 0 -1 ) ( 0.000 ) ( 0.00000 1.00000 -0.00000 ) ( 0.000 ) + _ + operation 17: 3 (inverse = 16) + ( 0 1 0 ) ( 0.000 ) ( 0.00000 -1.00000 0.00000 ) ( 0.000 ) + ( -1 1 0 ) ( 0.000 ) ( -0.00000 -0.00000 1.00000 ) ( 0.000 ) + ( 0 1 -1 ) ( 0.000 ) ( 1.00000 0.00000 -0.00000 ) ( 0.000 ) + + operation 18: 2 (inverse = 18) + ( -1 1 0 ) ( 0.000 ) ( 0.00000 -0.00000 1.00000 ) ( 0.000 ) + ( 0 1 0 ) ( 0.000 ) ( -0.00000 -1.00000 0.00000 ) ( 0.000 ) + ( 0 1 -1 ) ( 0.000 ) ( 1.00000 0.00000 -0.00000 ) ( 0.000 ) + + operation 19: 2 (inverse = 19) + ( -1 0 0 ) ( 0.000 ) ( -1.00000 -0.00000 -0.00000 ) ( 0.000 ) + ( 0 0 -1 ) ( 0.000 ) ( -0.00000 0.00000 -1.00000 ) ( 0.000 ) + ( 0 -1 0 ) ( 0.000 ) ( 0.00000 -1.00000 0.00000 ) ( 0.000 ) + _ + operation 20: 2 (inverse = 20) + ( 1 0 0 ) ( 0.000 ) ( -1.00000 0.00000 0.00000 ) ( 0.000 ) + ( 1 0 -1 ) ( 0.000 ) ( -0.00000 1.00000 -0.00000 ) ( 0.000 ) + ( 1 -1 0 ) ( 0.000 ) ( 0.00000 -0.00000 1.00000 ) ( 0.000 ) + _ + operation 21: 3 (inverse = 25) + ( 0 -1 0 ) ( 0.000 ) ( -0.00000 -1.00000 -0.00000 ) ( 0.000 ) + ( 0 0 -1 ) ( 0.000 ) ( 0.00000 -0.00000 -1.00000 ) ( 0.000 ) + ( -1 0 0 ) ( 0.000 ) ( -1.00000 0.00000 0.00000 ) ( 0.000 ) + + operation 22: 4 (inverse = 43) + ( 1 -1 0 ) ( 0.000 ) ( -0.00000 0.00000 1.00000 ) ( 0.000 ) + ( 1 0 -1 ) ( 0.000 ) ( 0.00000 1.00000 0.00000 ) ( 0.000 ) + ( 1 0 0 ) ( 0.000 ) ( -1.00000 -0.00000 -0.00000 ) ( 0.000 ) + + operation 23: 4 (inverse = 26) + ( 0 1 0 ) ( 0.000 ) ( 0.00000 -1.00000 0.00000 ) ( 0.000 ) + ( 0 1 -1 ) ( 0.000 ) ( 1.00000 -0.00000 -0.00000 ) ( 0.000 ) + ( -1 1 0 ) ( 0.000 ) ( -0.00000 0.00000 1.00000 ) ( 0.000 ) + _ + operation 24: 3 (inverse = 44) + ( -1 1 0 ) ( 0.000 ) ( 0.00000 -0.00000 1.00000 ) ( 0.000 ) + ( 0 1 -1 ) ( 0.000 ) ( 1.00000 0.00000 0.00000 ) ( 0.000 ) + ( 0 1 0 ) ( 0.000 ) ( -0.00000 -1.00000 -0.00000 ) ( 0.000 ) + _ + operation 25: 3 (inverse = 21) + ( 0 0 -1 ) ( 0.000 ) ( -0.00000 -0.00000 -1.00000 ) ( 0.000 ) + ( -1 0 0 ) ( 0.000 ) ( -1.00000 0.00000 -0.00000 ) ( 0.000 ) + ( 0 -1 0 ) ( 0.000 ) ( 0.00000 -1.00000 0.00000 ) ( 0.000 ) + + operation 26: 4 (inverse = 23) + ( 1 0 -1 ) ( 0.000 ) ( -0.00000 1.00000 0.00000 ) ( 0.000 ) + ( 1 0 0 ) ( 0.000 ) ( -1.00000 0.00000 -0.00000 ) ( 0.000 ) + ( 1 -1 0 ) ( 0.000 ) ( 0.00000 -0.00000 1.00000 ) ( 0.000 ) + + operation 27: 2 (inverse = 27) + ( 0 0 -1 ) ( 0.000 ) ( -0.00000 -0.00000 -1.00000 ) ( 0.000 ) + ( 0 -1 0 ) ( 0.000 ) ( 0.00000 -1.00000 -0.00000 ) ( 0.000 ) + ( -1 0 0 ) ( 0.000 ) ( -1.00000 0.00000 0.00000 ) ( 0.000 ) + _ + operation 28: 3 (inverse = 45) + ( 1 0 -1 ) ( 0.000 ) ( -0.00000 1.00000 0.00000 ) ( 0.000 ) + ( 1 -1 0 ) ( 0.000 ) ( 0.00000 0.00000 1.00000 ) ( 0.000 ) + ( 1 0 0 ) ( 0.000 ) ( -1.00000 -0.00000 -0.00000 ) ( 0.000 ) + _ + operation 29: 2 (inverse = 29) + ( 0 1 -1 ) ( 0.000 ) ( 1.00000 -0.00000 0.00000 ) ( 0.000 ) + ( 0 1 0 ) ( 0.000 ) ( 0.00000 -1.00000 -0.00000 ) ( 0.000 ) + ( -1 1 0 ) ( 0.000 ) ( -0.00000 0.00000 1.00000 ) ( 0.000 ) + + operation 30: 4 (inverse = 47) + ( 0 1 -1 ) ( 0.000 ) ( 1.00000 -0.00000 0.00000 ) ( 0.000 ) + ( -1 1 0 ) ( 0.000 ) ( 0.00000 0.00000 1.00000 ) ( 0.000 ) + ( 0 1 0 ) ( 0.000 ) ( -0.00000 -1.00000 -0.00000 ) ( 0.000 ) + + operation 31: 3 (inverse = 8) + ( -1 0 1 ) ( 0.000 ) ( 0.00000 -1.00000 -0.00000 ) ( 0.000 ) + ( -1 1 0 ) ( 0.000 ) ( -0.00000 -0.00000 -1.00000 ) ( 0.000 ) + ( -1 0 0 ) ( 0.000 ) ( 1.00000 0.00000 0.00000 ) ( 0.000 ) + _ + operation 32: 2 (inverse = 32) + ( 0 0 1 ) ( 0.000 ) ( 0.00000 0.00000 1.00000 ) ( 0.000 ) + ( 0 1 0 ) ( 0.000 ) ( -0.00000 1.00000 0.00000 ) ( 0.000 ) + ( 1 0 0 ) ( 0.000 ) ( 1.00000 -0.00000 -0.00000 ) ( 0.000 ) + _ + operation 33: 4 (inverse = 10) + ( 0 -1 1 ) ( 0.000 ) ( -1.00000 0.00000 -0.00000 ) ( 0.000 ) + ( 1 -1 0 ) ( 0.000 ) ( -0.00000 -0.00000 -1.00000 ) ( 0.000 ) + ( 0 -1 0 ) ( 0.000 ) ( 0.00000 1.00000 0.00000 ) ( 0.000 ) + + operation 34: 2 (inverse = 34) + ( 0 -1 1 ) ( 0.000 ) ( -1.00000 0.00000 -0.00000 ) ( 0.000 ) + ( 0 -1 0 ) ( 0.000 ) ( -0.00000 1.00000 0.00000 ) ( 0.000 ) + ( 1 -1 0 ) ( 0.000 ) ( 0.00000 -0.00000 -1.00000 ) ( 0.000 ) + _ + operation 35: 4 (inverse = 38) + ( -1 0 1 ) ( 0.000 ) ( 0.00000 -1.00000 -0.00000 ) ( 0.000 ) + ( -1 0 0 ) ( 0.000 ) ( 1.00000 -0.00000 0.00000 ) ( 0.000 ) + ( -1 1 0 ) ( 0.000 ) ( -0.00000 0.00000 -1.00000 ) ( 0.000 ) + + operation 36: 3 (inverse = 40) + ( 0 0 1 ) ( 0.000 ) ( 0.00000 0.00000 1.00000 ) ( 0.000 ) + ( 1 0 0 ) ( 0.000 ) ( 1.00000 -0.00000 0.00000 ) ( 0.000 ) + ( 0 1 0 ) ( 0.000 ) ( -0.00000 1.00000 -0.00000 ) ( 0.000 ) + + operation 37: 3 (inverse = 11) + ( 1 -1 0 ) ( 0.000 ) ( -0.00000 0.00000 -1.00000 ) ( 0.000 ) + ( 0 -1 1 ) ( 0.000 ) ( -1.00000 -0.00000 -0.00000 ) ( 0.000 ) + ( 0 -1 0 ) ( 0.000 ) ( 0.00000 1.00000 0.00000 ) ( 0.000 ) + _ + operation 38: 4 (inverse = 35) + ( 0 -1 0 ) ( 0.000 ) ( -0.00000 1.00000 -0.00000 ) ( 0.000 ) + ( 0 -1 1 ) ( 0.000 ) ( -1.00000 0.00000 0.00000 ) ( 0.000 ) + ( 1 -1 0 ) ( 0.000 ) ( 0.00000 -0.00000 -1.00000 ) ( 0.000 ) + _ + operation 39: 4 (inverse = 12) + ( -1 1 0 ) ( 0.000 ) ( 0.00000 -0.00000 -1.00000 ) ( 0.000 ) + ( -1 0 1 ) ( 0.000 ) ( -0.00000 -1.00000 -0.00000 ) ( 0.000 ) + ( -1 0 0 ) ( 0.000 ) ( 1.00000 0.00000 0.00000 ) ( 0.000 ) + + operation 40: 3 (inverse = 36) + ( 0 1 0 ) ( 0.000 ) ( 0.00000 1.00000 0.00000 ) ( 0.000 ) + ( 0 0 1 ) ( 0.000 ) ( -0.00000 0.00000 1.00000 ) ( 0.000 ) + ( 1 0 0 ) ( 0.000 ) ( 1.00000 -0.00000 -0.00000 ) ( 0.000 ) + _ + operation 41: 2 (inverse = 41) + ( 1 0 0 ) ( 0.000 ) ( 1.00000 0.00000 0.00000 ) ( 0.000 ) + ( 0 0 1 ) ( 0.000 ) ( 0.00000 -0.00000 1.00000 ) ( 0.000 ) + ( 0 1 0 ) ( 0.000 ) ( -0.00000 1.00000 -0.00000 ) ( 0.000 ) + + operation 42: 2 (inverse = 42) + ( -1 0 0 ) ( 0.000 ) ( 1.00000 -0.00000 -0.00000 ) ( 0.000 ) + ( -1 0 1 ) ( 0.000 ) ( 0.00000 -1.00000 0.00000 ) ( 0.000 ) + ( -1 1 0 ) ( 0.000 ) ( -0.00000 0.00000 -1.00000 ) ( 0.000 ) + + operation 43: 4 (inverse = 22) + ( 0 0 1 ) ( 0.000 ) ( 0.00000 0.00000 -1.00000 ) ( 0.000 ) + ( -1 0 1 ) ( 0.000 ) ( -0.00000 1.00000 -0.00000 ) ( 0.000 ) + ( 0 -1 1 ) ( 0.000 ) ( 1.00000 -0.00000 0.00000 ) ( 0.000 ) + _ + operation 44: 3 (inverse = 24) + ( -1 0 1 ) ( 0.000 ) ( 0.00000 1.00000 -0.00000 ) ( 0.000 ) + ( 0 0 1 ) ( 0.000 ) ( -0.00000 0.00000 -1.00000 ) ( 0.000 ) + ( 0 -1 1 ) ( 0.000 ) ( 1.00000 -0.00000 0.00000 ) ( 0.000 ) + _ + operation 45: 3 (inverse = 28) + ( 0 0 1 ) ( 0.000 ) ( 0.00000 0.00000 -1.00000 ) ( 0.000 ) + ( 0 -1 1 ) ( 0.000 ) ( 1.00000 -0.00000 -0.00000 ) ( 0.000 ) + ( -1 0 1 ) ( 0.000 ) ( -0.00000 1.00000 0.00000 ) ( 0.000 ) + + operation 46: 2 (inverse = 46) + ( -1 0 1 ) ( 0.000 ) ( 0.00000 1.00000 -0.00000 ) ( 0.000 ) + ( 0 -1 1 ) ( 0.000 ) ( 1.00000 0.00000 0.00000 ) ( 0.000 ) + ( 0 0 1 ) ( 0.000 ) ( -0.00000 -0.00000 -1.00000 ) ( 0.000 ) + + operation 47: 4 (inverse = 30) + ( 0 -1 1 ) ( 0.000 ) ( 1.00000 0.00000 -0.00000 ) ( 0.000 ) + ( 0 0 1 ) ( 0.000 ) ( 0.00000 -0.00000 -1.00000 ) ( 0.000 ) + ( -1 0 1 ) ( 0.000 ) ( -0.00000 1.00000 0.00000 ) ( 0.000 ) + _ + operation 48: 2 (inverse = 48) + ( 0 -1 1 ) ( 0.000 ) ( 1.00000 0.00000 -0.00000 ) ( 0.000 ) + ( -1 0 1 ) ( 0.000 ) ( 0.00000 1.00000 0.00000 ) ( 0.000 ) + ( 0 0 1 ) ( 0.000 ) ( -0.00000 -0.00000 -1.00000 ) ( 0.000 ) + + Multiplcation table: {R_j|t_j}{R_i|t_i} + operation j= 1 : 1 2 3 4 5 6 7 8 9 10 11 12 + 13 14 15 16 17 18 19 20 21 22 23 24 + 25 26 27 28 29 30 31 32 33 34 35 36 + 37 38 39 40 41 42 43 44 45 46 47 48 + operation j= 2 : 2 1 5 6 3 4 31 32 33 34 35 36 + 14 13 17 18 15 16 20 19 23 24 21 22 + 43 44 45 46 47 48 7 8 9 10 11 12 + 39 40 37 38 42 41 25 26 27 28 29 30 + operation j= 3 : 3 4 1 2 6 5 37 38 39 40 41 42 + 18 17 16 15 14 13 44 43 47 48 45 46 + 26 25 29 30 27 28 33 34 31 32 36 35 + 7 8 9 10 11 12 20 19 23 24 21 22 + operation j= 4 : 4 3 6 5 1 2 33 34 31 32 36 35 + 17 18 14 13 16 15 43 44 45 46 47 48 + 20 19 23 24 21 22 37 38 39 40 41 42 + 9 10 7 8 12 11 26 25 29 30 27 28 + operation j= 5 : 5 6 2 1 4 3 39 40 37 38 42 41 + 16 15 18 17 13 14 26 25 29 30 27 28 + 44 43 47 48 45 46 9 10 7 8 12 11 + 31 32 33 34 35 36 19 20 21 22 23 24 + operation j= 6 : 6 5 4 3 2 1 9 10 7 8 12 11 + 15 16 13 14 18 17 25 26 27 28 29 30 + 19 20 21 22 23 24 39 40 37 38 42 41 + 33 34 31 32 36 35 44 43 47 48 45 46 + operation j= 7 : 7 8 11 12 10 9 1 2 6 5 3 4 + 46 45 48 47 43 44 24 23 22 21 20 19 + 30 29 28 27 26 25 32 31 36 35 34 33 + 41 42 40 39 37 38 17 18 14 13 16 15 + operation j= 8 : 8 7 10 9 11 12 32 31 36 35 34 33 + 45 46 43 44 48 47 23 24 20 19 22 21 + 17 18 14 13 16 15 1 2 6 5 3 4 + 40 39 41 42 38 37 30 29 28 27 26 25 + operation j= 9 : 9 10 12 11 8 7 6 5 1 2 4 3 + 48 47 46 45 44 43 30 29 28 27 26 25 + 24 23 22 21 20 19 40 39 41 42 38 37 + 36 35 32 31 33 34 18 17 16 15 14 13 + operation j=10 : 10 9 8 7 12 11 40 39 41 42 38 37 + 47 48 44 43 46 45 29 30 26 25 28 27 + 18 17 16 15 14 13 6 5 1 2 4 3 + 32 31 36 35 34 33 24 23 22 21 20 19 + operation j=11 : 11 12 7 8 9 10 41 42 40 39 37 38 + 44 43 47 48 45 46 18 17 16 15 14 13 + 29 30 26 25 28 27 36 35 32 31 33 34 + 1 2 6 5 3 4 23 24 20 19 22 21 + operation j=12 : 12 11 9 10 7 8 36 35 32 31 33 34 + 43 44 45 46 47 48 17 18 14 13 16 15 + 23 24 20 19 22 21 41 42 40 39 37 38 + 6 5 1 2 4 3 29 30 26 25 28 27 + operation j=13 : 13 14 18 17 16 15 46 45 48 47 44 43 + 1 2 6 5 4 3 41 42 40 39 38 37 + 36 35 32 31 34 33 28 27 30 29 26 25 + 24 23 22 21 19 20 12 11 8 7 10 9 + operation j=14 : 14 13 16 15 18 17 28 27 30 29 26 25 + 2 1 4 3 6 5 42 41 38 37 40 39 + 12 11 8 7 10 9 46 45 48 47 44 43 + 22 21 24 23 20 19 36 35 32 31 34 33 + operation j=15 : 15 16 17 18 14 13 48 47 46 45 43 44 + 6 5 1 2 3 4 36 35 32 31 34 33 + 41 42 40 39 38 37 22 21 24 23 20 19 + 30 29 28 27 25 26 11 12 10 9 8 7 + operation j=16 : 16 15 14 13 17 18 22 21 24 23 20 19 + 5 6 3 4 1 2 35 36 34 33 32 31 + 11 12 10 9 8 7 48 47 46 45 43 44 + 28 27 30 29 26 25 41 42 40 39 38 37 + operation j=17 : 17 18 15 16 13 14 30 29 28 27 25 26 + 4 3 2 1 5 6 12 11 8 7 10 9 + 42 41 38 37 40 39 24 23 22 21 19 20 + 48 47 46 45 43 44 35 36 34 33 32 31 + operation j=18 : 18 17 13 14 15 16 24 23 22 21 19 20 + 3 4 5 6 2 1 11 12 10 9 8 7 + 35 36 34 33 32 31 30 29 28 27 25 26 + 46 45 48 47 44 43 42 41 38 37 40 39 + operation j=19 : 19 20 24 23 22 21 44 43 47 48 46 45 + 41 42 40 39 38 37 1 2 6 5 4 3 + 32 31 36 35 33 34 26 25 29 30 28 27 + 18 17 16 15 13 14 8 7 12 11 9 10 + operation j=20 : 20 19 22 21 24 23 26 25 29 30 28 27 + 42 41 38 37 40 39 2 1 4 3 6 5 + 8 7 12 11 9 10 44 43 47 48 46 45 + 16 15 18 17 14 13 32 31 36 35 33 34 + operation j=21 : 21 22 23 24 20 19 47 48 44 43 45 46 + 40 39 41 42 37 38 32 31 36 35 33 34 + 1 2 6 5 4 3 16 15 18 17 14 13 + 29 30 26 25 27 28 7 8 9 10 12 11 + operation j=22 : 22 21 20 19 23 24 16 15 18 17 14 13 + 39 40 37 38 41 42 31 32 33 34 36 35 + 7 8 9 10 12 11 47 48 44 43 45 46 + 26 25 29 30 28 27 1 2 6 5 4 3 + operation j=23 : 23 24 21 22 19 20 29 30 26 25 27 28 + 38 37 42 41 39 40 8 7 12 11 9 10 + 2 1 4 3 6 5 18 17 16 15 13 14 + 47 48 44 43 45 46 31 32 33 34 36 35 + operation j=24 : 24 23 19 20 21 22 18 17 16 15 13 14 + 37 38 39 40 42 41 7 8 9 10 12 11 + 31 32 33 34 36 35 29 30 26 25 27 28 + 44 43 47 48 46 45 2 1 4 3 6 5 + operation j=25 : 25 26 30 29 28 27 43 44 45 46 48 47 + 36 35 32 31 34 33 6 5 1 2 3 4 + 40 39 41 42 37 38 20 19 23 24 22 21 + 17 18 14 13 15 16 10 9 11 12 7 8 + operation j=26 : 26 25 28 27 30 29 20 19 23 24 22 21 + 35 36 34 33 32 31 5 6 3 4 1 2 + 10 9 11 12 7 8 43 44 45 46 48 47 + 14 13 17 18 16 15 40 39 41 42 37 38 + operation j=27 : 27 28 29 30 26 25 45 46 43 44 47 48 + 32 31 36 35 33 34 40 39 41 42 37 38 + 6 5 1 2 3 4 14 13 17 18 16 15 + 23 24 20 19 21 22 9 10 7 8 11 12 + operation j=28 : 28 27 26 25 29 30 14 13 17 18 16 15 + 31 32 33 34 36 35 39 40 37 38 41 42 + 9 10 7 8 11 12 45 46 43 44 47 48 + 20 19 23 24 22 21 6 5 1 2 3 4 + operation j=29 : 29 30 27 28 25 26 23 24 20 19 21 22 + 34 33 35 36 31 32 10 9 11 12 7 8 + 5 6 3 4 1 2 17 18 14 13 15 16 + 45 46 43 44 47 48 39 40 37 38 41 42 + operation j=30 : 30 29 25 26 27 28 17 18 14 13 15 16 + 33 34 31 32 35 36 9 10 7 8 11 12 + 39 40 37 38 41 42 23 24 20 19 21 22 + 43 44 45 46 48 47 5 6 3 4 1 2 + operation j=31 : 31 32 35 36 34 33 2 1 4 3 5 6 + 28 27 30 29 25 26 22 21 24 23 19 20 + 48 47 46 45 44 43 8 7 12 11 10 9 + 42 41 38 37 39 40 15 16 13 14 18 17 + operation j=32 : 32 31 34 33 35 36 8 7 12 11 10 9 + 27 28 25 26 30 29 21 22 19 20 24 23 + 15 16 13 14 18 17 2 1 4 3 5 6 + 38 37 42 41 40 39 48 47 46 45 44 43 + operation j=33 : 33 34 36 35 32 31 4 3 2 1 6 5 + 30 29 28 27 26 25 48 47 46 45 44 43 + 22 21 24 23 19 20 38 37 42 41 40 39 + 12 11 8 7 9 10 16 15 18 17 13 14 + operation j=34 : 34 33 32 31 36 35 38 37 42 41 40 39 + 29 30 26 25 28 27 47 48 44 43 46 45 + 16 15 18 17 13 14 4 3 2 1 6 5 + 8 7 12 11 10 9 22 21 24 23 19 20 + operation j=35 : 35 36 31 32 33 34 42 41 38 37 39 40 + 26 25 29 30 27 28 16 15 18 17 13 14 + 47 48 44 43 46 45 12 11 8 7 9 10 + 2 1 4 3 5 6 21 22 19 20 24 23 + operation j=36 : 36 35 33 34 31 32 12 11 8 7 9 10 + 25 26 27 28 29 30 15 16 13 14 18 17 + 21 22 19 20 24 23 42 41 38 37 39 40 + 4 3 2 1 6 5 47 48 44 43 46 45 + operation j=37 : 37 38 41 42 40 39 3 4 5 6 1 2 + 24 23 22 21 20 19 46 45 48 47 43 44 + 28 27 30 29 25 26 34 33 35 36 32 31 + 11 12 10 9 7 8 14 13 17 18 15 16 + operation j=38 : 38 37 40 39 41 42 34 33 35 36 32 31 + 23 24 20 19 22 21 45 46 43 44 48 47 + 14 13 17 18 15 16 3 4 5 6 1 2 + 10 9 11 12 8 7 28 27 30 29 25 26 + operation j=39 : 39 40 42 41 38 37 5 6 3 4 2 1 + 22 21 24 23 19 20 28 27 30 29 25 26 + 46 45 48 47 43 44 10 9 11 12 8 7 + 35 36 34 33 31 32 13 14 15 16 17 18 + operation j=40 : 40 39 38 37 42 41 10 9 11 12 8 7 + 21 22 19 20 24 23 27 28 25 26 30 29 + 13 14 15 16 17 18 5 6 3 4 2 1 + 34 33 35 36 32 31 46 45 48 47 43 44 + operation j=41 : 41 42 37 38 39 40 11 12 10 9 7 8 + 19 20 21 22 23 24 13 14 15 16 17 18 + 27 28 25 26 30 29 35 36 34 33 31 32 + 3 4 5 6 1 2 45 46 43 44 48 47 + operation j=42 : 42 41 39 40 37 38 35 36 34 33 31 32 + 20 19 23 24 21 22 14 13 17 18 15 16 + 45 46 43 44 48 47 11 12 10 9 7 8 + 5 6 3 4 2 1 27 28 25 26 30 29 + operation j=43 : 43 44 48 47 46 45 25 26 27 28 30 29 + 12 11 8 7 10 9 4 3 2 1 5 6 + 38 37 42 41 39 40 19 20 21 22 24 23 + 15 16 13 14 17 18 34 33 35 36 31 32 + operation j=44 : 44 43 46 45 48 47 19 20 21 22 24 23 + 11 12 10 9 8 7 3 4 5 6 2 1 + 34 33 35 36 31 32 25 26 27 28 30 29 + 13 14 15 16 18 17 38 37 42 41 39 40 + operation j=45 : 45 46 47 48 44 43 27 28 25 26 29 30 + 8 7 12 11 9 10 38 37 42 41 39 40 + 4 3 2 1 5 6 13 14 15 16 18 17 + 21 22 19 20 23 24 33 34 31 32 35 36 + operation j=46 : 46 45 44 43 47 48 13 14 15 16 18 17 + 7 8 9 10 12 11 37 38 39 40 42 41 + 33 34 31 32 35 36 27 28 25 26 29 30 + 19 20 21 22 24 23 4 3 2 1 5 6 + operation j=47 : 47 48 45 46 43 44 21 22 19 20 23 24 + 10 9 11 12 7 8 34 33 35 36 31 32 + 3 4 5 6 2 1 15 16 13 14 17 18 + 27 28 25 26 29 30 37 38 39 40 42 41 + operation j=48 : 48 47 43 44 45 46 15 16 13 14 17 18 + 9 10 7 8 11 12 33 34 31 32 35 36 + 37 38 39 40 42 41 21 22 19 20 23 24 + 25 26 27 28 30 29 3 4 5 6 2 1 + + + Space group can be generated using 5 generators: 13 4 9 34 2 + + generators (in lattice coordinates): + +&gen 5 + + -1 0 0 0.00000 + 0 -1 0 0.00000 + 0 0 -1 0.00000 + + 0 -1 0 0.00000 + 1 -1 0 0.00000 + 0 -1 1 0.00000 + + 0 1 -1 0.00000 + 1 0 -1 0.00000 + 0 0 -1 0.00000 + + 0 -1 1 0.00000 + 0 -1 0 0.00000 + 1 -1 0 0.00000 + + -1 0 0 0.00000 + -1 1 0 0.00000 + -1 0 1 0.00000 + +/ ! end generators + + + + Atomic positions: + ----------------- + atom types = 1 + total = 1 + + lattice coordinates (scaled) Cartesian coordinates atom + + atom type 1: atomic identification number = 74.0 representative = 1 + 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 1 + +atoms% 1 atoms 1 + Z( 1)= 74 atoms 1 + 0.000000 0.000000 0.000000 1 + ---------------------------------------------------- + Suggested values for input: + + Atom Z lmax jri rmt dx + W 74 10 841 2.544787 0.015844 +k_max = 3.92960 +G_max =11.78881 + + =============================================== + === modifying atomic input for &(all)atom === + =============================================== + +for atom 1 ( W) changed rmt to 2.100000 +for atom 1 ( W) changed jri to 981 +for atom 1 ( W) changed lmax to 12 +for atom 1 ( W) changed lnonsph to 6 +for atom 1 ( W) set econfig to [Kr] 4d10 4f14 | 5s2 5p6 6s2 5d4 + corestates = 16 with 60.0 electrons + valence st.= 5 with 14.0 electrons +nlod = 2 llod = 1 : 5s5p + nlo( 1) = 2 llo = 0 1 + lonqn = 5 5 +line: 13>&comp +line: 14>gmax=15.0 gmaxxc=12.5 kmax=5.0 / + ---------- + core : 1 -1 2.0 + core : 2 -1 2.0 + core : 2 1 2.0 + core : 2 -2 4.0 + core : 3 -1 2.0 + core : 3 1 2.0 + core : 3 -2 4.0 + core : 3 2 4.0 + core : 3 -3 6.0 + core : 4 -1 2.0 + core : 4 1 2.0 + core : 4 -2 4.0 + core : 4 2 4.0 + core : 4 -3 6.0 + core : 4 3 6.0 + core : 4 -4 8.0 + valence : 5 -1 2.0 5s + valence : 5 1 2.0 5p + valence : 5 -2 4.0 5p + valence : 6 -1 2.0 6s + valence : 5 2 2.0 5d + valence : 5 -3 2.0 5d + ---------- +Valence Electrons = 14 + ---------------------------------------------------- + Suggested values for input: + + Atom Z lmax jri rmt dx + W 74 10 841 2.544787 0.015844 +k_max = 3.92960 +G_max =11.78881 +line: 15>&kpt +line: 16>div1=3 div2=3 div3=3 tkb=0.0005 / + 5.22007561238382 5.22007561238382 5.22007561238382 + -0.333333333333333 -0.333333333333333 -0.333333333333333 + body centered cubic + values accepted unchanged + 3 3 3 nmop(i),i=1,3 + orientation of boundary faces + 1 -1 -0.5740361 ifac,iside,orient for xvec + 2 -1 -0.0454288 ifac,iside,orient for xvec + 3 -1 -0.0256305 ifac,iside,orient for xvec + 4 -1 -0.0469244 ifac,iside,orient for xvec +Bravais lattice vectors + -3.013812 3.013812 3.013812 + 3.013812 -3.013812 3.013812 + 3.013812 3.013812 -3.013812 +reciprocal lattice vectors + 0.000000 1.042398 1.042398 + 1.042398 0.000000 1.042398 + 1.042398 1.042398 0.000000 + 3 3 3 Monkhorst-Pack-parameters + 0 nreg; k-points in irreducible wedge of BZ + Monkhorst-Pack-fractions + 0 nbound; no k-points on boundary of BZ + 1 idim + -0.3333333 + 0.0000000 + 0.3333333 + 2 idim + -0.3333333 + 0.0000000 + 0.3333333 + 3 idim + -0.3333333 + 0.0000000 + 0.3333333 + +k-point count: 4 + +k-point mesh: 3 3 3 +k-point density: 2.035038 2.035038 2.035038 + diff --git a/tests/parsers/fixtures/inpgen/default/out.error b/tests/parsers/fixtures/inpgen/default/out.error new file mode 100644 index 000000000..e69de29bb diff --git a/tests/parsers/fixtures/inpgen/no_inpxml/out b/tests/parsers/fixtures/inpgen/no_inpxml/out new file mode 100644 index 000000000..fca99897b --- /dev/null +++ b/tests/parsers/fixtures/inpgen/no_inpxml/out @@ -0,0 +1,687 @@ +line: 1>A Fleur input generator calculation with aiida +line: 2>&input cartesian=F / +line: 3>-3.0138120600 3.0138120600 3.0138120600 +line: 4>3.0138120600 -3.0138120600 3.0138120600 +line: 5>3.0138120600 3.0138120600 -3.0138120600 +line: 6>1.0000000000 +line: 7>1.0000000000 1.0000000000 1.0000000000 +line: 8> +line: 9>1 +line: 10>74 0.0000000000 0.0000000000 0.0000000000 +line: 11>&atom +line: 12>econfig="[Kr] 4d10 4f14 | 5s2 5p6 6s2 5d4" element="W" jri=981 lm + + A Fleur input generator calculation with aiida + + film= F cartesian= F + checkinp= F symor= F + +a1 = -3.01381 3.01381 3.01381 +a2 = 3.01381 -3.01381 3.01381 +a3 = 3.01381 3.01381 -3.01381 + +dvac= -3.01381 aa = 1.00000 +scale = 1.00000 1.00000 1.00000 + +natin= 1 Z = 74 + positions: + 0.00000 0.00000 0.00000 + + generators: 0 (excluding identity) + + + Lattice information: + -------------------- + + overall lattice constant a0 = 1.000000 bohr + + real-space primitive lattice vectors in units of a_{x,y,z} + a_1: -3.013812 3.013812 3.013812 + a_2: 3.013812 -3.013812 3.013812 + a_3: 3.013812 3.013812 -3.013812 + + lattice constants a_x, a_y, a_z = 1.000000 1.000000 1.000000 + volume of unit cell (a.u.^3) = 109.498581 + +dbg: lattice matrices + 109.498580847925 +dbg: as : + -3.013812 3.013812 3.013812 + 3.013812 -3.013812 3.013812 + 3.013812 3.013812 -3.013812 +dbg: bs : + 0.000000 0.165903 0.165903 + 0.165903 0.000000 0.165903 + 0.165903 0.165903 0.000000 +dbg: amat : + -3.013812 3.013812 3.013812 + 3.013812 -3.013812 3.013812 + 3.013812 3.013812 -3.013812 +dbg: bmat : + 0.000000 0.521199 0.521199 + 0.521199 0.000000 0.521199 + 0.521199 0.521199 0.000000 +dbg: amatinv : + 0.000000 0.165903 0.165903 + 0.165903 0.000000 0.165903 + 0.165903 0.165903 0.000000 +dbg: aamat : + 27.249189 -9.083063 -9.083063 + -9.083063 27.249189 -9.083063 + -9.083063 -9.083063 27.249189 +dbg: bbmat : + 0.543297 0.271649 0.271649 + 0.271649 0.543297 0.271649 + 0.271649 0.271649 0.543297 + +dbg: lattice vectors : +vector 1 : -3.01381 3.01381 3.01381 length : 5.22008 +vector 2 : 3.01381 -3.01381 3.01381 length : 5.22008 +vector 3 : 3.01381 3.01381 -3.01381 length : 5.22008 +angle between vectors (1,2) =109.47122 +angle between vectors (1,3) =109.47122 +angle between vectors (2,3) =109.47122 + +dbg: reciprocal lattice vectors : +vector 1 : 0.00000 0.52120 0.52120 length : 0.73709 +vector 2 : 0.52120 0.00000 0.52120 length : 0.73709 +vector 3 : 0.52120 0.52120 0.00000 length : 0.73709 +angle between vectors (1,2) = 60.00000 +angle between vectors (1,3) = 60.00000 +angle between vectors (2,3) = 60.00000 + + + Point group of the Bravais lattice has 48 operations + + DBG: symor,zorth,oldfleur : T F F + DBG: optype : 1 -2 -2 3 3 -2 -2 3 2 -4 3 -4 -1 2 2 -3 -3 2 2 -2 -3 4 4 -3 -3 4 2 -3 -2 4 3 -2 -4 2 -4 3 3 -4 -4 3 -2 2 4 -3 -3 2 4 -2 + DBG: invsym,invs,zrfs,invs2 : T T F F + DBG: (before reorder) invsop,zrfsop,invs2op : 13 7 46 + + Space group information: + ------------------------ + 48 operations + space group is symmorphic + has inversion symmetry + + + Operations: (in International notation) + --------------------------------------- + lattice coordinates (scaled) Cartesian coordinates + + operation 1: 1 (inverse = 1) + ( 1 0 0 ) ( 0.000 ) ( 1.00000 0.00000 0.00000 ) ( 0.000 ) + ( 0 1 0 ) ( 0.000 ) ( 0.00000 1.00000 -0.00000 ) ( 0.000 ) + ( 0 0 1 ) ( 0.000 ) ( -0.00000 -0.00000 1.00000 ) ( 0.000 ) + _ + operation 2: 2 (inverse = 2) + ( -1 0 0 ) ( 0.000 ) ( 1.00000 -0.00000 -0.00000 ) ( 0.000 ) + ( -1 1 0 ) ( 0.000 ) ( 0.00000 0.00000 -1.00000 ) ( 0.000 ) + ( -1 0 1 ) ( 0.000 ) ( -0.00000 -1.00000 0.00000 ) ( 0.000 ) + _ + operation 3: 2 (inverse = 3) + ( 1 -1 0 ) ( 0.000 ) ( -0.00000 0.00000 -1.00000 ) ( 0.000 ) + ( 0 -1 0 ) ( 0.000 ) ( 0.00000 1.00000 -0.00000 ) ( 0.000 ) + ( 0 -1 1 ) ( 0.000 ) ( -1.00000 -0.00000 0.00000 ) ( 0.000 ) + + operation 4: 3 (inverse = 5) + ( 0 -1 0 ) ( 0.000 ) ( -0.00000 1.00000 -0.00000 ) ( 0.000 ) + ( 1 -1 0 ) ( 0.000 ) ( 0.00000 0.00000 -1.00000 ) ( 0.000 ) + ( 0 -1 1 ) ( 0.000 ) ( -1.00000 -0.00000 0.00000 ) ( 0.000 ) + + operation 5: 3 (inverse = 4) + ( -1 1 0 ) ( 0.000 ) ( 0.00000 -0.00000 -1.00000 ) ( 0.000 ) + ( -1 0 0 ) ( 0.000 ) ( 1.00000 0.00000 -0.00000 ) ( 0.000 ) + ( -1 0 1 ) ( 0.000 ) ( -0.00000 -1.00000 0.00000 ) ( 0.000 ) + _ + operation 6: 2 (inverse = 6) + ( 0 1 0 ) ( 0.000 ) ( 0.00000 1.00000 0.00000 ) ( 0.000 ) + ( 1 0 0 ) ( 0.000 ) ( 1.00000 0.00000 -0.00000 ) ( 0.000 ) + ( 0 0 1 ) ( 0.000 ) ( -0.00000 -0.00000 1.00000 ) ( 0.000 ) + _ + operation 7: 2 (inverse = 7) + ( 1 0 -1 ) ( 0.000 ) ( -0.00000 -1.00000 0.00000 ) ( 0.000 ) + ( 0 1 -1 ) ( 0.000 ) ( -1.00000 -0.00000 -0.00000 ) ( 0.000 ) + ( 0 0 -1 ) ( 0.000 ) ( 0.00000 0.00000 1.00000 ) ( 0.000 ) + + operation 8: 3 (inverse = 31) + ( 0 0 -1 ) ( 0.000 ) ( -0.00000 -0.00000 1.00000 ) ( 0.000 ) + ( 0 1 -1 ) ( 0.000 ) ( -1.00000 0.00000 0.00000 ) ( 0.000 ) + ( 1 0 -1 ) ( 0.000 ) ( 0.00000 -1.00000 -0.00000 ) ( 0.000 ) + + operation 9: 2 (inverse = 9) + ( 0 1 -1 ) ( 0.000 ) ( -1.00000 -0.00000 0.00000 ) ( 0.000 ) + ( 1 0 -1 ) ( 0.000 ) ( -0.00000 -1.00000 -0.00000 ) ( 0.000 ) + ( 0 0 -1 ) ( 0.000 ) ( 0.00000 0.00000 1.00000 ) ( 0.000 ) + _ + operation 10: 4 (inverse = 33) + ( 0 1 -1 ) ( 0.000 ) ( -1.00000 -0.00000 0.00000 ) ( 0.000 ) + ( 0 0 -1 ) ( 0.000 ) ( -0.00000 0.00000 1.00000 ) ( 0.000 ) + ( 1 0 -1 ) ( 0.000 ) ( 0.00000 -1.00000 -0.00000 ) ( 0.000 ) + + operation 11: 3 (inverse = 37) + ( 1 0 -1 ) ( 0.000 ) ( -0.00000 -1.00000 0.00000 ) ( 0.000 ) + ( 0 0 -1 ) ( 0.000 ) ( 0.00000 -0.00000 1.00000 ) ( 0.000 ) + ( 0 1 -1 ) ( 0.000 ) ( -1.00000 0.00000 -0.00000 ) ( 0.000 ) + _ + operation 12: 4 (inverse = 39) + ( 0 0 -1 ) ( 0.000 ) ( -0.00000 -0.00000 1.00000 ) ( 0.000 ) + ( 1 0 -1 ) ( 0.000 ) ( 0.00000 -1.00000 0.00000 ) ( 0.000 ) + ( 0 1 -1 ) ( 0.000 ) ( -1.00000 0.00000 -0.00000 ) ( 0.000 ) + _ + operation 13: 1 (inverse = 13) + ( -1 0 0 ) ( 0.000 ) ( -1.00000 -0.00000 -0.00000 ) ( 0.000 ) + ( 0 -1 0 ) ( 0.000 ) ( -0.00000 -1.00000 0.00000 ) ( 0.000 ) + ( 0 0 -1 ) ( 0.000 ) ( 0.00000 0.00000 -1.00000 ) ( 0.000 ) + + operation 14: 2 (inverse = 14) + ( 1 0 0 ) ( 0.000 ) ( -1.00000 0.00000 0.00000 ) ( 0.000 ) + ( 1 -1 0 ) ( 0.000 ) ( -0.00000 -0.00000 1.00000 ) ( 0.000 ) + ( 1 0 -1 ) ( 0.000 ) ( 0.00000 1.00000 -0.00000 ) ( 0.000 ) + + operation 15: 2 (inverse = 15) + ( 0 -1 0 ) ( 0.000 ) ( -0.00000 -1.00000 -0.00000 ) ( 0.000 ) + ( -1 0 0 ) ( 0.000 ) ( -1.00000 -0.00000 0.00000 ) ( 0.000 ) + ( 0 0 -1 ) ( 0.000 ) ( 0.00000 0.00000 -1.00000 ) ( 0.000 ) + _ + operation 16: 3 (inverse = 17) + ( 1 -1 0 ) ( 0.000 ) ( -0.00000 0.00000 1.00000 ) ( 0.000 ) + ( 1 0 0 ) ( 0.000 ) ( -1.00000 -0.00000 0.00000 ) ( 0.000 ) + ( 1 0 -1 ) ( 0.000 ) ( 0.00000 1.00000 -0.00000 ) ( 0.000 ) + _ + operation 17: 3 (inverse = 16) + ( 0 1 0 ) ( 0.000 ) ( 0.00000 -1.00000 0.00000 ) ( 0.000 ) + ( -1 1 0 ) ( 0.000 ) ( -0.00000 -0.00000 1.00000 ) ( 0.000 ) + ( 0 1 -1 ) ( 0.000 ) ( 1.00000 0.00000 -0.00000 ) ( 0.000 ) + + operation 18: 2 (inverse = 18) + ( -1 1 0 ) ( 0.000 ) ( 0.00000 -0.00000 1.00000 ) ( 0.000 ) + ( 0 1 0 ) ( 0.000 ) ( -0.00000 -1.00000 0.00000 ) ( 0.000 ) + ( 0 1 -1 ) ( 0.000 ) ( 1.00000 0.00000 -0.00000 ) ( 0.000 ) + + operation 19: 2 (inverse = 19) + ( -1 0 0 ) ( 0.000 ) ( -1.00000 -0.00000 -0.00000 ) ( 0.000 ) + ( 0 0 -1 ) ( 0.000 ) ( -0.00000 0.00000 -1.00000 ) ( 0.000 ) + ( 0 -1 0 ) ( 0.000 ) ( 0.00000 -1.00000 0.00000 ) ( 0.000 ) + _ + operation 20: 2 (inverse = 20) + ( 1 0 0 ) ( 0.000 ) ( -1.00000 0.00000 0.00000 ) ( 0.000 ) + ( 1 0 -1 ) ( 0.000 ) ( -0.00000 1.00000 -0.00000 ) ( 0.000 ) + ( 1 -1 0 ) ( 0.000 ) ( 0.00000 -0.00000 1.00000 ) ( 0.000 ) + _ + operation 21: 3 (inverse = 25) + ( 0 -1 0 ) ( 0.000 ) ( -0.00000 -1.00000 -0.00000 ) ( 0.000 ) + ( 0 0 -1 ) ( 0.000 ) ( 0.00000 -0.00000 -1.00000 ) ( 0.000 ) + ( -1 0 0 ) ( 0.000 ) ( -1.00000 0.00000 0.00000 ) ( 0.000 ) + + operation 22: 4 (inverse = 43) + ( 1 -1 0 ) ( 0.000 ) ( -0.00000 0.00000 1.00000 ) ( 0.000 ) + ( 1 0 -1 ) ( 0.000 ) ( 0.00000 1.00000 0.00000 ) ( 0.000 ) + ( 1 0 0 ) ( 0.000 ) ( -1.00000 -0.00000 -0.00000 ) ( 0.000 ) + + operation 23: 4 (inverse = 26) + ( 0 1 0 ) ( 0.000 ) ( 0.00000 -1.00000 0.00000 ) ( 0.000 ) + ( 0 1 -1 ) ( 0.000 ) ( 1.00000 -0.00000 -0.00000 ) ( 0.000 ) + ( -1 1 0 ) ( 0.000 ) ( -0.00000 0.00000 1.00000 ) ( 0.000 ) + _ + operation 24: 3 (inverse = 44) + ( -1 1 0 ) ( 0.000 ) ( 0.00000 -0.00000 1.00000 ) ( 0.000 ) + ( 0 1 -1 ) ( 0.000 ) ( 1.00000 0.00000 0.00000 ) ( 0.000 ) + ( 0 1 0 ) ( 0.000 ) ( -0.00000 -1.00000 -0.00000 ) ( 0.000 ) + _ + operation 25: 3 (inverse = 21) + ( 0 0 -1 ) ( 0.000 ) ( -0.00000 -0.00000 -1.00000 ) ( 0.000 ) + ( -1 0 0 ) ( 0.000 ) ( -1.00000 0.00000 -0.00000 ) ( 0.000 ) + ( 0 -1 0 ) ( 0.000 ) ( 0.00000 -1.00000 0.00000 ) ( 0.000 ) + + operation 26: 4 (inverse = 23) + ( 1 0 -1 ) ( 0.000 ) ( -0.00000 1.00000 0.00000 ) ( 0.000 ) + ( 1 0 0 ) ( 0.000 ) ( -1.00000 0.00000 -0.00000 ) ( 0.000 ) + ( 1 -1 0 ) ( 0.000 ) ( 0.00000 -0.00000 1.00000 ) ( 0.000 ) + + operation 27: 2 (inverse = 27) + ( 0 0 -1 ) ( 0.000 ) ( -0.00000 -0.00000 -1.00000 ) ( 0.000 ) + ( 0 -1 0 ) ( 0.000 ) ( 0.00000 -1.00000 -0.00000 ) ( 0.000 ) + ( -1 0 0 ) ( 0.000 ) ( -1.00000 0.00000 0.00000 ) ( 0.000 ) + _ + operation 28: 3 (inverse = 45) + ( 1 0 -1 ) ( 0.000 ) ( -0.00000 1.00000 0.00000 ) ( 0.000 ) + ( 1 -1 0 ) ( 0.000 ) ( 0.00000 0.00000 1.00000 ) ( 0.000 ) + ( 1 0 0 ) ( 0.000 ) ( -1.00000 -0.00000 -0.00000 ) ( 0.000 ) + _ + operation 29: 2 (inverse = 29) + ( 0 1 -1 ) ( 0.000 ) ( 1.00000 -0.00000 0.00000 ) ( 0.000 ) + ( 0 1 0 ) ( 0.000 ) ( 0.00000 -1.00000 -0.00000 ) ( 0.000 ) + ( -1 1 0 ) ( 0.000 ) ( -0.00000 0.00000 1.00000 ) ( 0.000 ) + + operation 30: 4 (inverse = 47) + ( 0 1 -1 ) ( 0.000 ) ( 1.00000 -0.00000 0.00000 ) ( 0.000 ) + ( -1 1 0 ) ( 0.000 ) ( 0.00000 0.00000 1.00000 ) ( 0.000 ) + ( 0 1 0 ) ( 0.000 ) ( -0.00000 -1.00000 -0.00000 ) ( 0.000 ) + + operation 31: 3 (inverse = 8) + ( -1 0 1 ) ( 0.000 ) ( 0.00000 -1.00000 -0.00000 ) ( 0.000 ) + ( -1 1 0 ) ( 0.000 ) ( -0.00000 -0.00000 -1.00000 ) ( 0.000 ) + ( -1 0 0 ) ( 0.000 ) ( 1.00000 0.00000 0.00000 ) ( 0.000 ) + _ + operation 32: 2 (inverse = 32) + ( 0 0 1 ) ( 0.000 ) ( 0.00000 0.00000 1.00000 ) ( 0.000 ) + ( 0 1 0 ) ( 0.000 ) ( -0.00000 1.00000 0.00000 ) ( 0.000 ) + ( 1 0 0 ) ( 0.000 ) ( 1.00000 -0.00000 -0.00000 ) ( 0.000 ) + _ + operation 33: 4 (inverse = 10) + ( 0 -1 1 ) ( 0.000 ) ( -1.00000 0.00000 -0.00000 ) ( 0.000 ) + ( 1 -1 0 ) ( 0.000 ) ( -0.00000 -0.00000 -1.00000 ) ( 0.000 ) + ( 0 -1 0 ) ( 0.000 ) ( 0.00000 1.00000 0.00000 ) ( 0.000 ) + + operation 34: 2 (inverse = 34) + ( 0 -1 1 ) ( 0.000 ) ( -1.00000 0.00000 -0.00000 ) ( 0.000 ) + ( 0 -1 0 ) ( 0.000 ) ( -0.00000 1.00000 0.00000 ) ( 0.000 ) + ( 1 -1 0 ) ( 0.000 ) ( 0.00000 -0.00000 -1.00000 ) ( 0.000 ) + _ + operation 35: 4 (inverse = 38) + ( -1 0 1 ) ( 0.000 ) ( 0.00000 -1.00000 -0.00000 ) ( 0.000 ) + ( -1 0 0 ) ( 0.000 ) ( 1.00000 -0.00000 0.00000 ) ( 0.000 ) + ( -1 1 0 ) ( 0.000 ) ( -0.00000 0.00000 -1.00000 ) ( 0.000 ) + + operation 36: 3 (inverse = 40) + ( 0 0 1 ) ( 0.000 ) ( 0.00000 0.00000 1.00000 ) ( 0.000 ) + ( 1 0 0 ) ( 0.000 ) ( 1.00000 -0.00000 0.00000 ) ( 0.000 ) + ( 0 1 0 ) ( 0.000 ) ( -0.00000 1.00000 -0.00000 ) ( 0.000 ) + + operation 37: 3 (inverse = 11) + ( 1 -1 0 ) ( 0.000 ) ( -0.00000 0.00000 -1.00000 ) ( 0.000 ) + ( 0 -1 1 ) ( 0.000 ) ( -1.00000 -0.00000 -0.00000 ) ( 0.000 ) + ( 0 -1 0 ) ( 0.000 ) ( 0.00000 1.00000 0.00000 ) ( 0.000 ) + _ + operation 38: 4 (inverse = 35) + ( 0 -1 0 ) ( 0.000 ) ( -0.00000 1.00000 -0.00000 ) ( 0.000 ) + ( 0 -1 1 ) ( 0.000 ) ( -1.00000 0.00000 0.00000 ) ( 0.000 ) + ( 1 -1 0 ) ( 0.000 ) ( 0.00000 -0.00000 -1.00000 ) ( 0.000 ) + _ + operation 39: 4 (inverse = 12) + ( -1 1 0 ) ( 0.000 ) ( 0.00000 -0.00000 -1.00000 ) ( 0.000 ) + ( -1 0 1 ) ( 0.000 ) ( -0.00000 -1.00000 -0.00000 ) ( 0.000 ) + ( -1 0 0 ) ( 0.000 ) ( 1.00000 0.00000 0.00000 ) ( 0.000 ) + + operation 40: 3 (inverse = 36) + ( 0 1 0 ) ( 0.000 ) ( 0.00000 1.00000 0.00000 ) ( 0.000 ) + ( 0 0 1 ) ( 0.000 ) ( -0.00000 0.00000 1.00000 ) ( 0.000 ) + ( 1 0 0 ) ( 0.000 ) ( 1.00000 -0.00000 -0.00000 ) ( 0.000 ) + _ + operation 41: 2 (inverse = 41) + ( 1 0 0 ) ( 0.000 ) ( 1.00000 0.00000 0.00000 ) ( 0.000 ) + ( 0 0 1 ) ( 0.000 ) ( 0.00000 -0.00000 1.00000 ) ( 0.000 ) + ( 0 1 0 ) ( 0.000 ) ( -0.00000 1.00000 -0.00000 ) ( 0.000 ) + + operation 42: 2 (inverse = 42) + ( -1 0 0 ) ( 0.000 ) ( 1.00000 -0.00000 -0.00000 ) ( 0.000 ) + ( -1 0 1 ) ( 0.000 ) ( 0.00000 -1.00000 0.00000 ) ( 0.000 ) + ( -1 1 0 ) ( 0.000 ) ( -0.00000 0.00000 -1.00000 ) ( 0.000 ) + + operation 43: 4 (inverse = 22) + ( 0 0 1 ) ( 0.000 ) ( 0.00000 0.00000 -1.00000 ) ( 0.000 ) + ( -1 0 1 ) ( 0.000 ) ( -0.00000 1.00000 -0.00000 ) ( 0.000 ) + ( 0 -1 1 ) ( 0.000 ) ( 1.00000 -0.00000 0.00000 ) ( 0.000 ) + _ + operation 44: 3 (inverse = 24) + ( -1 0 1 ) ( 0.000 ) ( 0.00000 1.00000 -0.00000 ) ( 0.000 ) + ( 0 0 1 ) ( 0.000 ) ( -0.00000 0.00000 -1.00000 ) ( 0.000 ) + ( 0 -1 1 ) ( 0.000 ) ( 1.00000 -0.00000 0.00000 ) ( 0.000 ) + _ + operation 45: 3 (inverse = 28) + ( 0 0 1 ) ( 0.000 ) ( 0.00000 0.00000 -1.00000 ) ( 0.000 ) + ( 0 -1 1 ) ( 0.000 ) ( 1.00000 -0.00000 -0.00000 ) ( 0.000 ) + ( -1 0 1 ) ( 0.000 ) ( -0.00000 1.00000 0.00000 ) ( 0.000 ) + + operation 46: 2 (inverse = 46) + ( -1 0 1 ) ( 0.000 ) ( 0.00000 1.00000 -0.00000 ) ( 0.000 ) + ( 0 -1 1 ) ( 0.000 ) ( 1.00000 0.00000 0.00000 ) ( 0.000 ) + ( 0 0 1 ) ( 0.000 ) ( -0.00000 -0.00000 -1.00000 ) ( 0.000 ) + + operation 47: 4 (inverse = 30) + ( 0 -1 1 ) ( 0.000 ) ( 1.00000 0.00000 -0.00000 ) ( 0.000 ) + ( 0 0 1 ) ( 0.000 ) ( 0.00000 -0.00000 -1.00000 ) ( 0.000 ) + ( -1 0 1 ) ( 0.000 ) ( -0.00000 1.00000 0.00000 ) ( 0.000 ) + _ + operation 48: 2 (inverse = 48) + ( 0 -1 1 ) ( 0.000 ) ( 1.00000 0.00000 -0.00000 ) ( 0.000 ) + ( -1 0 1 ) ( 0.000 ) ( 0.00000 1.00000 0.00000 ) ( 0.000 ) + ( 0 0 1 ) ( 0.000 ) ( -0.00000 -0.00000 -1.00000 ) ( 0.000 ) + + Multiplcation table: {R_j|t_j}{R_i|t_i} + operation j= 1 : 1 2 3 4 5 6 7 8 9 10 11 12 + 13 14 15 16 17 18 19 20 21 22 23 24 + 25 26 27 28 29 30 31 32 33 34 35 36 + 37 38 39 40 41 42 43 44 45 46 47 48 + operation j= 2 : 2 1 5 6 3 4 31 32 33 34 35 36 + 14 13 17 18 15 16 20 19 23 24 21 22 + 43 44 45 46 47 48 7 8 9 10 11 12 + 39 40 37 38 42 41 25 26 27 28 29 30 + operation j= 3 : 3 4 1 2 6 5 37 38 39 40 41 42 + 18 17 16 15 14 13 44 43 47 48 45 46 + 26 25 29 30 27 28 33 34 31 32 36 35 + 7 8 9 10 11 12 20 19 23 24 21 22 + operation j= 4 : 4 3 6 5 1 2 33 34 31 32 36 35 + 17 18 14 13 16 15 43 44 45 46 47 48 + 20 19 23 24 21 22 37 38 39 40 41 42 + 9 10 7 8 12 11 26 25 29 30 27 28 + operation j= 5 : 5 6 2 1 4 3 39 40 37 38 42 41 + 16 15 18 17 13 14 26 25 29 30 27 28 + 44 43 47 48 45 46 9 10 7 8 12 11 + 31 32 33 34 35 36 19 20 21 22 23 24 + operation j= 6 : 6 5 4 3 2 1 9 10 7 8 12 11 + 15 16 13 14 18 17 25 26 27 28 29 30 + 19 20 21 22 23 24 39 40 37 38 42 41 + 33 34 31 32 36 35 44 43 47 48 45 46 + operation j= 7 : 7 8 11 12 10 9 1 2 6 5 3 4 + 46 45 48 47 43 44 24 23 22 21 20 19 + 30 29 28 27 26 25 32 31 36 35 34 33 + 41 42 40 39 37 38 17 18 14 13 16 15 + operation j= 8 : 8 7 10 9 11 12 32 31 36 35 34 33 + 45 46 43 44 48 47 23 24 20 19 22 21 + 17 18 14 13 16 15 1 2 6 5 3 4 + 40 39 41 42 38 37 30 29 28 27 26 25 + operation j= 9 : 9 10 12 11 8 7 6 5 1 2 4 3 + 48 47 46 45 44 43 30 29 28 27 26 25 + 24 23 22 21 20 19 40 39 41 42 38 37 + 36 35 32 31 33 34 18 17 16 15 14 13 + operation j=10 : 10 9 8 7 12 11 40 39 41 42 38 37 + 47 48 44 43 46 45 29 30 26 25 28 27 + 18 17 16 15 14 13 6 5 1 2 4 3 + 32 31 36 35 34 33 24 23 22 21 20 19 + operation j=11 : 11 12 7 8 9 10 41 42 40 39 37 38 + 44 43 47 48 45 46 18 17 16 15 14 13 + 29 30 26 25 28 27 36 35 32 31 33 34 + 1 2 6 5 3 4 23 24 20 19 22 21 + operation j=12 : 12 11 9 10 7 8 36 35 32 31 33 34 + 43 44 45 46 47 48 17 18 14 13 16 15 + 23 24 20 19 22 21 41 42 40 39 37 38 + 6 5 1 2 4 3 29 30 26 25 28 27 + operation j=13 : 13 14 18 17 16 15 46 45 48 47 44 43 + 1 2 6 5 4 3 41 42 40 39 38 37 + 36 35 32 31 34 33 28 27 30 29 26 25 + 24 23 22 21 19 20 12 11 8 7 10 9 + operation j=14 : 14 13 16 15 18 17 28 27 30 29 26 25 + 2 1 4 3 6 5 42 41 38 37 40 39 + 12 11 8 7 10 9 46 45 48 47 44 43 + 22 21 24 23 20 19 36 35 32 31 34 33 + operation j=15 : 15 16 17 18 14 13 48 47 46 45 43 44 + 6 5 1 2 3 4 36 35 32 31 34 33 + 41 42 40 39 38 37 22 21 24 23 20 19 + 30 29 28 27 25 26 11 12 10 9 8 7 + operation j=16 : 16 15 14 13 17 18 22 21 24 23 20 19 + 5 6 3 4 1 2 35 36 34 33 32 31 + 11 12 10 9 8 7 48 47 46 45 43 44 + 28 27 30 29 26 25 41 42 40 39 38 37 + operation j=17 : 17 18 15 16 13 14 30 29 28 27 25 26 + 4 3 2 1 5 6 12 11 8 7 10 9 + 42 41 38 37 40 39 24 23 22 21 19 20 + 48 47 46 45 43 44 35 36 34 33 32 31 + operation j=18 : 18 17 13 14 15 16 24 23 22 21 19 20 + 3 4 5 6 2 1 11 12 10 9 8 7 + 35 36 34 33 32 31 30 29 28 27 25 26 + 46 45 48 47 44 43 42 41 38 37 40 39 + operation j=19 : 19 20 24 23 22 21 44 43 47 48 46 45 + 41 42 40 39 38 37 1 2 6 5 4 3 + 32 31 36 35 33 34 26 25 29 30 28 27 + 18 17 16 15 13 14 8 7 12 11 9 10 + operation j=20 : 20 19 22 21 24 23 26 25 29 30 28 27 + 42 41 38 37 40 39 2 1 4 3 6 5 + 8 7 12 11 9 10 44 43 47 48 46 45 + 16 15 18 17 14 13 32 31 36 35 33 34 + operation j=21 : 21 22 23 24 20 19 47 48 44 43 45 46 + 40 39 41 42 37 38 32 31 36 35 33 34 + 1 2 6 5 4 3 16 15 18 17 14 13 + 29 30 26 25 27 28 7 8 9 10 12 11 + operation j=22 : 22 21 20 19 23 24 16 15 18 17 14 13 + 39 40 37 38 41 42 31 32 33 34 36 35 + 7 8 9 10 12 11 47 48 44 43 45 46 + 26 25 29 30 28 27 1 2 6 5 4 3 + operation j=23 : 23 24 21 22 19 20 29 30 26 25 27 28 + 38 37 42 41 39 40 8 7 12 11 9 10 + 2 1 4 3 6 5 18 17 16 15 13 14 + 47 48 44 43 45 46 31 32 33 34 36 35 + operation j=24 : 24 23 19 20 21 22 18 17 16 15 13 14 + 37 38 39 40 42 41 7 8 9 10 12 11 + 31 32 33 34 36 35 29 30 26 25 27 28 + 44 43 47 48 46 45 2 1 4 3 6 5 + operation j=25 : 25 26 30 29 28 27 43 44 45 46 48 47 + 36 35 32 31 34 33 6 5 1 2 3 4 + 40 39 41 42 37 38 20 19 23 24 22 21 + 17 18 14 13 15 16 10 9 11 12 7 8 + operation j=26 : 26 25 28 27 30 29 20 19 23 24 22 21 + 35 36 34 33 32 31 5 6 3 4 1 2 + 10 9 11 12 7 8 43 44 45 46 48 47 + 14 13 17 18 16 15 40 39 41 42 37 38 + operation j=27 : 27 28 29 30 26 25 45 46 43 44 47 48 + 32 31 36 35 33 34 40 39 41 42 37 38 + 6 5 1 2 3 4 14 13 17 18 16 15 + 23 24 20 19 21 22 9 10 7 8 11 12 + operation j=28 : 28 27 26 25 29 30 14 13 17 18 16 15 + 31 32 33 34 36 35 39 40 37 38 41 42 + 9 10 7 8 11 12 45 46 43 44 47 48 + 20 19 23 24 22 21 6 5 1 2 3 4 + operation j=29 : 29 30 27 28 25 26 23 24 20 19 21 22 + 34 33 35 36 31 32 10 9 11 12 7 8 + 5 6 3 4 1 2 17 18 14 13 15 16 + 45 46 43 44 47 48 39 40 37 38 41 42 + operation j=30 : 30 29 25 26 27 28 17 18 14 13 15 16 + 33 34 31 32 35 36 9 10 7 8 11 12 + 39 40 37 38 41 42 23 24 20 19 21 22 + 43 44 45 46 48 47 5 6 3 4 1 2 + operation j=31 : 31 32 35 36 34 33 2 1 4 3 5 6 + 28 27 30 29 25 26 22 21 24 23 19 20 + 48 47 46 45 44 43 8 7 12 11 10 9 + 42 41 38 37 39 40 15 16 13 14 18 17 + operation j=32 : 32 31 34 33 35 36 8 7 12 11 10 9 + 27 28 25 26 30 29 21 22 19 20 24 23 + 15 16 13 14 18 17 2 1 4 3 5 6 + 38 37 42 41 40 39 48 47 46 45 44 43 + operation j=33 : 33 34 36 35 32 31 4 3 2 1 6 5 + 30 29 28 27 26 25 48 47 46 45 44 43 + 22 21 24 23 19 20 38 37 42 41 40 39 + 12 11 8 7 9 10 16 15 18 17 13 14 + operation j=34 : 34 33 32 31 36 35 38 37 42 41 40 39 + 29 30 26 25 28 27 47 48 44 43 46 45 + 16 15 18 17 13 14 4 3 2 1 6 5 + 8 7 12 11 10 9 22 21 24 23 19 20 + operation j=35 : 35 36 31 32 33 34 42 41 38 37 39 40 + 26 25 29 30 27 28 16 15 18 17 13 14 + 47 48 44 43 46 45 12 11 8 7 9 10 + 2 1 4 3 5 6 21 22 19 20 24 23 + operation j=36 : 36 35 33 34 31 32 12 11 8 7 9 10 + 25 26 27 28 29 30 15 16 13 14 18 17 + 21 22 19 20 24 23 42 41 38 37 39 40 + 4 3 2 1 6 5 47 48 44 43 46 45 + operation j=37 : 37 38 41 42 40 39 3 4 5 6 1 2 + 24 23 22 21 20 19 46 45 48 47 43 44 + 28 27 30 29 25 26 34 33 35 36 32 31 + 11 12 10 9 7 8 14 13 17 18 15 16 + operation j=38 : 38 37 40 39 41 42 34 33 35 36 32 31 + 23 24 20 19 22 21 45 46 43 44 48 47 + 14 13 17 18 15 16 3 4 5 6 1 2 + 10 9 11 12 8 7 28 27 30 29 25 26 + operation j=39 : 39 40 42 41 38 37 5 6 3 4 2 1 + 22 21 24 23 19 20 28 27 30 29 25 26 + 46 45 48 47 43 44 10 9 11 12 8 7 + 35 36 34 33 31 32 13 14 15 16 17 18 + operation j=40 : 40 39 38 37 42 41 10 9 11 12 8 7 + 21 22 19 20 24 23 27 28 25 26 30 29 + 13 14 15 16 17 18 5 6 3 4 2 1 + 34 33 35 36 32 31 46 45 48 47 43 44 + operation j=41 : 41 42 37 38 39 40 11 12 10 9 7 8 + 19 20 21 22 23 24 13 14 15 16 17 18 + 27 28 25 26 30 29 35 36 34 33 31 32 + 3 4 5 6 1 2 45 46 43 44 48 47 + operation j=42 : 42 41 39 40 37 38 35 36 34 33 31 32 + 20 19 23 24 21 22 14 13 17 18 15 16 + 45 46 43 44 48 47 11 12 10 9 7 8 + 5 6 3 4 2 1 27 28 25 26 30 29 + operation j=43 : 43 44 48 47 46 45 25 26 27 28 30 29 + 12 11 8 7 10 9 4 3 2 1 5 6 + 38 37 42 41 39 40 19 20 21 22 24 23 + 15 16 13 14 17 18 34 33 35 36 31 32 + operation j=44 : 44 43 46 45 48 47 19 20 21 22 24 23 + 11 12 10 9 8 7 3 4 5 6 2 1 + 34 33 35 36 31 32 25 26 27 28 30 29 + 13 14 15 16 18 17 38 37 42 41 39 40 + operation j=45 : 45 46 47 48 44 43 27 28 25 26 29 30 + 8 7 12 11 9 10 38 37 42 41 39 40 + 4 3 2 1 5 6 13 14 15 16 18 17 + 21 22 19 20 23 24 33 34 31 32 35 36 + operation j=46 : 46 45 44 43 47 48 13 14 15 16 18 17 + 7 8 9 10 12 11 37 38 39 40 42 41 + 33 34 31 32 35 36 27 28 25 26 29 30 + 19 20 21 22 24 23 4 3 2 1 5 6 + operation j=47 : 47 48 45 46 43 44 21 22 19 20 23 24 + 10 9 11 12 7 8 34 33 35 36 31 32 + 3 4 5 6 2 1 15 16 13 14 17 18 + 27 28 25 26 29 30 37 38 39 40 42 41 + operation j=48 : 48 47 43 44 45 46 15 16 13 14 17 18 + 9 10 7 8 11 12 33 34 31 32 35 36 + 37 38 39 40 42 41 21 22 19 20 23 24 + 25 26 27 28 30 29 3 4 5 6 2 1 + + + Space group can be generated using 5 generators: 13 4 9 34 2 + + generators (in lattice coordinates): + +&gen 5 + + -1 0 0 0.00000 + 0 -1 0 0.00000 + 0 0 -1 0.00000 + + 0 -1 0 0.00000 + 1 -1 0 0.00000 + 0 -1 1 0.00000 + + 0 1 -1 0.00000 + 1 0 -1 0.00000 + 0 0 -1 0.00000 + + 0 -1 1 0.00000 + 0 -1 0 0.00000 + 1 -1 0 0.00000 + + -1 0 0 0.00000 + -1 1 0 0.00000 + -1 0 1 0.00000 + +/ ! end generators + + + + Atomic positions: + ----------------- + atom types = 1 + total = 1 + + lattice coordinates (scaled) Cartesian coordinates atom + + atom type 1: atomic identification number = 74.0 representative = 1 + 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 1 + +atoms% 1 atoms 1 + Z( 1)= 74 atoms 1 + 0.000000 0.000000 0.000000 1 + ---------------------------------------------------- + Suggested values for input: + + Atom Z lmax jri rmt dx + W 74 10 841 2.544787 0.015844 +k_max = 3.92960 +G_max =11.78881 + + =============================================== + === modifying atomic input for &(all)atom === + =============================================== + +for atom 1 ( W) changed rmt to 2.100000 +for atom 1 ( W) changed jri to 981 +for atom 1 ( W) changed lmax to 12 +for atom 1 ( W) changed lnonsph to 6 +for atom 1 ( W) set econfig to [Kr] 4d10 4f14 | 5s2 5p6 6s2 5d4 + corestates = 16 with 60.0 electrons + valence st.= 5 with 14.0 electrons +nlod = 2 llod = 1 : 5s5p + nlo( 1) = 2 llo = 0 1 + lonqn = 5 5 +line: 13>&comp +line: 14>gmax=15.0 gmaxxc=12.5 kmax=5.0 / + ---------- + core : 1 -1 2.0 + core : 2 -1 2.0 + core : 2 1 2.0 + core : 2 -2 4.0 + core : 3 -1 2.0 + core : 3 1 2.0 + core : 3 -2 4.0 + core : 3 2 4.0 + core : 3 -3 6.0 + core : 4 -1 2.0 + core : 4 1 2.0 + core : 4 -2 4.0 + core : 4 2 4.0 + core : 4 -3 6.0 + core : 4 3 6.0 + core : 4 -4 8.0 + valence : 5 -1 2.0 5s + valence : 5 1 2.0 5p + valence : 5 -2 4.0 5p + valence : 6 -1 2.0 6s + valence : 5 2 2.0 5d + valence : 5 -3 2.0 5d + ---------- +Valence Electrons = 14 + ---------------------------------------------------- + Suggested values for input: + + Atom Z lmax jri rmt dx + W 74 10 841 2.544787 0.015844 +k_max = 3.92960 +G_max =11.78881 +line: 15>&kpt +line: 16>div1=3 div2=3 div3=3 tkb=0.0005 / + 5.22007561238382 5.22007561238382 5.22007561238382 + -0.333333333333333 -0.333333333333333 -0.333333333333333 + body centered cubic + values accepted unchanged + 3 3 3 nmop(i),i=1,3 + orientation of boundary faces + 1 -1 -0.5740361 ifac,iside,orient for xvec + 2 -1 -0.0454288 ifac,iside,orient for xvec + 3 -1 -0.0256305 ifac,iside,orient for xvec + 4 -1 -0.0469244 ifac,iside,orient for xvec +Bravais lattice vectors + -3.013812 3.013812 3.013812 + 3.013812 -3.013812 3.013812 + 3.013812 3.013812 -3.013812 +reciprocal lattice vectors + 0.000000 1.042398 1.042398 + 1.042398 0.000000 1.042398 + 1.042398 1.042398 0.000000 + 3 3 3 Monkhorst-Pack-parameters + 0 nreg; k-points in irreducible wedge of BZ + Monkhorst-Pack-fractions + 0 nbound; no k-points on boundary of BZ + 1 idim + -0.3333333 + 0.0000000 + 0.3333333 + 2 idim + -0.3333333 + 0.0000000 + 0.3333333 + 3 idim + -0.3333333 + 0.0000000 + 0.3333333 + +k-point count: 4 + +k-point mesh: 3 3 3 +k-point density: 2.035038 2.035038 2.035038 + diff --git a/tests/parsers/fixtures/inpgen/no_inpxml/out.error b/tests/parsers/fixtures/inpgen/no_inpxml/out.error new file mode 100644 index 000000000..79c4a880f --- /dev/null +++ b/tests/parsers/fixtures/inpgen/no_inpxml/out.error @@ -0,0 +1,2 @@ +ERROR +Check_rmt diff --git a/tests/parsers/fixtures/inpgen/no_otherfiles/inp.xml b/tests/parsers/fixtures/inpgen/no_otherfiles/inp.xml new file mode 100644 index 000000000..2bf34f24c --- /dev/null +++ b/tests/parsers/fixtures/inpgen/no_otherfiles/inp.xml @@ -0,0 +1,319 @@ + + + + A Fleur input generator calculation with aiida + + + + + + + + + + .0000000000 .0000000000 .0000000000 + + + + + + + -0.000000 0.333333 0.333333 + -0.333333 0.333333 0.333333 + -0.000000 0.000000 0.333333 + 0.000000 0.000000 0.000000 + + + + + + + + + + + 1 0 0 .0000000000 + 0 1 0 .0000000000 + 0 0 1 .0000000000 + + + -1 0 0 .0000000000 + -1 1 0 .0000000000 + -1 0 1 .0000000000 + + + 1 -1 0 .0000000000 + 0 -1 0 .0000000000 + 0 -1 1 .0000000000 + + + 0 -1 0 .0000000000 + 1 -1 0 .0000000000 + 0 -1 1 .0000000000 + + + -1 1 0 .0000000000 + -1 0 0 .0000000000 + -1 0 1 .0000000000 + + + 0 1 0 .0000000000 + 1 0 0 .0000000000 + 0 0 1 .0000000000 + + + 1 0 -1 .0000000000 + 0 1 -1 .0000000000 + 0 0 -1 .0000000000 + + + 0 0 -1 .0000000000 + 0 1 -1 .0000000000 + 1 0 -1 .0000000000 + + + 0 1 -1 .0000000000 + 1 0 -1 .0000000000 + 0 0 -1 .0000000000 + + + 0 1 -1 .0000000000 + 0 0 -1 .0000000000 + 1 0 -1 .0000000000 + + + 1 0 -1 .0000000000 + 0 0 -1 .0000000000 + 0 1 -1 .0000000000 + + + 0 0 -1 .0000000000 + 1 0 -1 .0000000000 + 0 1 -1 .0000000000 + + + -1 0 0 .0000000000 + 0 -1 0 .0000000000 + 0 0 -1 .0000000000 + + + 1 0 0 .0000000000 + 1 -1 0 .0000000000 + 1 0 -1 .0000000000 + + + 0 -1 0 .0000000000 + -1 0 0 .0000000000 + 0 0 -1 .0000000000 + + + 1 -1 0 .0000000000 + 1 0 0 .0000000000 + 1 0 -1 .0000000000 + + + 0 1 0 .0000000000 + -1 1 0 .0000000000 + 0 1 -1 .0000000000 + + + -1 1 0 .0000000000 + 0 1 0 .0000000000 + 0 1 -1 .0000000000 + + + -1 0 0 .0000000000 + 0 0 -1 .0000000000 + 0 -1 0 .0000000000 + + + 1 0 0 .0000000000 + 1 0 -1 .0000000000 + 1 -1 0 .0000000000 + + + 0 -1 0 .0000000000 + 0 0 -1 .0000000000 + -1 0 0 .0000000000 + + + 1 -1 0 .0000000000 + 1 0 -1 .0000000000 + 1 0 0 .0000000000 + + + 0 1 0 .0000000000 + 0 1 -1 .0000000000 + -1 1 0 .0000000000 + + + -1 1 0 .0000000000 + 0 1 -1 .0000000000 + 0 1 0 .0000000000 + + + 0 0 -1 .0000000000 + -1 0 0 .0000000000 + 0 -1 0 .0000000000 + + + 1 0 -1 .0000000000 + 1 0 0 .0000000000 + 1 -1 0 .0000000000 + + + 0 0 -1 .0000000000 + 0 -1 0 .0000000000 + -1 0 0 .0000000000 + + + 1 0 -1 .0000000000 + 1 -1 0 .0000000000 + 1 0 0 .0000000000 + + + 0 1 -1 .0000000000 + 0 1 0 .0000000000 + -1 1 0 .0000000000 + + + 0 1 -1 .0000000000 + -1 1 0 .0000000000 + 0 1 0 .0000000000 + + + -1 0 1 .0000000000 + -1 1 0 .0000000000 + -1 0 0 .0000000000 + + + 0 0 1 .0000000000 + 0 1 0 .0000000000 + 1 0 0 .0000000000 + + + 0 -1 1 .0000000000 + 1 -1 0 .0000000000 + 0 -1 0 .0000000000 + + + 0 -1 1 .0000000000 + 0 -1 0 .0000000000 + 1 -1 0 .0000000000 + + + -1 0 1 .0000000000 + -1 0 0 .0000000000 + -1 1 0 .0000000000 + + + 0 0 1 .0000000000 + 1 0 0 .0000000000 + 0 1 0 .0000000000 + + + 1 -1 0 .0000000000 + 0 -1 1 .0000000000 + 0 -1 0 .0000000000 + + + 0 -1 0 .0000000000 + 0 -1 1 .0000000000 + 1 -1 0 .0000000000 + + + -1 1 0 .0000000000 + -1 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 + -1 0 1 .0000000000 + -1 1 0 .0000000000 + + + 0 0 1 .0000000000 + -1 0 1 .0000000000 + 0 -1 1 .0000000000 + + + -1 0 1 .0000000000 + 0 0 1 .0000000000 + 0 -1 1 .0000000000 + + + 0 0 1 .0000000000 + 0 -1 1 .0000000000 + -1 0 1 .0000000000 + + + -1 0 1 .0000000000 + 0 -1 1 .0000000000 + 0 0 1 .0000000000 + + + 0 -1 1 .0000000000 + 0 0 1 .0000000000 + -1 0 1 .0000000000 + + + 0 -1 1 .0000000000 + -1 0 1 .0000000000 + 0 0 1 .0000000000 + + + + + -3.013812060000000 3.013812060000000 3.013812060000000 + 3.013812060000000 -3.013812060000000 3.013812060000000 + 3.013812060000000 3.013812060000000 -3.013812060000000 + + + + + + + + + + + + [Kr] (4d3/2) (4d5/2) (4f5/2) (4f7/2) + (5s1/2) (5p1/2) (5p3/2) (6s1/2) (5d3/2) (5d5/2) + + + + + + + + + + .0000000000 .0000000000 .0000000000 + + + + + + + + + + + + + + + + + diff --git a/tests/parsers/fixtures/inpgen/no_otherfiles/out.error b/tests/parsers/fixtures/inpgen/no_otherfiles/out.error new file mode 100644 index 000000000..e69de29bb diff --git a/tests/parsers/fixtures/inpgen/novalid_inpxml/inp.xml b/tests/parsers/fixtures/inpgen/novalid_inpxml/inp.xml new file mode 100644 index 000000000..a203a82dd --- /dev/null +++ b/tests/parsers/fixtures/inpgen/novalid_inpxml/inp.xml @@ -0,0 +1,319 @@ + + + + A Fleur input generator calculation with aiida + + + + + + + + + + .0000000000 .0000000000 .0000000000 + + + + + + + -0.000000 0.333333 0.333333 + -0.333333 0.333333 0.333333 + -0.000000 0.000000 0.333333 + 0.000000 0.000000 0.000000 + + + + + + + + + + + 1 0 0 .0000000000 + 0 1 0 .0000000000 + 0 0 1 .0000000000 + + + -1 0 0 .0000000000 + -1 1 0 .0000000000 + -1 0 1 .0000000000 + + + 1 -1 0 .0000000000 + 0 -1 0 .0000000000 + 0 -1 1 .0000000000 + + + 0 -1 0 .0000000000 + 1 -1 0 .0000000000 + 0 -1 1 .0000000000 + + + -1 1 0 .0000000000 + -1 0 0 .0000000000 + -1 0 1 .0000000000 + + + 0 1 0 .0000000000 + 1 0 0 .0000000000 + 0 0 1 .0000000000 + + + 1 0 -1 .0000000000 + 0 1 -1 .0000000000 + 0 0 -1 .0000000000 + + + 0 0 -1 .0000000000 + 0 1 -1 .0000000000 + 1 0 -1 .0000000000 + + + 0 1 -1 .0000000000 + 1 0 -1 .0000000000 + 0 0 -1 .0000000000 + + + 0 1 -1 .0000000000 + 0 0 -1 .0000000000 + 1 0 -1 .0000000000 + + + 1 0 -1 .0000000000 + 0 0 -1 .0000000000 + 0 1 -1 .0000000000 + + + 0 0 -1 .0000000000 + 1 0 -1 .0000000000 + 0 1 -1 .0000000000 + + + -1 0 0 .0000000000 + 0 -1 0 .0000000000 + 0 0 -1 .0000000000 + + + 1 0 0 .0000000000 + 1 -1 0 .0000000000 + 1 0 -1 .0000000000 + + + 0 -1 0 .0000000000 + -1 0 0 .0000000000 + 0 0 -1 .0000000000 + + + 1 -1 0 .0000000000 + 1 0 0 .0000000000 + 1 0 -1 .0000000000 + + + 0 1 0 .0000000000 + -1 1 0 .0000000000 + 0 1 -1 .0000000000 + + + -1 1 0 .0000000000 + 0 1 0 .0000000000 + 0 1 -1 .0000000000 + + + -1 0 0 .0000000000 + 0 0 -1 .0000000000 + 0 -1 0 .0000000000 + + + 1 0 0 .0000000000 + 1 0 -1 .0000000000 + 1 -1 0 .0000000000 + + + 0 -1 0 .0000000000 + 0 0 -1 .0000000000 + -1 0 0 .0000000000 + + + 1 -1 0 .0000000000 + 1 0 -1 .0000000000 + 1 0 0 .0000000000 + + + 0 1 0 .0000000000 + 0 1 -1 .0000000000 + -1 1 0 .0000000000 + + + -1 1 0 .0000000000 + 0 1 -1 .0000000000 + 0 1 0 .0000000000 + + + 0 0 -1 .0000000000 + -1 0 0 .0000000000 + 0 -1 0 .0000000000 + + + 1 0 -1 .0000000000 + 1 0 0 .0000000000 + 1 -1 0 .0000000000 + + + 0 0 -1 .0000000000 + 0 -1 0 .0000000000 + -1 0 0 .0000000000 + + + 1 0 -1 .0000000000 + 1 -1 0 .0000000000 + 1 0 0 .0000000000 + + + 0 1 -1 .0000000000 + 0 1 0 .0000000000 + -1 1 0 .0000000000 + + + 0 1 -1 .0000000000 + -1 1 0 .0000000000 + 0 1 0 .0000000000 + + + -1 0 1 .0000000000 + -1 1 0 .0000000000 + -1 0 0 .0000000000 + + + 0 0 1 .0000000000 + 0 1 0 .0000000000 + 1 0 0 .0000000000 + + + 0 -1 1 .0000000000 + 1 -1 0 .0000000000 + 0 -1 0 .0000000000 + + + 0 -1 1 .0000000000 + 0 -1 0 .0000000000 + 1 -1 0 .0000000000 + + + -1 0 1 .0000000000 + -1 0 0 .0000000000 + -1 1 0 .0000000000 + + + 0 0 1 .0000000000 + 1 0 0 .0000000000 + 0 1 0 .0000000000 + + + 1 -1 0 .0000000000 + 0 -1 1 .0000000000 + 0 -1 0 .0000000000 + + + 0 -1 0 .0000000000 + 0 -1 1 .0000000000 + 1 -1 0 .0000000000 + + + -1 1 0 .0000000000 + -1 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 + -1 0 1 .0000000000 + -1 1 0 .0000000000 + + + 0 0 1 .0000000000 + -1 0 1 .0000000000 + 0 -1 1 .0000000000 + + + -1 0 1 .0000000000 + 0 0 1 .0000000000 + 0 -1 1 .0000000000 + + + 0 0 1 .0000000000 + 0 -1 1 .0000000000 + -1 0 1 .0000000000 + + + -1 0 1 .0000000000 + 0 -1 1 .0000000000 + 0 0 1 .0000000000 + + + 0 -1 1 .0000000000 + 0 0 1 .0000000000 + -1 0 1 .0000000000 + + + 0 -1 1 .0000000000 + -1 0 1 .0000000000 + 0 0 1 .0000000000 + + + + + -3.013812060000000 3.013812060000000 3.013812060000000 + 3.013812060000000 -3.013812060000000 3.013812060000000 + 3.013812060000000 3.013812060000000 -3.013812060000000 + + + + + + + + + + + + [Kr] (4d3/2) (4d5/2) (4f5/2) (4f7/2) + (5s1/2) (5p1/2) (5p3/2) (6s1/2) (5d3/2) (5d5/2) + + + + + + + + + + .0000000000 .0000000000 .0000000000 + + + + + + + + + + + + + + + + + diff --git a/tests/parsers/fixtures/inpgen/novalid_inpxml/out b/tests/parsers/fixtures/inpgen/novalid_inpxml/out new file mode 100644 index 000000000..fca99897b --- /dev/null +++ b/tests/parsers/fixtures/inpgen/novalid_inpxml/out @@ -0,0 +1,687 @@ +line: 1>A Fleur input generator calculation with aiida +line: 2>&input cartesian=F / +line: 3>-3.0138120600 3.0138120600 3.0138120600 +line: 4>3.0138120600 -3.0138120600 3.0138120600 +line: 5>3.0138120600 3.0138120600 -3.0138120600 +line: 6>1.0000000000 +line: 7>1.0000000000 1.0000000000 1.0000000000 +line: 8> +line: 9>1 +line: 10>74 0.0000000000 0.0000000000 0.0000000000 +line: 11>&atom +line: 12>econfig="[Kr] 4d10 4f14 | 5s2 5p6 6s2 5d4" element="W" jri=981 lm + + A Fleur input generator calculation with aiida + + film= F cartesian= F + checkinp= F symor= F + +a1 = -3.01381 3.01381 3.01381 +a2 = 3.01381 -3.01381 3.01381 +a3 = 3.01381 3.01381 -3.01381 + +dvac= -3.01381 aa = 1.00000 +scale = 1.00000 1.00000 1.00000 + +natin= 1 Z = 74 + positions: + 0.00000 0.00000 0.00000 + + generators: 0 (excluding identity) + + + Lattice information: + -------------------- + + overall lattice constant a0 = 1.000000 bohr + + real-space primitive lattice vectors in units of a_{x,y,z} + a_1: -3.013812 3.013812 3.013812 + a_2: 3.013812 -3.013812 3.013812 + a_3: 3.013812 3.013812 -3.013812 + + lattice constants a_x, a_y, a_z = 1.000000 1.000000 1.000000 + volume of unit cell (a.u.^3) = 109.498581 + +dbg: lattice matrices + 109.498580847925 +dbg: as : + -3.013812 3.013812 3.013812 + 3.013812 -3.013812 3.013812 + 3.013812 3.013812 -3.013812 +dbg: bs : + 0.000000 0.165903 0.165903 + 0.165903 0.000000 0.165903 + 0.165903 0.165903 0.000000 +dbg: amat : + -3.013812 3.013812 3.013812 + 3.013812 -3.013812 3.013812 + 3.013812 3.013812 -3.013812 +dbg: bmat : + 0.000000 0.521199 0.521199 + 0.521199 0.000000 0.521199 + 0.521199 0.521199 0.000000 +dbg: amatinv : + 0.000000 0.165903 0.165903 + 0.165903 0.000000 0.165903 + 0.165903 0.165903 0.000000 +dbg: aamat : + 27.249189 -9.083063 -9.083063 + -9.083063 27.249189 -9.083063 + -9.083063 -9.083063 27.249189 +dbg: bbmat : + 0.543297 0.271649 0.271649 + 0.271649 0.543297 0.271649 + 0.271649 0.271649 0.543297 + +dbg: lattice vectors : +vector 1 : -3.01381 3.01381 3.01381 length : 5.22008 +vector 2 : 3.01381 -3.01381 3.01381 length : 5.22008 +vector 3 : 3.01381 3.01381 -3.01381 length : 5.22008 +angle between vectors (1,2) =109.47122 +angle between vectors (1,3) =109.47122 +angle between vectors (2,3) =109.47122 + +dbg: reciprocal lattice vectors : +vector 1 : 0.00000 0.52120 0.52120 length : 0.73709 +vector 2 : 0.52120 0.00000 0.52120 length : 0.73709 +vector 3 : 0.52120 0.52120 0.00000 length : 0.73709 +angle between vectors (1,2) = 60.00000 +angle between vectors (1,3) = 60.00000 +angle between vectors (2,3) = 60.00000 + + + Point group of the Bravais lattice has 48 operations + + DBG: symor,zorth,oldfleur : T F F + DBG: optype : 1 -2 -2 3 3 -2 -2 3 2 -4 3 -4 -1 2 2 -3 -3 2 2 -2 -3 4 4 -3 -3 4 2 -3 -2 4 3 -2 -4 2 -4 3 3 -4 -4 3 -2 2 4 -3 -3 2 4 -2 + DBG: invsym,invs,zrfs,invs2 : T T F F + DBG: (before reorder) invsop,zrfsop,invs2op : 13 7 46 + + Space group information: + ------------------------ + 48 operations + space group is symmorphic + has inversion symmetry + + + Operations: (in International notation) + --------------------------------------- + lattice coordinates (scaled) Cartesian coordinates + + operation 1: 1 (inverse = 1) + ( 1 0 0 ) ( 0.000 ) ( 1.00000 0.00000 0.00000 ) ( 0.000 ) + ( 0 1 0 ) ( 0.000 ) ( 0.00000 1.00000 -0.00000 ) ( 0.000 ) + ( 0 0 1 ) ( 0.000 ) ( -0.00000 -0.00000 1.00000 ) ( 0.000 ) + _ + operation 2: 2 (inverse = 2) + ( -1 0 0 ) ( 0.000 ) ( 1.00000 -0.00000 -0.00000 ) ( 0.000 ) + ( -1 1 0 ) ( 0.000 ) ( 0.00000 0.00000 -1.00000 ) ( 0.000 ) + ( -1 0 1 ) ( 0.000 ) ( -0.00000 -1.00000 0.00000 ) ( 0.000 ) + _ + operation 3: 2 (inverse = 3) + ( 1 -1 0 ) ( 0.000 ) ( -0.00000 0.00000 -1.00000 ) ( 0.000 ) + ( 0 -1 0 ) ( 0.000 ) ( 0.00000 1.00000 -0.00000 ) ( 0.000 ) + ( 0 -1 1 ) ( 0.000 ) ( -1.00000 -0.00000 0.00000 ) ( 0.000 ) + + operation 4: 3 (inverse = 5) + ( 0 -1 0 ) ( 0.000 ) ( -0.00000 1.00000 -0.00000 ) ( 0.000 ) + ( 1 -1 0 ) ( 0.000 ) ( 0.00000 0.00000 -1.00000 ) ( 0.000 ) + ( 0 -1 1 ) ( 0.000 ) ( -1.00000 -0.00000 0.00000 ) ( 0.000 ) + + operation 5: 3 (inverse = 4) + ( -1 1 0 ) ( 0.000 ) ( 0.00000 -0.00000 -1.00000 ) ( 0.000 ) + ( -1 0 0 ) ( 0.000 ) ( 1.00000 0.00000 -0.00000 ) ( 0.000 ) + ( -1 0 1 ) ( 0.000 ) ( -0.00000 -1.00000 0.00000 ) ( 0.000 ) + _ + operation 6: 2 (inverse = 6) + ( 0 1 0 ) ( 0.000 ) ( 0.00000 1.00000 0.00000 ) ( 0.000 ) + ( 1 0 0 ) ( 0.000 ) ( 1.00000 0.00000 -0.00000 ) ( 0.000 ) + ( 0 0 1 ) ( 0.000 ) ( -0.00000 -0.00000 1.00000 ) ( 0.000 ) + _ + operation 7: 2 (inverse = 7) + ( 1 0 -1 ) ( 0.000 ) ( -0.00000 -1.00000 0.00000 ) ( 0.000 ) + ( 0 1 -1 ) ( 0.000 ) ( -1.00000 -0.00000 -0.00000 ) ( 0.000 ) + ( 0 0 -1 ) ( 0.000 ) ( 0.00000 0.00000 1.00000 ) ( 0.000 ) + + operation 8: 3 (inverse = 31) + ( 0 0 -1 ) ( 0.000 ) ( -0.00000 -0.00000 1.00000 ) ( 0.000 ) + ( 0 1 -1 ) ( 0.000 ) ( -1.00000 0.00000 0.00000 ) ( 0.000 ) + ( 1 0 -1 ) ( 0.000 ) ( 0.00000 -1.00000 -0.00000 ) ( 0.000 ) + + operation 9: 2 (inverse = 9) + ( 0 1 -1 ) ( 0.000 ) ( -1.00000 -0.00000 0.00000 ) ( 0.000 ) + ( 1 0 -1 ) ( 0.000 ) ( -0.00000 -1.00000 -0.00000 ) ( 0.000 ) + ( 0 0 -1 ) ( 0.000 ) ( 0.00000 0.00000 1.00000 ) ( 0.000 ) + _ + operation 10: 4 (inverse = 33) + ( 0 1 -1 ) ( 0.000 ) ( -1.00000 -0.00000 0.00000 ) ( 0.000 ) + ( 0 0 -1 ) ( 0.000 ) ( -0.00000 0.00000 1.00000 ) ( 0.000 ) + ( 1 0 -1 ) ( 0.000 ) ( 0.00000 -1.00000 -0.00000 ) ( 0.000 ) + + operation 11: 3 (inverse = 37) + ( 1 0 -1 ) ( 0.000 ) ( -0.00000 -1.00000 0.00000 ) ( 0.000 ) + ( 0 0 -1 ) ( 0.000 ) ( 0.00000 -0.00000 1.00000 ) ( 0.000 ) + ( 0 1 -1 ) ( 0.000 ) ( -1.00000 0.00000 -0.00000 ) ( 0.000 ) + _ + operation 12: 4 (inverse = 39) + ( 0 0 -1 ) ( 0.000 ) ( -0.00000 -0.00000 1.00000 ) ( 0.000 ) + ( 1 0 -1 ) ( 0.000 ) ( 0.00000 -1.00000 0.00000 ) ( 0.000 ) + ( 0 1 -1 ) ( 0.000 ) ( -1.00000 0.00000 -0.00000 ) ( 0.000 ) + _ + operation 13: 1 (inverse = 13) + ( -1 0 0 ) ( 0.000 ) ( -1.00000 -0.00000 -0.00000 ) ( 0.000 ) + ( 0 -1 0 ) ( 0.000 ) ( -0.00000 -1.00000 0.00000 ) ( 0.000 ) + ( 0 0 -1 ) ( 0.000 ) ( 0.00000 0.00000 -1.00000 ) ( 0.000 ) + + operation 14: 2 (inverse = 14) + ( 1 0 0 ) ( 0.000 ) ( -1.00000 0.00000 0.00000 ) ( 0.000 ) + ( 1 -1 0 ) ( 0.000 ) ( -0.00000 -0.00000 1.00000 ) ( 0.000 ) + ( 1 0 -1 ) ( 0.000 ) ( 0.00000 1.00000 -0.00000 ) ( 0.000 ) + + operation 15: 2 (inverse = 15) + ( 0 -1 0 ) ( 0.000 ) ( -0.00000 -1.00000 -0.00000 ) ( 0.000 ) + ( -1 0 0 ) ( 0.000 ) ( -1.00000 -0.00000 0.00000 ) ( 0.000 ) + ( 0 0 -1 ) ( 0.000 ) ( 0.00000 0.00000 -1.00000 ) ( 0.000 ) + _ + operation 16: 3 (inverse = 17) + ( 1 -1 0 ) ( 0.000 ) ( -0.00000 0.00000 1.00000 ) ( 0.000 ) + ( 1 0 0 ) ( 0.000 ) ( -1.00000 -0.00000 0.00000 ) ( 0.000 ) + ( 1 0 -1 ) ( 0.000 ) ( 0.00000 1.00000 -0.00000 ) ( 0.000 ) + _ + operation 17: 3 (inverse = 16) + ( 0 1 0 ) ( 0.000 ) ( 0.00000 -1.00000 0.00000 ) ( 0.000 ) + ( -1 1 0 ) ( 0.000 ) ( -0.00000 -0.00000 1.00000 ) ( 0.000 ) + ( 0 1 -1 ) ( 0.000 ) ( 1.00000 0.00000 -0.00000 ) ( 0.000 ) + + operation 18: 2 (inverse = 18) + ( -1 1 0 ) ( 0.000 ) ( 0.00000 -0.00000 1.00000 ) ( 0.000 ) + ( 0 1 0 ) ( 0.000 ) ( -0.00000 -1.00000 0.00000 ) ( 0.000 ) + ( 0 1 -1 ) ( 0.000 ) ( 1.00000 0.00000 -0.00000 ) ( 0.000 ) + + operation 19: 2 (inverse = 19) + ( -1 0 0 ) ( 0.000 ) ( -1.00000 -0.00000 -0.00000 ) ( 0.000 ) + ( 0 0 -1 ) ( 0.000 ) ( -0.00000 0.00000 -1.00000 ) ( 0.000 ) + ( 0 -1 0 ) ( 0.000 ) ( 0.00000 -1.00000 0.00000 ) ( 0.000 ) + _ + operation 20: 2 (inverse = 20) + ( 1 0 0 ) ( 0.000 ) ( -1.00000 0.00000 0.00000 ) ( 0.000 ) + ( 1 0 -1 ) ( 0.000 ) ( -0.00000 1.00000 -0.00000 ) ( 0.000 ) + ( 1 -1 0 ) ( 0.000 ) ( 0.00000 -0.00000 1.00000 ) ( 0.000 ) + _ + operation 21: 3 (inverse = 25) + ( 0 -1 0 ) ( 0.000 ) ( -0.00000 -1.00000 -0.00000 ) ( 0.000 ) + ( 0 0 -1 ) ( 0.000 ) ( 0.00000 -0.00000 -1.00000 ) ( 0.000 ) + ( -1 0 0 ) ( 0.000 ) ( -1.00000 0.00000 0.00000 ) ( 0.000 ) + + operation 22: 4 (inverse = 43) + ( 1 -1 0 ) ( 0.000 ) ( -0.00000 0.00000 1.00000 ) ( 0.000 ) + ( 1 0 -1 ) ( 0.000 ) ( 0.00000 1.00000 0.00000 ) ( 0.000 ) + ( 1 0 0 ) ( 0.000 ) ( -1.00000 -0.00000 -0.00000 ) ( 0.000 ) + + operation 23: 4 (inverse = 26) + ( 0 1 0 ) ( 0.000 ) ( 0.00000 -1.00000 0.00000 ) ( 0.000 ) + ( 0 1 -1 ) ( 0.000 ) ( 1.00000 -0.00000 -0.00000 ) ( 0.000 ) + ( -1 1 0 ) ( 0.000 ) ( -0.00000 0.00000 1.00000 ) ( 0.000 ) + _ + operation 24: 3 (inverse = 44) + ( -1 1 0 ) ( 0.000 ) ( 0.00000 -0.00000 1.00000 ) ( 0.000 ) + ( 0 1 -1 ) ( 0.000 ) ( 1.00000 0.00000 0.00000 ) ( 0.000 ) + ( 0 1 0 ) ( 0.000 ) ( -0.00000 -1.00000 -0.00000 ) ( 0.000 ) + _ + operation 25: 3 (inverse = 21) + ( 0 0 -1 ) ( 0.000 ) ( -0.00000 -0.00000 -1.00000 ) ( 0.000 ) + ( -1 0 0 ) ( 0.000 ) ( -1.00000 0.00000 -0.00000 ) ( 0.000 ) + ( 0 -1 0 ) ( 0.000 ) ( 0.00000 -1.00000 0.00000 ) ( 0.000 ) + + operation 26: 4 (inverse = 23) + ( 1 0 -1 ) ( 0.000 ) ( -0.00000 1.00000 0.00000 ) ( 0.000 ) + ( 1 0 0 ) ( 0.000 ) ( -1.00000 0.00000 -0.00000 ) ( 0.000 ) + ( 1 -1 0 ) ( 0.000 ) ( 0.00000 -0.00000 1.00000 ) ( 0.000 ) + + operation 27: 2 (inverse = 27) + ( 0 0 -1 ) ( 0.000 ) ( -0.00000 -0.00000 -1.00000 ) ( 0.000 ) + ( 0 -1 0 ) ( 0.000 ) ( 0.00000 -1.00000 -0.00000 ) ( 0.000 ) + ( -1 0 0 ) ( 0.000 ) ( -1.00000 0.00000 0.00000 ) ( 0.000 ) + _ + operation 28: 3 (inverse = 45) + ( 1 0 -1 ) ( 0.000 ) ( -0.00000 1.00000 0.00000 ) ( 0.000 ) + ( 1 -1 0 ) ( 0.000 ) ( 0.00000 0.00000 1.00000 ) ( 0.000 ) + ( 1 0 0 ) ( 0.000 ) ( -1.00000 -0.00000 -0.00000 ) ( 0.000 ) + _ + operation 29: 2 (inverse = 29) + ( 0 1 -1 ) ( 0.000 ) ( 1.00000 -0.00000 0.00000 ) ( 0.000 ) + ( 0 1 0 ) ( 0.000 ) ( 0.00000 -1.00000 -0.00000 ) ( 0.000 ) + ( -1 1 0 ) ( 0.000 ) ( -0.00000 0.00000 1.00000 ) ( 0.000 ) + + operation 30: 4 (inverse = 47) + ( 0 1 -1 ) ( 0.000 ) ( 1.00000 -0.00000 0.00000 ) ( 0.000 ) + ( -1 1 0 ) ( 0.000 ) ( 0.00000 0.00000 1.00000 ) ( 0.000 ) + ( 0 1 0 ) ( 0.000 ) ( -0.00000 -1.00000 -0.00000 ) ( 0.000 ) + + operation 31: 3 (inverse = 8) + ( -1 0 1 ) ( 0.000 ) ( 0.00000 -1.00000 -0.00000 ) ( 0.000 ) + ( -1 1 0 ) ( 0.000 ) ( -0.00000 -0.00000 -1.00000 ) ( 0.000 ) + ( -1 0 0 ) ( 0.000 ) ( 1.00000 0.00000 0.00000 ) ( 0.000 ) + _ + operation 32: 2 (inverse = 32) + ( 0 0 1 ) ( 0.000 ) ( 0.00000 0.00000 1.00000 ) ( 0.000 ) + ( 0 1 0 ) ( 0.000 ) ( -0.00000 1.00000 0.00000 ) ( 0.000 ) + ( 1 0 0 ) ( 0.000 ) ( 1.00000 -0.00000 -0.00000 ) ( 0.000 ) + _ + operation 33: 4 (inverse = 10) + ( 0 -1 1 ) ( 0.000 ) ( -1.00000 0.00000 -0.00000 ) ( 0.000 ) + ( 1 -1 0 ) ( 0.000 ) ( -0.00000 -0.00000 -1.00000 ) ( 0.000 ) + ( 0 -1 0 ) ( 0.000 ) ( 0.00000 1.00000 0.00000 ) ( 0.000 ) + + operation 34: 2 (inverse = 34) + ( 0 -1 1 ) ( 0.000 ) ( -1.00000 0.00000 -0.00000 ) ( 0.000 ) + ( 0 -1 0 ) ( 0.000 ) ( -0.00000 1.00000 0.00000 ) ( 0.000 ) + ( 1 -1 0 ) ( 0.000 ) ( 0.00000 -0.00000 -1.00000 ) ( 0.000 ) + _ + operation 35: 4 (inverse = 38) + ( -1 0 1 ) ( 0.000 ) ( 0.00000 -1.00000 -0.00000 ) ( 0.000 ) + ( -1 0 0 ) ( 0.000 ) ( 1.00000 -0.00000 0.00000 ) ( 0.000 ) + ( -1 1 0 ) ( 0.000 ) ( -0.00000 0.00000 -1.00000 ) ( 0.000 ) + + operation 36: 3 (inverse = 40) + ( 0 0 1 ) ( 0.000 ) ( 0.00000 0.00000 1.00000 ) ( 0.000 ) + ( 1 0 0 ) ( 0.000 ) ( 1.00000 -0.00000 0.00000 ) ( 0.000 ) + ( 0 1 0 ) ( 0.000 ) ( -0.00000 1.00000 -0.00000 ) ( 0.000 ) + + operation 37: 3 (inverse = 11) + ( 1 -1 0 ) ( 0.000 ) ( -0.00000 0.00000 -1.00000 ) ( 0.000 ) + ( 0 -1 1 ) ( 0.000 ) ( -1.00000 -0.00000 -0.00000 ) ( 0.000 ) + ( 0 -1 0 ) ( 0.000 ) ( 0.00000 1.00000 0.00000 ) ( 0.000 ) + _ + operation 38: 4 (inverse = 35) + ( 0 -1 0 ) ( 0.000 ) ( -0.00000 1.00000 -0.00000 ) ( 0.000 ) + ( 0 -1 1 ) ( 0.000 ) ( -1.00000 0.00000 0.00000 ) ( 0.000 ) + ( 1 -1 0 ) ( 0.000 ) ( 0.00000 -0.00000 -1.00000 ) ( 0.000 ) + _ + operation 39: 4 (inverse = 12) + ( -1 1 0 ) ( 0.000 ) ( 0.00000 -0.00000 -1.00000 ) ( 0.000 ) + ( -1 0 1 ) ( 0.000 ) ( -0.00000 -1.00000 -0.00000 ) ( 0.000 ) + ( -1 0 0 ) ( 0.000 ) ( 1.00000 0.00000 0.00000 ) ( 0.000 ) + + operation 40: 3 (inverse = 36) + ( 0 1 0 ) ( 0.000 ) ( 0.00000 1.00000 0.00000 ) ( 0.000 ) + ( 0 0 1 ) ( 0.000 ) ( -0.00000 0.00000 1.00000 ) ( 0.000 ) + ( 1 0 0 ) ( 0.000 ) ( 1.00000 -0.00000 -0.00000 ) ( 0.000 ) + _ + operation 41: 2 (inverse = 41) + ( 1 0 0 ) ( 0.000 ) ( 1.00000 0.00000 0.00000 ) ( 0.000 ) + ( 0 0 1 ) ( 0.000 ) ( 0.00000 -0.00000 1.00000 ) ( 0.000 ) + ( 0 1 0 ) ( 0.000 ) ( -0.00000 1.00000 -0.00000 ) ( 0.000 ) + + operation 42: 2 (inverse = 42) + ( -1 0 0 ) ( 0.000 ) ( 1.00000 -0.00000 -0.00000 ) ( 0.000 ) + ( -1 0 1 ) ( 0.000 ) ( 0.00000 -1.00000 0.00000 ) ( 0.000 ) + ( -1 1 0 ) ( 0.000 ) ( -0.00000 0.00000 -1.00000 ) ( 0.000 ) + + operation 43: 4 (inverse = 22) + ( 0 0 1 ) ( 0.000 ) ( 0.00000 0.00000 -1.00000 ) ( 0.000 ) + ( -1 0 1 ) ( 0.000 ) ( -0.00000 1.00000 -0.00000 ) ( 0.000 ) + ( 0 -1 1 ) ( 0.000 ) ( 1.00000 -0.00000 0.00000 ) ( 0.000 ) + _ + operation 44: 3 (inverse = 24) + ( -1 0 1 ) ( 0.000 ) ( 0.00000 1.00000 -0.00000 ) ( 0.000 ) + ( 0 0 1 ) ( 0.000 ) ( -0.00000 0.00000 -1.00000 ) ( 0.000 ) + ( 0 -1 1 ) ( 0.000 ) ( 1.00000 -0.00000 0.00000 ) ( 0.000 ) + _ + operation 45: 3 (inverse = 28) + ( 0 0 1 ) ( 0.000 ) ( 0.00000 0.00000 -1.00000 ) ( 0.000 ) + ( 0 -1 1 ) ( 0.000 ) ( 1.00000 -0.00000 -0.00000 ) ( 0.000 ) + ( -1 0 1 ) ( 0.000 ) ( -0.00000 1.00000 0.00000 ) ( 0.000 ) + + operation 46: 2 (inverse = 46) + ( -1 0 1 ) ( 0.000 ) ( 0.00000 1.00000 -0.00000 ) ( 0.000 ) + ( 0 -1 1 ) ( 0.000 ) ( 1.00000 0.00000 0.00000 ) ( 0.000 ) + ( 0 0 1 ) ( 0.000 ) ( -0.00000 -0.00000 -1.00000 ) ( 0.000 ) + + operation 47: 4 (inverse = 30) + ( 0 -1 1 ) ( 0.000 ) ( 1.00000 0.00000 -0.00000 ) ( 0.000 ) + ( 0 0 1 ) ( 0.000 ) ( 0.00000 -0.00000 -1.00000 ) ( 0.000 ) + ( -1 0 1 ) ( 0.000 ) ( -0.00000 1.00000 0.00000 ) ( 0.000 ) + _ + operation 48: 2 (inverse = 48) + ( 0 -1 1 ) ( 0.000 ) ( 1.00000 0.00000 -0.00000 ) ( 0.000 ) + ( -1 0 1 ) ( 0.000 ) ( 0.00000 1.00000 0.00000 ) ( 0.000 ) + ( 0 0 1 ) ( 0.000 ) ( -0.00000 -0.00000 -1.00000 ) ( 0.000 ) + + Multiplcation table: {R_j|t_j}{R_i|t_i} + operation j= 1 : 1 2 3 4 5 6 7 8 9 10 11 12 + 13 14 15 16 17 18 19 20 21 22 23 24 + 25 26 27 28 29 30 31 32 33 34 35 36 + 37 38 39 40 41 42 43 44 45 46 47 48 + operation j= 2 : 2 1 5 6 3 4 31 32 33 34 35 36 + 14 13 17 18 15 16 20 19 23 24 21 22 + 43 44 45 46 47 48 7 8 9 10 11 12 + 39 40 37 38 42 41 25 26 27 28 29 30 + operation j= 3 : 3 4 1 2 6 5 37 38 39 40 41 42 + 18 17 16 15 14 13 44 43 47 48 45 46 + 26 25 29 30 27 28 33 34 31 32 36 35 + 7 8 9 10 11 12 20 19 23 24 21 22 + operation j= 4 : 4 3 6 5 1 2 33 34 31 32 36 35 + 17 18 14 13 16 15 43 44 45 46 47 48 + 20 19 23 24 21 22 37 38 39 40 41 42 + 9 10 7 8 12 11 26 25 29 30 27 28 + operation j= 5 : 5 6 2 1 4 3 39 40 37 38 42 41 + 16 15 18 17 13 14 26 25 29 30 27 28 + 44 43 47 48 45 46 9 10 7 8 12 11 + 31 32 33 34 35 36 19 20 21 22 23 24 + operation j= 6 : 6 5 4 3 2 1 9 10 7 8 12 11 + 15 16 13 14 18 17 25 26 27 28 29 30 + 19 20 21 22 23 24 39 40 37 38 42 41 + 33 34 31 32 36 35 44 43 47 48 45 46 + operation j= 7 : 7 8 11 12 10 9 1 2 6 5 3 4 + 46 45 48 47 43 44 24 23 22 21 20 19 + 30 29 28 27 26 25 32 31 36 35 34 33 + 41 42 40 39 37 38 17 18 14 13 16 15 + operation j= 8 : 8 7 10 9 11 12 32 31 36 35 34 33 + 45 46 43 44 48 47 23 24 20 19 22 21 + 17 18 14 13 16 15 1 2 6 5 3 4 + 40 39 41 42 38 37 30 29 28 27 26 25 + operation j= 9 : 9 10 12 11 8 7 6 5 1 2 4 3 + 48 47 46 45 44 43 30 29 28 27 26 25 + 24 23 22 21 20 19 40 39 41 42 38 37 + 36 35 32 31 33 34 18 17 16 15 14 13 + operation j=10 : 10 9 8 7 12 11 40 39 41 42 38 37 + 47 48 44 43 46 45 29 30 26 25 28 27 + 18 17 16 15 14 13 6 5 1 2 4 3 + 32 31 36 35 34 33 24 23 22 21 20 19 + operation j=11 : 11 12 7 8 9 10 41 42 40 39 37 38 + 44 43 47 48 45 46 18 17 16 15 14 13 + 29 30 26 25 28 27 36 35 32 31 33 34 + 1 2 6 5 3 4 23 24 20 19 22 21 + operation j=12 : 12 11 9 10 7 8 36 35 32 31 33 34 + 43 44 45 46 47 48 17 18 14 13 16 15 + 23 24 20 19 22 21 41 42 40 39 37 38 + 6 5 1 2 4 3 29 30 26 25 28 27 + operation j=13 : 13 14 18 17 16 15 46 45 48 47 44 43 + 1 2 6 5 4 3 41 42 40 39 38 37 + 36 35 32 31 34 33 28 27 30 29 26 25 + 24 23 22 21 19 20 12 11 8 7 10 9 + operation j=14 : 14 13 16 15 18 17 28 27 30 29 26 25 + 2 1 4 3 6 5 42 41 38 37 40 39 + 12 11 8 7 10 9 46 45 48 47 44 43 + 22 21 24 23 20 19 36 35 32 31 34 33 + operation j=15 : 15 16 17 18 14 13 48 47 46 45 43 44 + 6 5 1 2 3 4 36 35 32 31 34 33 + 41 42 40 39 38 37 22 21 24 23 20 19 + 30 29 28 27 25 26 11 12 10 9 8 7 + operation j=16 : 16 15 14 13 17 18 22 21 24 23 20 19 + 5 6 3 4 1 2 35 36 34 33 32 31 + 11 12 10 9 8 7 48 47 46 45 43 44 + 28 27 30 29 26 25 41 42 40 39 38 37 + operation j=17 : 17 18 15 16 13 14 30 29 28 27 25 26 + 4 3 2 1 5 6 12 11 8 7 10 9 + 42 41 38 37 40 39 24 23 22 21 19 20 + 48 47 46 45 43 44 35 36 34 33 32 31 + operation j=18 : 18 17 13 14 15 16 24 23 22 21 19 20 + 3 4 5 6 2 1 11 12 10 9 8 7 + 35 36 34 33 32 31 30 29 28 27 25 26 + 46 45 48 47 44 43 42 41 38 37 40 39 + operation j=19 : 19 20 24 23 22 21 44 43 47 48 46 45 + 41 42 40 39 38 37 1 2 6 5 4 3 + 32 31 36 35 33 34 26 25 29 30 28 27 + 18 17 16 15 13 14 8 7 12 11 9 10 + operation j=20 : 20 19 22 21 24 23 26 25 29 30 28 27 + 42 41 38 37 40 39 2 1 4 3 6 5 + 8 7 12 11 9 10 44 43 47 48 46 45 + 16 15 18 17 14 13 32 31 36 35 33 34 + operation j=21 : 21 22 23 24 20 19 47 48 44 43 45 46 + 40 39 41 42 37 38 32 31 36 35 33 34 + 1 2 6 5 4 3 16 15 18 17 14 13 + 29 30 26 25 27 28 7 8 9 10 12 11 + operation j=22 : 22 21 20 19 23 24 16 15 18 17 14 13 + 39 40 37 38 41 42 31 32 33 34 36 35 + 7 8 9 10 12 11 47 48 44 43 45 46 + 26 25 29 30 28 27 1 2 6 5 4 3 + operation j=23 : 23 24 21 22 19 20 29 30 26 25 27 28 + 38 37 42 41 39 40 8 7 12 11 9 10 + 2 1 4 3 6 5 18 17 16 15 13 14 + 47 48 44 43 45 46 31 32 33 34 36 35 + operation j=24 : 24 23 19 20 21 22 18 17 16 15 13 14 + 37 38 39 40 42 41 7 8 9 10 12 11 + 31 32 33 34 36 35 29 30 26 25 27 28 + 44 43 47 48 46 45 2 1 4 3 6 5 + operation j=25 : 25 26 30 29 28 27 43 44 45 46 48 47 + 36 35 32 31 34 33 6 5 1 2 3 4 + 40 39 41 42 37 38 20 19 23 24 22 21 + 17 18 14 13 15 16 10 9 11 12 7 8 + operation j=26 : 26 25 28 27 30 29 20 19 23 24 22 21 + 35 36 34 33 32 31 5 6 3 4 1 2 + 10 9 11 12 7 8 43 44 45 46 48 47 + 14 13 17 18 16 15 40 39 41 42 37 38 + operation j=27 : 27 28 29 30 26 25 45 46 43 44 47 48 + 32 31 36 35 33 34 40 39 41 42 37 38 + 6 5 1 2 3 4 14 13 17 18 16 15 + 23 24 20 19 21 22 9 10 7 8 11 12 + operation j=28 : 28 27 26 25 29 30 14 13 17 18 16 15 + 31 32 33 34 36 35 39 40 37 38 41 42 + 9 10 7 8 11 12 45 46 43 44 47 48 + 20 19 23 24 22 21 6 5 1 2 3 4 + operation j=29 : 29 30 27 28 25 26 23 24 20 19 21 22 + 34 33 35 36 31 32 10 9 11 12 7 8 + 5 6 3 4 1 2 17 18 14 13 15 16 + 45 46 43 44 47 48 39 40 37 38 41 42 + operation j=30 : 30 29 25 26 27 28 17 18 14 13 15 16 + 33 34 31 32 35 36 9 10 7 8 11 12 + 39 40 37 38 41 42 23 24 20 19 21 22 + 43 44 45 46 48 47 5 6 3 4 1 2 + operation j=31 : 31 32 35 36 34 33 2 1 4 3 5 6 + 28 27 30 29 25 26 22 21 24 23 19 20 + 48 47 46 45 44 43 8 7 12 11 10 9 + 42 41 38 37 39 40 15 16 13 14 18 17 + operation j=32 : 32 31 34 33 35 36 8 7 12 11 10 9 + 27 28 25 26 30 29 21 22 19 20 24 23 + 15 16 13 14 18 17 2 1 4 3 5 6 + 38 37 42 41 40 39 48 47 46 45 44 43 + operation j=33 : 33 34 36 35 32 31 4 3 2 1 6 5 + 30 29 28 27 26 25 48 47 46 45 44 43 + 22 21 24 23 19 20 38 37 42 41 40 39 + 12 11 8 7 9 10 16 15 18 17 13 14 + operation j=34 : 34 33 32 31 36 35 38 37 42 41 40 39 + 29 30 26 25 28 27 47 48 44 43 46 45 + 16 15 18 17 13 14 4 3 2 1 6 5 + 8 7 12 11 10 9 22 21 24 23 19 20 + operation j=35 : 35 36 31 32 33 34 42 41 38 37 39 40 + 26 25 29 30 27 28 16 15 18 17 13 14 + 47 48 44 43 46 45 12 11 8 7 9 10 + 2 1 4 3 5 6 21 22 19 20 24 23 + operation j=36 : 36 35 33 34 31 32 12 11 8 7 9 10 + 25 26 27 28 29 30 15 16 13 14 18 17 + 21 22 19 20 24 23 42 41 38 37 39 40 + 4 3 2 1 6 5 47 48 44 43 46 45 + operation j=37 : 37 38 41 42 40 39 3 4 5 6 1 2 + 24 23 22 21 20 19 46 45 48 47 43 44 + 28 27 30 29 25 26 34 33 35 36 32 31 + 11 12 10 9 7 8 14 13 17 18 15 16 + operation j=38 : 38 37 40 39 41 42 34 33 35 36 32 31 + 23 24 20 19 22 21 45 46 43 44 48 47 + 14 13 17 18 15 16 3 4 5 6 1 2 + 10 9 11 12 8 7 28 27 30 29 25 26 + operation j=39 : 39 40 42 41 38 37 5 6 3 4 2 1 + 22 21 24 23 19 20 28 27 30 29 25 26 + 46 45 48 47 43 44 10 9 11 12 8 7 + 35 36 34 33 31 32 13 14 15 16 17 18 + operation j=40 : 40 39 38 37 42 41 10 9 11 12 8 7 + 21 22 19 20 24 23 27 28 25 26 30 29 + 13 14 15 16 17 18 5 6 3 4 2 1 + 34 33 35 36 32 31 46 45 48 47 43 44 + operation j=41 : 41 42 37 38 39 40 11 12 10 9 7 8 + 19 20 21 22 23 24 13 14 15 16 17 18 + 27 28 25 26 30 29 35 36 34 33 31 32 + 3 4 5 6 1 2 45 46 43 44 48 47 + operation j=42 : 42 41 39 40 37 38 35 36 34 33 31 32 + 20 19 23 24 21 22 14 13 17 18 15 16 + 45 46 43 44 48 47 11 12 10 9 7 8 + 5 6 3 4 2 1 27 28 25 26 30 29 + operation j=43 : 43 44 48 47 46 45 25 26 27 28 30 29 + 12 11 8 7 10 9 4 3 2 1 5 6 + 38 37 42 41 39 40 19 20 21 22 24 23 + 15 16 13 14 17 18 34 33 35 36 31 32 + operation j=44 : 44 43 46 45 48 47 19 20 21 22 24 23 + 11 12 10 9 8 7 3 4 5 6 2 1 + 34 33 35 36 31 32 25 26 27 28 30 29 + 13 14 15 16 18 17 38 37 42 41 39 40 + operation j=45 : 45 46 47 48 44 43 27 28 25 26 29 30 + 8 7 12 11 9 10 38 37 42 41 39 40 + 4 3 2 1 5 6 13 14 15 16 18 17 + 21 22 19 20 23 24 33 34 31 32 35 36 + operation j=46 : 46 45 44 43 47 48 13 14 15 16 18 17 + 7 8 9 10 12 11 37 38 39 40 42 41 + 33 34 31 32 35 36 27 28 25 26 29 30 + 19 20 21 22 24 23 4 3 2 1 5 6 + operation j=47 : 47 48 45 46 43 44 21 22 19 20 23 24 + 10 9 11 12 7 8 34 33 35 36 31 32 + 3 4 5 6 2 1 15 16 13 14 17 18 + 27 28 25 26 29 30 37 38 39 40 42 41 + operation j=48 : 48 47 43 44 45 46 15 16 13 14 17 18 + 9 10 7 8 11 12 33 34 31 32 35 36 + 37 38 39 40 42 41 21 22 19 20 23 24 + 25 26 27 28 30 29 3 4 5 6 2 1 + + + Space group can be generated using 5 generators: 13 4 9 34 2 + + generators (in lattice coordinates): + +&gen 5 + + -1 0 0 0.00000 + 0 -1 0 0.00000 + 0 0 -1 0.00000 + + 0 -1 0 0.00000 + 1 -1 0 0.00000 + 0 -1 1 0.00000 + + 0 1 -1 0.00000 + 1 0 -1 0.00000 + 0 0 -1 0.00000 + + 0 -1 1 0.00000 + 0 -1 0 0.00000 + 1 -1 0 0.00000 + + -1 0 0 0.00000 + -1 1 0 0.00000 + -1 0 1 0.00000 + +/ ! end generators + + + + Atomic positions: + ----------------- + atom types = 1 + total = 1 + + lattice coordinates (scaled) Cartesian coordinates atom + + atom type 1: atomic identification number = 74.0 representative = 1 + 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 1 + +atoms% 1 atoms 1 + Z( 1)= 74 atoms 1 + 0.000000 0.000000 0.000000 1 + ---------------------------------------------------- + Suggested values for input: + + Atom Z lmax jri rmt dx + W 74 10 841 2.544787 0.015844 +k_max = 3.92960 +G_max =11.78881 + + =============================================== + === modifying atomic input for &(all)atom === + =============================================== + +for atom 1 ( W) changed rmt to 2.100000 +for atom 1 ( W) changed jri to 981 +for atom 1 ( W) changed lmax to 12 +for atom 1 ( W) changed lnonsph to 6 +for atom 1 ( W) set econfig to [Kr] 4d10 4f14 | 5s2 5p6 6s2 5d4 + corestates = 16 with 60.0 electrons + valence st.= 5 with 14.0 electrons +nlod = 2 llod = 1 : 5s5p + nlo( 1) = 2 llo = 0 1 + lonqn = 5 5 +line: 13>&comp +line: 14>gmax=15.0 gmaxxc=12.5 kmax=5.0 / + ---------- + core : 1 -1 2.0 + core : 2 -1 2.0 + core : 2 1 2.0 + core : 2 -2 4.0 + core : 3 -1 2.0 + core : 3 1 2.0 + core : 3 -2 4.0 + core : 3 2 4.0 + core : 3 -3 6.0 + core : 4 -1 2.0 + core : 4 1 2.0 + core : 4 -2 4.0 + core : 4 2 4.0 + core : 4 -3 6.0 + core : 4 3 6.0 + core : 4 -4 8.0 + valence : 5 -1 2.0 5s + valence : 5 1 2.0 5p + valence : 5 -2 4.0 5p + valence : 6 -1 2.0 6s + valence : 5 2 2.0 5d + valence : 5 -3 2.0 5d + ---------- +Valence Electrons = 14 + ---------------------------------------------------- + Suggested values for input: + + Atom Z lmax jri rmt dx + W 74 10 841 2.544787 0.015844 +k_max = 3.92960 +G_max =11.78881 +line: 15>&kpt +line: 16>div1=3 div2=3 div3=3 tkb=0.0005 / + 5.22007561238382 5.22007561238382 5.22007561238382 + -0.333333333333333 -0.333333333333333 -0.333333333333333 + body centered cubic + values accepted unchanged + 3 3 3 nmop(i),i=1,3 + orientation of boundary faces + 1 -1 -0.5740361 ifac,iside,orient for xvec + 2 -1 -0.0454288 ifac,iside,orient for xvec + 3 -1 -0.0256305 ifac,iside,orient for xvec + 4 -1 -0.0469244 ifac,iside,orient for xvec +Bravais lattice vectors + -3.013812 3.013812 3.013812 + 3.013812 -3.013812 3.013812 + 3.013812 3.013812 -3.013812 +reciprocal lattice vectors + 0.000000 1.042398 1.042398 + 1.042398 0.000000 1.042398 + 1.042398 1.042398 0.000000 + 3 3 3 Monkhorst-Pack-parameters + 0 nreg; k-points in irreducible wedge of BZ + Monkhorst-Pack-fractions + 0 nbound; no k-points on boundary of BZ + 1 idim + -0.3333333 + 0.0000000 + 0.3333333 + 2 idim + -0.3333333 + 0.0000000 + 0.3333333 + 3 idim + -0.3333333 + 0.0000000 + 0.3333333 + +k-point count: 4 + +k-point mesh: 3 3 3 +k-point density: 2.035038 2.035038 2.035038 + diff --git a/tests/parsers/fixtures/inpgen/novalid_inpxml/out.error b/tests/parsers/fixtures/inpgen/novalid_inpxml/out.error new file mode 100644 index 000000000..e69de29bb diff --git a/tests/parsers/test_fleur_parser.py b/tests/parsers/test_fleur_parser.py index 30a6b4b4d..88da744dd 100644 --- a/tests/parsers/test_fleur_parser.py +++ b/tests/parsers/test_fleur_parser.py @@ -1,10 +1,19 @@ # -*- coding: utf-8 -*- -''' Contains tests for routines used by the fleur parser. ''' +''' Contains tests for the fleur parser and its routines. ''' from __future__ import absolute_import import os import pytest import math +from aiida.common import AttributeDict +from aiida import orm +import aiida_fleur +#TODO use pytest-regression for full dict tests, easier to update if parser changes. + +aiida_path = os.path.dirname(aiida_fleur.__file__) +TEST_INP_XML_PATH = os.path.join(aiida_path, '../tests/files/inpxml/Si/inp.xml') +# for relaxation path +TEST_INP_XML_PATH1 = os.path.join(aiida_path, '../tests/parsers/fixtures/fleur/relax/inp.xml') # parse_xmlout_file @@ -329,3 +338,183 @@ def test_parse_bands_file(): # test if the right aiida datastructures are produced for different output # also check if errors are working... # if an empty and broken file works, broken before and after first iteration + + +def test_fleur_parser_default_full(fixture_localhost, generate_parser, generate_calc_job_node, create_fleurinp, + data_regression): + """ + Default inpgen parser test of a successful inpgen calculation. + Checks via data regression if attributes of outputparamters are the same + """ + + name = 'default' + entry_point_calc_job = 'fleur.fleur' + entry_point_parser = 'fleur.fleurparser' + + inputs = AttributeDict({'fleurinp': create_fleurinp(TEST_INP_XML_PATH), 'metadata': {}}) + + #change retrieve list to save space + retrieve_list = [ + 'out.xml', 'inp.xml', 'shell.out', 'out.error', 'cdn1', '_scheduler-stdout.txt', '_scheduler-stderr.txt' + ] + node = generate_calc_job_node(entry_point_calc_job, + fixture_localhost, + name, + inputs, + store=True, + retrieve_list=retrieve_list) + parser = generate_parser(entry_point_parser) + results, calcfunction = parser.parse_from_node(node, store_provenance=False) + + assert calcfunction.is_finished, calcfunction.exception + assert calcfunction.is_finished_ok, calcfunction.exit_message + assert not orm.Log.objects.get_logs_for(node), [log.message for log in orm.Log.objects.get_logs_for(node)] + assert 'output_parameters' in results + assert 'output_params_complex' not in results + assert 'relax_parameters' not in results + assert 'error_params' not in results + print(results['output_parameters'].get_dict()) + res_dict = results['output_parameters'].get_dict() + + #data_regression.check({ + # 'output_parameters': dict(res_dict),#results['output_parameters'].get_dict(), + # }) + + +''' +def test_fleur_parser_band_dos(fixture_localhost, generate_parser, generate_calc_job_node, create_fleurinp, data_regression): + """ + Default inpgen parser test of a successful inpgen calculation. + Checks via data regression if attributes of fleurinp are the same + """ + + name = 'band_dos' + entry_point_calc_job = 'fleur.fleur' + entry_point_parser = 'fleur.fleurparser' + + inputs = AttributeDict({'fleurinp': create_fleurinp(TEST_INP_XML_PATH), + 'metadata' : {}}) + + #change retrieve list to save space + retrieve_list = ['out.xml', 'inp.xml', 'shell.out', 'out.error','cdn1','_scheduler-stdout.txt','_scheduler-stderr.txt'] + node = generate_calc_job_node(entry_point_calc_job, fixture_localhost, name, inputs, store=True, retrieve_list=retrieve_list) + parser = generate_parser(entry_point_parser) + results, calcfunction = parser.parse_from_node(node, store_provenance=False) + + assert calcfunction.is_finished, calcfunction.exception + assert calcfunction.is_finished_ok, calcfunction.exit_message + assert not orm.Log.objects.get_logs_for(node), [log.message for log in orm.Log.objects.get_logs_for(node)] + assert 'output_parameters' in results + assert 'output_params_complex' not in results + assert 'relax_parameters' not in results + assert 'error_params' not in results + + data_regression.check({ + 'output_parameters': results['output_parameters'].attributes, + }) +''' + + +def test_fleur_parser_relax(fixture_localhost, generate_parser, generate_calc_job_node, create_fleurinp, + data_regression): + """ + Default inpgen parser test of a successful inpgen calculation. + Checks via data regression if attributes of fleurinp are the same + """ + + name = 'relax' + entry_point_calc_job = 'fleur.fleur' + entry_point_parser = 'fleur.fleurparser' + + inputs = AttributeDict({'fleurinp': create_fleurinp(TEST_INP_XML_PATH1), 'metadata': {}}) + + #change retrieve list to save space + retrieve_list = ['out.xml', 'out.error', 'relax.xml'] + node = generate_calc_job_node(entry_point_calc_job, + fixture_localhost, + name, + inputs, + store=True, + retrieve_list=retrieve_list) + parser = generate_parser(entry_point_parser) + results, calcfunction = parser.parse_from_node(node, store_provenance=False) + + assert calcfunction.is_finished, calcfunction.exception + assert calcfunction.is_finished_ok, calcfunction.exit_message + assert not orm.Log.objects.get_logs_for(node), [log.message for log in orm.Log.objects.get_logs_for(node)] + assert 'output_parameters' in results + assert 'output_params_complex' not in results + assert 'relax_parameters' in results + assert 'error_params' not in results + + data_regression.check({ + #'output_parameters': results['output_parameters'].get_dict(), + 'relax_parameters': results['relax_parameters'].get_dict() + }) + + +def test_fleur_parser_MT_overlap_erroroutput(fixture_localhost, generate_parser, generate_calc_job_node, + create_fleurinp, data_regression): + """ + Default inpgen parser test of a failed fleur calculation. + """ + + name = 'mt_overlap_errorout' + entry_point_calc_job = 'fleur.fleur' + entry_point_parser = 'fleur.fleurparser' + + inputs = AttributeDict({'fleurinp': create_fleurinp(TEST_INP_XML_PATH1), 'metadata': {}}) + + #change retrieve list to save space + retrieve_list = ['out.xml', 'out.error', 'relax.xml'] + node = generate_calc_job_node(entry_point_calc_job, + fixture_localhost, + name, + inputs, + store=True, + retrieve_list=retrieve_list) + parser = generate_parser(entry_point_parser) + results, calcfunction = parser.parse_from_node(node, store_provenance=False) + + assert calcfunction.is_finished, calcfunction.exception + assert calcfunction.is_failed, calcfunction.exit_status + assert calcfunction.exit_status == node.process_class.exit_codes.ERROR_MT_RADII_RELAX.status + assert 'output_parameters' not in results + assert 'output_params_complex' not in results + assert 'relax_parameters' not in results + assert 'error_params' in results + data_regression.check({'error_params': results['error_params'].get_dict()}) + + +def test_fleur_parser_complex_erroroutput(fixture_localhost, generate_parser, generate_calc_job_node, create_fleurinp, + data_regression): + """ + Default inpgen parser test of a successful inpgen calculation. + Checks via data regression if attributes of fleurinp are the same + """ + + name = 'complex_errorout' + entry_point_calc_job = 'fleur.fleur' + entry_point_parser = 'fleur.fleurparser' + + inputs = AttributeDict({'fleurinp': create_fleurinp(TEST_INP_XML_PATH), 'metadata': {}}) + + #change retrieve list to save space + retrieve_list = ['out.xml', 'out.error', 'usage.json'] + node = generate_calc_job_node(entry_point_calc_job, + fixture_localhost, + name, + inputs, + store=True, + retrieve_list=retrieve_list) + parser = generate_parser(entry_point_parser) + results, calcfunction = parser.parse_from_node(node, store_provenance=False) + + assert calcfunction.is_finished, calcfunction.exception + assert calcfunction.is_failed, calcfunction.exit_status + assert calcfunction.exit_status == node.process_class.exit_codes.ERROR_FLEUR_CALC_FAILED.status + + assert 'output_parameters' not in results + assert 'output_params_complex' not in results + assert 'relax_parameters' not in results + assert 'error_params' not in results diff --git a/tests/parsers/test_fleur_parser/test_fleur_parser_MT_overlap_erroroutput.yml b/tests/parsers/test_fleur_parser/test_fleur_parser_MT_overlap_erroroutput.yml new file mode 100644 index 000000000..52ca0bdcf --- /dev/null +++ b/tests/parsers/test_fleur_parser/test_fleur_parser_MT_overlap_erroroutput.yml @@ -0,0 +1,8 @@ +error_params: + description: This output node contains informationabout FLEUR error + error_name: MT_OVERLAP_RELAX + iteration_number: 3 + overlaping_value: '6.8200E-03' + overlapped_indices: + - '2' + - '1' diff --git a/tests/parsers/test_fleur_parser/test_fleur_parser_relax.yml b/tests/parsers/test_fleur_parser/test_fleur_parser_relax.yml new file mode 100644 index 000000000..eed86ab67 --- /dev/null +++ b/tests/parsers/test_fleur_parser/test_fleur_parser_relax.yml @@ -0,0 +1,21 @@ +relax_parameters: + displacements: + - - 0.0 + - 0.0 + - 0.0246059526 + energies: + - -1.1623986264 + - -1.1630924497 + posforces: + - - - 0.0 + - 0.0 + - -0.75589045 + - 0.0 + - 0.0 + - 0.028382558 + - - - 0.0 + - 0.0 + - -0.741699171 + - 0.0 + - 0.0 + - 0.0208293472 diff --git a/tests/parsers/test_inpgen_parser.py b/tests/parsers/test_inpgen_parser.py index 5c682871c..5e1a4da56 100644 --- a/tests/parsers/test_inpgen_parser.py +++ b/tests/parsers/test_inpgen_parser.py @@ -1,5 +1,117 @@ # -*- coding: utf-8 -*- -# test all routines used by inpgen parser +''' Contains tests for the inpgen parser and its routines. ''' # TODO: implement all # test the full parser itself. + +from aiida.common import AttributeDict +from aiida import orm + + +def test_inpgen_parser_default(fixture_localhost, generate_parser, generate_calc_job_node, generate_structure, + data_regression): + """ + Default inpgen parser test of a successful inpgen calculation. + Checks via data regression if attributes of fleurinp are the same + """ + + name = 'default' + entry_point_calc_job = 'fleur.inpgen' + entry_point_parser = 'fleur.fleurinpgenparser' + + inputs = AttributeDict({'structure': generate_structure(), 'metadata': {}}) + node = generate_calc_job_node(entry_point_calc_job, fixture_localhost, name, inputs, store=True) + parser = generate_parser(entry_point_parser) + results, calcfunction = parser.parse_from_node(node, store_provenance=False) + + assert calcfunction.is_finished, calcfunction.exception + assert calcfunction.is_finished_ok, calcfunction.exit_message + assert not orm.Log.objects.get_logs_for(node), [log.message for log in orm.Log.objects.get_logs_for(node)] + assert 'fleurinpData' in results + + data_regression.check({ + 'fleurinpData': results['fleurinpData'].attributes, + }) + + +def test_inpgen_parser_no_inpxml(fixture_localhost, generate_parser, generate_calc_job_node, generate_structure): + """ + Default inpgen parser test of a failed inpgen calculation, inp.xml file missing. + """ + + name = 'no_inpxml' + entry_point_calc_job = 'fleur.inpgen' + entry_point_parser = 'fleur.fleurinpgenparser' + + inputs = AttributeDict({'structure': generate_structure(), 'metadata': {}}) + node = generate_calc_job_node(entry_point_calc_job, fixture_localhost, name, inputs, store=True) + parser = generate_parser(entry_point_parser) + results, calcfunction = parser.parse_from_node(node, store_provenance=False) + + assert calcfunction.is_finished, calcfunction.exception + assert calcfunction.is_failed, calcfunction.exit_status + assert calcfunction.exit_status == node.process_class.exit_codes.ERROR_NO_INPXML.status + assert 'fleurinpData' not in results + + +def test_inpgen_parser_no_other_files(fixture_localhost, generate_parser, generate_calc_job_node, generate_structure): + """ + Default inpgen parser test of a failed inpgen calculation, where files are missing. + """ + + name = 'no_otherfiles' + entry_point_calc_job = 'fleur.inpgen' + entry_point_parser = 'fleur.fleurinpgenparser' + + inputs = AttributeDict({'structure': generate_structure(), 'metadata': {}}) + node = generate_calc_job_node(entry_point_calc_job, fixture_localhost, name, inputs, store=True) + parser = generate_parser(entry_point_parser) + results, calcfunction = parser.parse_from_node(node, store_provenance=False) + + assert calcfunction.is_finished, calcfunction.exception + assert calcfunction.is_failed, calcfunction.exit_status + assert calcfunction.exit_status == node.process_class.exit_codes.ERROR_MISSING_RETRIEVED_FILES.status + assert 'fleurinpData' not in results + + +def test_inpgen_parser_broken_inpxml(fixture_localhost, generate_parser, generate_calc_job_node, generate_structure): + """ + Default inpgen parser test of a failed inpgen calculation with broken xml. + """ + + name = 'broken_inpxml' + entry_point_calc_job = 'fleur.inpgen' + entry_point_parser = 'fleur.fleurinpgenparser' + + inputs = AttributeDict({'structure': generate_structure(), 'metadata': {}}) + node = generate_calc_job_node(entry_point_calc_job, fixture_localhost, name, inputs, store=True) + parser = generate_parser(entry_point_parser) + results, calcfunction = parser.parse_from_node(node, store_provenance=False) + + assert calcfunction.is_finished, calcfunction.exception + assert calcfunction.is_failed, calcfunction.exit_status + assert calcfunction.exit_status == node.process_class.exit_codes.ERROR_FLEURINPDATA_INPUT_NOT_VALID.status + assert 'fleurinpData' not in results + + +def test_inpgen_parser_nonvalid_inpxml(fixture_localhost, generate_parser, generate_calc_job_node, generate_structure): + """ + Default inpgen parser test of a failed inpgen calculation with non valid inpmxl. + """ + + name = 'novalid_inpxml' + entry_point_calc_job = 'fleur.inpgen' + entry_point_parser = 'fleur.fleurinpgenparser' + + inputs = AttributeDict({'structure': generate_structure(), 'metadata': {}}) + node = generate_calc_job_node(entry_point_calc_job, fixture_localhost, name, inputs, store=True) + parser = generate_parser(entry_point_parser) + results, calcfunction = parser.parse_from_node(node, store_provenance=False) + + assert calcfunction.is_finished, calcfunction.exception + assert calcfunction.is_failed, calcfunction.exit_status + assert calcfunction.exit_status == node.process_class.exit_codes.ERROR_FLEURINPDATA_INPUT_NOT_VALID.status + assert 'fleurinpData' not in results + + +# TODO test multi files, enpara, kpts, relax.xml nnmpmat ... diff --git a/tests/parsers/test_inpgen_parser/test_inpgen_parser_default.yml b/tests/parsers/test_inpgen_parser/test_inpgen_parser_default.yml new file mode 100644 index 000000000..29544873d --- /dev/null +++ b/tests/parsers/test_inpgen_parser/test_inpgen_parser_default.yml @@ -0,0 +1,353 @@ +fleurinpData: + _has_schema: true + _schema_file_path: /home/broeder/aiida/github/judft/aiida-fleur/aiida_fleur/fleur_schema/input/0.31/FleurInputSchema.xsd + _search_paths: + - /home/broeder/aiida/github/judft/aiida-fleur/aiida_fleur/fleur_schema/input/0.27/FleurInputSchema.xsd + - /home/broeder/aiida/github/judft/aiida-fleur/aiida_fleur/fleur_schema/input/0.28/FleurInputSchema.xsd + - /home/broeder/aiida/github/judft/aiida-fleur/aiida_fleur/fleur_schema/input/0.29/FleurInputSchema.xsd + - /home/broeder/aiida/github/judft/aiida-fleur/aiida_fleur/fleur_schema/input/0.30/FleurInputSchema.xsd + - /home/broeder/aiida/github/judft/aiida-fleur/aiida_fleur/fleur_schema/input/0.31/FleurInputSchema.xsd + - /home/broeder/aiida/github/judft/aiida-fleur/aiida_fleur/fleur_schema/input/0.32/FleurInputSchema.xsd + - /home/broeder/aiida/github/judft/aiida-fleur/aiida_fleur/fleur_schema/input/0.33/FleurInputSchema.xsd + - /home/broeder/aiida/github/judft/aiida-fleur/aiida_fleur/fleur_schema + - ./ + files: + - inp.xml + inp_dict: + 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 + 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 + calculationSetup: + bzIntegration: + altKPointSet: + kPointCount: + count: 240 + gamma: false + purpose: bands + fermiSmearingEnergy: 0.0005 + 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 + posScale: '1.00000000' + weightScale: '1.00000000' + mode: hist + valenceElectrons: 14.0 + coreElectrons: + coretail_lmax: '0' + ctail: true + frcor: false + kcrel: 0 + cutoffs: + Gmax: 15.0 + GmaxXC: 12.5 + Kmax: 5.0 + numbands: 0 + energyParameterLimits: + ellow: -1.8 + elup: 1.0 + expertModes: + gw: 0 + secvar: false + geometryOptimization: + epsdisp: 1.0e-05 + epsforce: 1.0e-05 + forcealpha: 1.0 + forcemix: BFGS + l_f: false + ldaU: + - l_linMix: false + mixParam: 0.05 + spinf: 1.0 + magnetism: + jspins: 1 + l_noco: false + lflip: false + swsp: false + nocoParams: + l_constr: F + l_mperp: F + l_ss: false + mix_b: '.00000000' + qss: .0000000000 .0000000000 .0000000000 + prodBasis: + bands: '0' + ewaldlambda: '3' + gcutm: '3.40000000' + lexp: '16' + tolerance: '.00010000' + scfLoop: + alpha: 0.05 + imix: Anderson + itmax: 15 + maxIterBroyd: 99 + minDistance: 1.0e-05 + precondParam: '0.0' + spinf: 2.0 + soc: + l_soc: false + phi: 0.0 + spav: false + theta: 0.0 + 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 + 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 + comment: A Fleur input generator calculation with aiida + fleurInputVersion: '0.31' + output: + band: false + chargeDensitySlicing: + maxEigenval: 0.0 + minEigenval: 0.0 + nnne: 0 + numkpt: 0 + pallst: false + checks: + cdinf: false + vchk: false + densityOfStates: + maxEnergy: 0.5 + minEnergy: -0.5 + ndir: 0 + sigma: 0.015 + dos: false + magneticCircularDichroism: + energyLo: '-10.00000000' + energyUp: '.00000000' + mcd: F + plotting: + iplot: 0 + plplot: false + score: false + slice: false + specialOutput: + bmt: false + eonly: false + unfoldingBand: + supercellX: '1' + supercellY: '1' + supercellZ: '1' + unfoldBand: F + vacdos: false + vacuumDOS: + integ: false + layers: 0 + locx1: 0.0 + locx2: 0.0 + locy1: 0.0 + locy2: 0.0 + nstars: 0 + nstm: 0 + star: false + tworkf: 0.0 + xcFunctional: + name: pbe + relativisticCorrections: false From f48696b79b4811f0a046bfb979758bdd71b34f7f Mon Sep 17 00:00:00 2001 From: broeder-j Date: Thu, 10 Dec 2020 22:09:55 +0100 Subject: [PATCH 41/53] Make regression comparisson work for out.xml files, also inpgen on CI --- tests/parsers/test_fleur_parser.py | 28 +- .../test_fleur_parser_default_full.yml | 35 + .../test_fleur_parser_relax.yml | 61 ++ tests/parsers/test_inpgen_parser.py | 2 +- .../test_inpgen_parser_default.yml | 685 +++++++++--------- 5 files changed, 454 insertions(+), 357 deletions(-) create mode 100644 tests/parsers/test_fleur_parser/test_fleur_parser_default_full.yml diff --git a/tests/parsers/test_fleur_parser.py b/tests/parsers/test_fleur_parser.py index 88da744dd..e6cbe1a1b 100644 --- a/tests/parsers/test_fleur_parser.py +++ b/tests/parsers/test_fleur_parser.py @@ -373,12 +373,10 @@ def test_fleur_parser_default_full(fixture_localhost, generate_parser, generate_ assert 'output_params_complex' not in results assert 'relax_parameters' not in results assert 'error_params' not in results - print(results['output_parameters'].get_dict()) - res_dict = results['output_parameters'].get_dict() - #data_regression.check({ - # 'output_parameters': dict(res_dict),#results['output_parameters'].get_dict(), - # }) + data_regression.check({ + 'output_parameters': clean_outdict_for_reg_dump(results['output_parameters'].get_dict()), + }) ''' @@ -448,7 +446,7 @@ def test_fleur_parser_relax(fixture_localhost, generate_parser, generate_calc_jo assert 'error_params' not in results data_regression.check({ - #'output_parameters': results['output_parameters'].get_dict(), + 'output_parameters': clean_outdict_for_reg_dump(results['output_parameters'].get_dict()), 'relax_parameters': results['relax_parameters'].get_dict() }) @@ -518,3 +516,21 @@ def test_fleur_parser_complex_erroroutput(fixture_localhost, generate_parser, ge assert 'output_params_complex' not in results assert 'relax_parameters' not in results assert 'error_params' not in results + + +def clean_outdict_for_reg_dump(outdict): + """ + Apparently the regression dumper has problems with + ' ', '0.33', 'fleur 31', dates + we remove these keys. + """ + outdict.pop('creator_target_structure', None) + outdict.pop('creator_name', None) + outdict.pop('creator_target_architecture', None) + outdict.pop('title', None) + outdict.pop('output_file_version', None) + outdict.pop('start_date', None) + outdict.pop('end_date', None) + outdict.pop('relax_atomtype_info', None) + + return outdict 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 new file mode 100644 index 000000000..d68282aba --- /dev/null +++ b/tests/parsers/test_fleur_parser/test_fleur_parser_default_full.yml @@ -0,0 +1,35 @@ +output_parameters: + bandgap: 0.85561712 + bandgap_units: eV + charge_den_xc_den_integral: -41.74447706 + charge_density: 3.29535e-05 + density_convergence_units: me/bohr^3 + energy: -15784.562940686706 + energy_core_electrons: -316.8117176949 + energy_hartree: -580.0719889092 + energy_hartree_units: Htr + energy_units: eV + energy_valence_electrons: 0.1710194344 + fermi_energy: 0.2022322894 + fermi_energy_units: Htr + force_largest: -0.0 + kmax: 3.5 + number_of_atom_types: 1 + number_of_atoms: 2 + number_of_iterations: 11 + number_of_iterations_total: 11 + number_of_kpoints: 60 + number_of_species: 1 + number_of_spin_components: 1 + number_of_symmetries: 48 + parser_info: AiiDA Fleur Parser v0.3.2 + parser_warnings: [] + sum_of_eigenvalues: -316.6406982605 + unparsed: [] + 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 eed86ab67..5cc7e75c9 100644 --- a/tests/parsers/test_fleur_parser/test_fleur_parser_relax.yml +++ b/tests/parsers/test_fleur_parser/test_fleur_parser_relax.yml @@ -1,3 +1,64 @@ +output_parameters: + abspos_x_type1: 0.0 + abspos_y_type1: 0.0 + abspos_z_type1: -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 + energy_hartree_units: Htr + energy_units: eV + energy_valence_electrons: -0.747242106 + fermi_energy: -0.373621053 + fermi_energy_units: Htr + film: 'True' + force_largest: 0.02082935 + force_units: Htr/bohr + force_x_type1: 0.0 + force_y_type1: 0.0 + force_z_type1: 0.02082935 + kmax: 5.0 + number_of_atom_types: 1 + number_of_atoms: 2 + number_of_iterations: 42 + number_of_iterations_total: 142 + number_of_kpoints: 1 + number_of_species: 1 + number_of_spin_components: 1 + number_of_symmetries: 16 + parser_info: AiiDA Fleur Parser v0.3.2 + parser_warnings: + - 'Can not get attributename: "units" from node "[]", because node is not an element + of etree.' + relax_atom_positions: + - - 0.0 + - 0.0 + - -0.741699171 + - - 0.0 + - 0.0 + - 0.741699171 + relax_brav_vectors: + - - 28.345891875 + - 0.0 + - 0.0 + - - 0.0 + - 28.345891875 + - 0.0 + - - 0.0 + - 0.0 + - 4.63 + sum_of_eigenvalues: -0.747242106 + unparsed: [] + walltime: 10290 + walltime_units: seconds + warnings: + debug: {} + error: {} + info: {} + warning: {} relax_parameters: displacements: - - 0.0 diff --git a/tests/parsers/test_inpgen_parser.py b/tests/parsers/test_inpgen_parser.py index 5e1a4da56..d70ab37ff 100644 --- a/tests/parsers/test_inpgen_parser.py +++ b/tests/parsers/test_inpgen_parser.py @@ -30,7 +30,7 @@ def test_inpgen_parser_default(fixture_localhost, generate_parser, generate_calc assert 'fleurinpData' in results data_regression.check({ - 'fleurinpData': results['fleurinpData'].attributes, + 'fleurinpData': results['fleurinpData'].inp_dict, }) 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 29544873d..45b4bbfde 100644 --- a/tests/parsers/test_inpgen_parser/test_inpgen_parser_default.yml +++ b/tests/parsers/test_inpgen_parser/test_inpgen_parser_default.yml @@ -1,353 +1,338 @@ fleurinpData: - _has_schema: true - _schema_file_path: /home/broeder/aiida/github/judft/aiida-fleur/aiida_fleur/fleur_schema/input/0.31/FleurInputSchema.xsd - _search_paths: - - /home/broeder/aiida/github/judft/aiida-fleur/aiida_fleur/fleur_schema/input/0.27/FleurInputSchema.xsd - - /home/broeder/aiida/github/judft/aiida-fleur/aiida_fleur/fleur_schema/input/0.28/FleurInputSchema.xsd - - /home/broeder/aiida/github/judft/aiida-fleur/aiida_fleur/fleur_schema/input/0.29/FleurInputSchema.xsd - - /home/broeder/aiida/github/judft/aiida-fleur/aiida_fleur/fleur_schema/input/0.30/FleurInputSchema.xsd - - /home/broeder/aiida/github/judft/aiida-fleur/aiida_fleur/fleur_schema/input/0.31/FleurInputSchema.xsd - - /home/broeder/aiida/github/judft/aiida-fleur/aiida_fleur/fleur_schema/input/0.32/FleurInputSchema.xsd - - /home/broeder/aiida/github/judft/aiida-fleur/aiida_fleur/fleur_schema/input/0.33/FleurInputSchema.xsd - - /home/broeder/aiida/github/judft/aiida-fleur/aiida_fleur/fleur_schema - - ./ - files: - - inp.xml - inp_dict: - 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 - 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 - calculationSetup: - bzIntegration: - altKPointSet: - kPointCount: - count: 240 - gamma: false - purpose: bands - fermiSmearingEnergy: 0.0005 - 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 - posScale: '1.00000000' - weightScale: '1.00000000' - mode: hist - valenceElectrons: 14.0 - coreElectrons: - coretail_lmax: '0' - ctail: true - frcor: false - kcrel: 0 - cutoffs: - Gmax: 15.0 - GmaxXC: 12.5 - Kmax: 5.0 - numbands: 0 - energyParameterLimits: - ellow: -1.8 - elup: 1.0 - expertModes: - gw: 0 - secvar: false - geometryOptimization: - epsdisp: 1.0e-05 - epsforce: 1.0e-05 - forcealpha: 1.0 - forcemix: BFGS - l_f: false - ldaU: - - l_linMix: false - mixParam: 0.05 - spinf: 1.0 - magnetism: - jspins: 1 - l_noco: false - lflip: false - swsp: false + atomGroups: + atomGroup: + - force: + calculate: true + relaxXYZ: TTT nocoParams: - l_constr: F - l_mperp: F - l_ss: false - mix_b: '.00000000' - qss: .0000000000 .0000000000 .0000000000 + alpha: 0.0 + b_cons_x: '.00000000' + b_cons_y: '.00000000' + beta: '.00000000' + l_relax: F + relPos: + - .0000000000 .0000000000 .0000000000 + 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: - bands: '0' - ewaldlambda: '3' - gcutm: '3.40000000' - lexp: '16' - tolerance: '.00010000' - scfLoop: - alpha: 0.05 - imix: Anderson - itmax: 15 - maxIterBroyd: 99 - minDistance: 1.0e-05 - precondParam: '0.0' - spinf: 2.0 - soc: - l_soc: false - phi: 0.0 - spav: false - theta: 0.0 - 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 - 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 - comment: A Fleur input generator calculation with aiida - fleurInputVersion: '0.31' - output: - band: false - chargeDensitySlicing: - maxEigenval: 0.0 - minEigenval: 0.0 - nnne: 0 - numkpt: 0 - pallst: false - checks: - cdinf: false - vchk: false - densityOfStates: - maxEnergy: 0.5 - minEnergy: -0.5 - ndir: 0 - sigma: 0.015 - dos: false - magneticCircularDichroism: - energyLo: '-10.00000000' - energyUp: '.00000000' - mcd: F - plotting: - iplot: 0 - plplot: false - score: false - slice: false - specialOutput: - bmt: false - eonly: false - unfoldingBand: - supercellX: '1' - supercellY: '1' - supercellZ: '1' - unfoldBand: F - vacdos: false - vacuumDOS: - integ: false - layers: 0 - locx1: 0.0 - locx2: 0.0 - locy1: 0.0 - locy2: 0.0 - nstars: 0 - nstm: 0 - star: false - tworkf: 0.0 - xcFunctional: - name: pbe - relativisticCorrections: false + lcutm: '4' + lcutwf: '11' + select: 4 0 4 2 + calculationSetup: + bzIntegration: + altKPointSet: + kPointCount: + count: 240 + gamma: false + purpose: bands + fermiSmearingEnergy: 0.0005 + 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 + posScale: '1.00000000' + weightScale: '1.00000000' + mode: hist + valenceElectrons: 14.0 + coreElectrons: + coretail_lmax: '0' + ctail: true + frcor: false + kcrel: 0 + cutoffs: + Gmax: 15.0 + GmaxXC: 12.5 + Kmax: 5.0 + numbands: 0 + energyParameterLimits: + ellow: -1.8 + elup: 1.0 + expertModes: + gw: 0 + secvar: false + geometryOptimization: + epsdisp: 1.0e-05 + epsforce: 1.0e-05 + forcealpha: 1.0 + forcemix: BFGS + l_f: false + ldaU: + - l_linMix: false + mixParam: 0.05 + spinf: 1.0 + magnetism: + jspins: 1 + l_noco: false + lflip: false + swsp: false + nocoParams: + l_constr: F + l_mperp: F + l_ss: false + mix_b: '.00000000' + qss: .0000000000 .0000000000 .0000000000 + prodBasis: + bands: '0' + ewaldlambda: '3' + gcutm: '3.40000000' + lexp: '16' + tolerance: '.00010000' + scfLoop: + alpha: 0.05 + imix: Anderson + itmax: 15 + maxIterBroyd: 99 + minDistance: 1.0e-05 + precondParam: '0.0' + spinf: 2.0 + soc: + l_soc: false + phi: 0.0 + spav: false + theta: 0.0 + 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 + 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 + comment: A Fleur input generator calculation with aiida + fleurInputVersion: '0.31' + output: + band: false + chargeDensitySlicing: + maxEigenval: 0.0 + minEigenval: 0.0 + nnne: 0 + numkpt: 0 + pallst: false + checks: + cdinf: false + vchk: false + densityOfStates: + maxEnergy: 0.5 + minEnergy: -0.5 + ndir: 0 + sigma: 0.015 + dos: false + magneticCircularDichroism: + energyLo: '-10.00000000' + energyUp: '.00000000' + mcd: F + plotting: + iplot: 0 + plplot: false + score: false + slice: false + specialOutput: + bmt: false + eonly: false + unfoldingBand: + supercellX: '1' + supercellY: '1' + supercellZ: '1' + unfoldBand: F + vacdos: false + vacuumDOS: + integ: false + layers: 0 + locx1: 0.0 + locx2: 0.0 + locy1: 0.0 + locy2: 0.0 + nstars: 0 + nstm: 0 + star: false + tworkf: 0.0 + xcFunctional: + name: pbe + relativisticCorrections: false From a0d923d1d4c40eaac7446ba2450a624c9d7c6a33 Mon Sep 17 00:00:00 2001 From: broeder-j Date: Mon, 14 Dec 2020 09:50:03 +0100 Subject: [PATCH 42/53] Add more tests for create corehole and read_cif_folder Also remove the some of the old todo files for these from tests --- aiida_fleur/tools/create_corehole.py | 49 +++---- aiida_fleur/tools/read_cif_folder.py | 16 ++- .../btest_fleurinpmodifier_py.back | 114 --------------- tests/old_todo/util/36.dot | 4 - .../util/btest_create_corehole_py.back | 101 -------------- tests/old_todo/util/btest_read_cif_folder.py | 18 --- tests/tools/test_create_corehole.py | 131 ++++++++++++++++++ .../test_create_corehole_para.yml | 24 ++++ tests/tools/test_io_routines.py | 6 + tests/tools/test_read_cif_folder.py | 42 ++++-- 10 files changed, 220 insertions(+), 285 deletions(-) delete mode 100644 tests/old_todo/data/fleurinpmodifier/btest_fleurinpmodifier_py.back delete mode 100644 tests/old_todo/util/36.dot delete mode 100644 tests/old_todo/util/btest_create_corehole_py.back delete mode 100644 tests/old_todo/util/btest_read_cif_folder.py create mode 100644 tests/tools/test_create_corehole/test_create_corehole_para.yml diff --git a/aiida_fleur/tools/create_corehole.py b/aiida_fleur/tools/create_corehole.py index b81cc5617..ae624efcd 100644 --- a/aiida_fleur/tools/create_corehole.py +++ b/aiida_fleur/tools/create_corehole.py @@ -13,48 +13,42 @@ Contains helper functions to create core-holes in Fleur input files from AiiDA data nodes. ''' -from __future__ import absolute_import -from __future__ import print_function -from aiida.plugins import DataFactory -import six + # TODO maybe merge these methods into fleurinp or structure util? or create a parameterData utils #355 - - def create_corehole_para(structure, kind, econfig, species_name='corehole', parameterdata=None): """ This methods sets of electron configurations for a kind or position given, make sure to break the symmetry for this position/kind beforehand, otherwise you will create several coreholes. - param: structure: StructureData - param: kind, a string with the kind_name (TODO: alternative the kind object) - param: econfig, string, e.g. econfig = "[Kr] 5s2 4d10 4f13 | 5p6 5d5 6s2" - ! THis is the new econfig therefore + :param structure: StructureData + :param kind: a string with the kind_name (TODO: alternative the kind object) + :param econfig: string, e.g. econfig = "[Kr] 5s2 4d10 4f13 | 5p6 5d5 6s2" to set, i.e. the corehole + ! This is the new (MaXR2) econfig therefore - returns a Dict node + :return: a Dict node """ - + # TODO: Since fleur MaXR5 there is a default econfig file and the order behavior + # has changed. now to atom lists only change the default if they have an id. from aiida.common.constants import elements as PeriodicTableElements - - _atomic_numbers = {data['symbol']: num for num, data in six.iteritems(PeriodicTableElements)} + from aiida import orm + _atomic_numbers = {data['symbol']: num for num, data in PeriodicTableElements.items()} #from aiida_fleur.tools.merge_parameter import merge_parameter kindo = structure.get_kind(kind) symbol = kindo.symbol head = kindo.name.rstrip('01223456789') - #print(kindo) charge = _atomic_numbers[kindo.symbol] a_id = float('{}.{}'.format(charge, kindo.name[len(head):])) - #print('a_id {}'.format(a_id)) # get kind symbol, get kind name, #&atom element="W" jri=921 lmax=8 rmt=2.52 dx=0.014 lo="5p" econfig="[Kr] 5s2 4d10 4f13 | 5p6 5d4 6s2" / #count = 0 if parameterdata: new_parameterd = parameterdata.get_dict() # dict()otherwise parameterdata is changed - for key, val in six.iteritems(new_parameterd): + for key, val in new_parameterd.items(): if 'atom' in key: if val.get('element', None) == symbol: # remember atomic id is atomic number.some int @@ -74,13 +68,13 @@ def create_corehole_para(structure, kind, econfig, species_name='corehole', para else: new_parameterd = {'atom': {'element': symbol, 'econfig': econfig}} - from aiida.orm import Dict - new_parameter = Dict(dict=new_parameterd) + new_parameter = orm.Dict(dict=new_parameterd) #if parameterdata: # new_parameter = merge_parameter(parameterdata, new_parameter) return new_parameter #structure +''' # Move to fleurinpmod? fleurinp->self # This method is fully implemented yet since it turned out to better go over inpgen def create_corehole_fleurinp(fleurinp, species, stateocc, pos=None, coreconfig='same', valenceconfig='same'): @@ -110,14 +104,14 @@ def create_corehole_fleurinp(fleurinp, species, stateocc, pos=None, coreconfig=' :return: the changes fleurinpData object """ - ''' - - [Kr] (5s1/2) (4d3/2) (4d5/2) (4f5/2) (4f7/2) - (5p1/2) (5p3/2) (6s1/2) (5d3/2) (5d5/2) - - - - ''' + + # + # [Kr] (5s1/2) (4d3/2) (4d5/2) (4f5/2) (4f7/2) + # (5p1/2) (5p3/2) (6s1/2) (5d3/2) (5d5/2) + # + # + # + from aiida_fleur.tools.xml_util import eval_xpath2, get_xml_attribute #from aiida_fleur.data.fleurinpmodifier import FleurinpModifier # or from fleurinp? @@ -191,3 +185,4 @@ def write_change(xmltree, changelist_xpath): xpath = element[0] value = element[1] return xmltree_new +''' diff --git a/aiida_fleur/tools/read_cif_folder.py b/aiida_fleur/tools/read_cif_folder.py index 6401eb8f8..d8104460e 100644 --- a/aiida_fleur/tools/read_cif_folder.py +++ b/aiida_fleur/tools/read_cif_folder.py @@ -49,6 +49,8 @@ def read_cif_folder(path=os.getcwd(), :params: extras: dir/string/arb: extras added to the structures stored in the db """ + # TODO check for duplicates in the database, so that reruning the functions + # won't import anything else in the database cifdata = DataFactory('cif') ############ parameters for the user to set ######## @@ -104,7 +106,7 @@ def read_cif_folder(path=os.getcwd(), # continue #asecell = new_cif[0].get_ase() #structuredatas.append(DataFactory('structure')) - #filenames2.append(filenames[i]) + filenames2.append(filenames[i]) #struc = structuredatas[-1](ase=asecell) #formula = struc.get_formula() if store_db: @@ -119,7 +121,7 @@ def read_cif_folder(path=os.getcwd(), user = struc.user # we are the creator struc.add_comment(comment, user) if extra: - if isinstance(extra, type(dict())): + if isinstance(extra, dict): struc.set_extra_many(extra) else: struc.set_extra('specification', extra) @@ -127,7 +129,7 @@ def read_cif_folder(path=os.getcwd(), structuredatas2.append(struc) else: struc = struc_from_cif(new_cif[0]) - structuredatas.append(struc) + structuredatas2.append(struc) formula = struc.get_formula() if write_log: # This file is a logfile/info file created by 'read_cif_folder' @@ -154,9 +156,7 @@ def read_cif_folder(path=os.getcwd(), @cf def wf_struc_from_cif(cif): - asecell = cif.get_ase() - struc = DataFactory('structure')(ase=asecell) - return struc + return struc_from_cif(cif) def struc_from_cif(cif): @@ -165,6 +165,9 @@ def struc_from_cif(cif): return struc +# TODO add this to command line, or better move to aiida-jutools +# ggf add what Roman has done there. +''' if __name__ == '__main__': import argparse import json @@ -212,3 +215,4 @@ def struc_from_cif(cif): read_cif_folder(path=args.p, recursive=args.r, store=args.s, log=args.l, comments=args.c, extras=args.e) else: read_cif_folder(recursive=args.r, store=args.s, log=args.l, comments=args.c, extras=args.e) +''' diff --git a/tests/old_todo/data/fleurinpmodifier/btest_fleurinpmodifier_py.back b/tests/old_todo/data/fleurinpmodifier/btest_fleurinpmodifier_py.back deleted file mode 100644 index b06a01104..000000000 --- a/tests/old_todo/data/fleurinpmodifier/btest_fleurinpmodifier_py.back +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -This test tries to create a fleurinpdata and to modefy it -""" -from __future__ import absolute_import -from __future__ import print_function -from aiida.orm import ParameterData -import time -import os -from lxml import etree -from pprint import pprint -from aiida_fleur.data.fleurinpmodifier import FleurinpModifier -from aiida.plugins import DataFactory -from aiida_fleur.data.fleurinp import FleurinpData -from aiida import load_dbenv, is_dbenv_loaded -if not is_dbenv_loaded(): - load_dbenv() - -start_time = time.time() - -# schemanameQq - -path = os.getcwd() # path.realpath(__file__) -print(path) -filepath = path + '/inp.xml' -# '/Users/broeder/aiida/scratch/broeder/aiida_run2/ff/4c/c14d-8a1b-40b3-af95-400e23002bcb/inp.xml' - -new_fleurinpData = FleurinpData(files=[filepath]) -# print(new_fleurinpData.get_file_abs_path('inp.xml')) -# new_fleurinpData.store() - -#new_fleurinpData= load_node(6) - -fleurmode = FleurinpModifier(new_fleurinpData) - -#fleurmode.set_switch({'dos': True}) - -fleurmode.set_inpchanges({}) -fleurmode.set_inpchanges({'dos': True}) -tria = True -nkpts = 800 - -change_dict = {'dos': True, 'ndir': -1, 'minEnergy': -0.8, 'maxEnergy': 0.8, 'sigma': 0.005} - -fleurmode.set_inpchanges(change_dict) -if tria: - change_dict = {'mode': 'tria'} - fleurmode.set_inpchanges(change_dict) -if nkpts: - fleurmode.set_nkpts(count=nkpts) -''' -fleurmode.set_species('W-1', {'radius' : 3.5}) -fleurmode.change_atom('forces', True, position=(0.0, 0.0, 0.0)) -fleurmode.set_xpath('/fleurinput/@dos', True) -''' -''' -name = 'Na-1' -xpathn = '/fleurInput/atomSpecies/species[@name = "{}"]/mtSphere'.format(name)# 'radius': -attributename = 'radius' -attribv = 0.0000 -fleurmode.xml_set_all_attribv(xpathn, attributename, attribv) - -xpathn = '/fleurInput/atomSpecies/species/mtSphere' -xpathn = '/fleurInput' -#attribv = [0.0000, '1.2', 3.4] -#fleurmode.xml_set_all_attribv(xpathn, attributename, attribv) - - -#fleurmode.xml_set_attribv_occ(xmltree, xpathn, attributename, attribv, occ=[0]) -#fleurmode.xml_set_first_attribv(xmltree, xpathn, attributename, attribv) -xpathn = '/fleurInput/atomSpecies/species[@name = "{}"]/mtSphere/@radius'.format(name)# 'radius': - -attribv = 1.1111 -#set_xpath(xmltree, xpathn, attribv)# does not work - - -xpathn = '/fleurInput/atomGroups/atomGroup/relPos' -text = '1.20000 PI/3 5.1-MYCrazyCostant' -#fleurmode.xml_set_all_text(xpathn, text) - - -fleurmode.set_species('Na-1', { 'mtSphere' : {'radius' : 3.5}}) -fleurmode.set_species('W-2', {'atomicCutoffs' : {'lmax' : 9, 'lnonsphr': 7}, 'energyParameters': {'d' : 6}, 'mtSphere' : {'radius' : 2.6, 'gridPoints' : 925, 'logIncrement' : .01800000}}) -print 'here1' -# -fleurmode.set_species('W-2', {'electronConfig' : {'coreConfig' : '[Xe] (4f5/2) (4f7/2)', 'valenceConfig' : '(6s1/2) (5d3/2) (5d5/2) (6p1/2) (6p3/2)'}}, create=True)#, 'stateOccupation' : [{'state' : "(6p3/2)", 'spinUp' : "1.00000000", 'spinDown' : "1.00000000"}, {'state' : "(6p1/2)", 'spinUp' : "1.00000000", 'spinDown' : "1.00000000"}]}}, create=True) - - -xpathn = '/fleurInput/atomSpecies/species[@name = "{}"]'.format(name)# 'radius': -xpathn2 = '/fleurInput/atomSpecies/species[@name = "{}"]/electronConfig3/e/d/g/f/yeah'.format(name) - -new_e = etree.Element('lo') -new_e.set('type', "SCLO") - -#fleurmode.create_tag(xpathn, new_e, True) -#fleurmode.create_tag(xpathn,'electronConfig2', True) - -#eval_xpath3(root, xpathn2, create=True) -#fleurmode.set_species('W-2', {'lo': {'type':"SCLO", 'l' : 1, 'n' : 5, 'eDeriv' : 1}}, True) - -#fleurmode.set_species('W-2', {'lo': [{'type':"SCLO", 'l' : 1, 'n' : 5, 'eDeriv' : 2}]}, True) -#fleurmode.set_species('W-2', {'lo': [{'type':"SCLO", 'l' : 1, 'n' : 5, 'eDeriv' : 3}, {'type':"SCLO", 'l' : 1, 'n' : 5, 'eDeriv' : 4}, {'type':"SCLO", 'l' : 1, 'n' : 5, 'eDeriv' : 5}, {'type':"SCLO", 'l' : 1, 'n' : 5, 'eDeriv' : 6}]}, True) -''' - -# fleurmode.changes() -fleurmode.show(validate=True) # , display=False) - -print(fleurmode._original) -print(fleurmode._tasks) -out = '' # fleurmode.freeze() - -print('out: {}'.format(out)) -print('in: {}'.format(new_fleurinpData)) diff --git a/tests/old_todo/util/36.dot b/tests/old_todo/util/36.dot deleted file mode 100644 index 8126966e4..000000000 --- a/tests/old_todo/util/36.dot +++ /dev/null @@ -1,4 +0,0 @@ -digraph G { - N36 [shape=ellipse,label="StructureData (36) -Cr",color="lightblue",style="filled"]; -} diff --git a/tests/old_todo/util/btest_create_corehole_py.back b/tests/old_todo/util/btest_create_corehole_py.back deleted file mode 100644 index bfec885fa..000000000 --- a/tests/old_todo/util/btest_create_corehole_py.back +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/env python -# -*- 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, os -from aiida.orm.querybuilder import QueryBuilder - -from aiida_fleur_ad.util.create_corehole import create_corehole, create_corehole_fleurinp, write_change -from aiida_fleur.data.fleurinp import FleurinpData -from aiida.plugins import Code, CalculationFactory, DataFactory -from aiida.orm import load_node -from pprint import pprint -from aiida_fleur.tools.StructureData_util import break_symmetry as bs - -from aiida.orm import StructureData -''' -ids = [13586, 13934, 12748]#, 12927] - -for id in ids: - s = load_node(id) - new_s = bs(s, atoms=['Be'], site=[0, 1], pos=[(0.0, 0.0, -1.83792744752922), (0.0, 0.0, 0.918963723764612)]) - #new_s.store() -''' -#s = load_node(355) - -ids = [] #13924]#, 13925]#, 13926, 13927, 13928, 13929, 13930, 13931, 13932, 13933, 13934, 13935] -#ids = [479, 480, 481, 482, 537]# O12W4, O12W4, O6W2, O6W2, O36W3Y18 - -kind = 'W1' -econfig = '[Kr] 5s2 4d10 4f13 | 5p6 5d5 6s2' -para1 = Dict( - dict={ - 'title': 'A test calculation of Tungsten', - 'input': { - 'film': False, - 'cartesian': True, - }, - 'atom': { - 'element': 'W', - 'jri': 833, - 'rmt': 2.3, - 'dx': 0.015, - 'lmax': 8, - 'lo': '5p', - 'econfig': '[Kr] 5s2 4d10 4f14| 5p6 5d4 6s2', - }, - 'soc': { - 'theta': 0.0, - 'phi': 0.0 - }, - 'comp': { - 'kmax': 3.5, - 'gmax': 2.9, - }, - 'kpt': { - 'nkpt': 200, - } - }) -#para1.store() -#pprint(para1.get_dict()) - -for id in ids: - s = load_node(id) - new_s, para = bs(s, atoms=[], site=[0, 1], pos=[(0.0, 0.0, 0, 0)], parameterData=para1) - #print new_s.sites - #pprint(para.get_dict()) - res = create_corehole(new_s, kind, econfig, para) - #print res - #pprint(para.get_dict()) - #pprint(res.get_dict()) - -# test create_corehole_fleurinp -#fleurinp = load_node(14039) # W film - -inpxmlfile1 = '../inp_xml_files/W/inp.xml' -inpxmlfile = os.path.abspath(inpxmlfile1) -fleurinp = FleurinpData(files=[inpxmlfile]) -species = 'W-1' -stateocc = {'(5d3/2)': (2.5, 0.0), '(4f7/2)': (3.5, 4.0)} -pos = [] -coreconfig = 'same' -valenceconfig = 'same' -#pprint(fleurinp.inp_dict) - -new_inp = create_corehole_fleurinp(fleurinp, species, stateocc) -print(new_inp) - -etree = '' -change = [(1, 2)] -res = write_change(etree, change) -#res.write('.outtree') -print(res) diff --git a/tests/old_todo/util/btest_read_cif_folder.py b/tests/old_todo/util/btest_read_cif_folder.py deleted file mode 100644 index de9c75e59..000000000 --- a/tests/old_todo/util/btest_read_cif_folder.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -'''Implement a test for this interface''' -from __future__ import absolute_import -from aiida import load_dbenv, is_dbenv_loaded -if not is_dbenv_loaded(): - load_dbenv() -from aiida_fleur.tools.read_cif_folder import read_cif_folder - -read_cif_folder(log=True, - store=True, - recursive=True, - extras={ - 'type': 'bulk', - 'project': 'Fusion', - 'specification': 'aiida_work', - 'comment': 'Materials for Fusion' - }) diff --git a/tests/tools/test_create_corehole.py b/tests/tools/test_create_corehole.py index f33128211..d1f27c04c 100644 --- a/tests/tools/test_create_corehole.py +++ b/tests/tools/test_create_corehole.py @@ -1,3 +1,134 @@ # -*- 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/ # +############################################################################### +'''Contains tests for create_corehole functions.''' +import pytest +from aiida import orm + # create_corehole_para # test interface of corehole para, that the parameter dict that comes out is right +PARAMETERS2 = { + 'atom1': { + 'element': 'Si', + 'id': 14.1, + 'rmt': 2.1, + 'jri': 981, + 'lmax': 12, + 'lnonsph': 6 + }, #'econfig': '[He] 2s2 2p6 | 3s2 3p2', 'lo': ''}, + 'atom2': { + 'element': 'Si', + 'z': 14, + 'id': 14.2, + 'rmt': 2.1, + 'jri': 981, + 'lmax': 12, + 'lnonsph': 6 + }, + 'comp': { + 'kmax': 5.0, + } +} + + +def test_create_corehole_para(generate_structure, data_regression): + """Test if the create corehole para function has thr right interface and returns + the correct things + """ + from aiida_fleur.tools.create_corehole import create_corehole_para + from aiida_fleur.tools.StructureData_util import break_symmetry + + dict2 = orm.Dict(dict=PARAMETERS2) + struc = generate_structure() #Si + struc2, para_new = break_symmetry(struc, parameterdata=dict2) + parameters1 = create_corehole_para(struc2, kind='Si1', econfig='[He] 2s1 2p6 | 3s2 3p3', parameterdata=para_new) + assert isinstance(parameters1, orm.Dict) + + # with no parameterdata to modify + parameters2 = create_corehole_para(struc2, kind='Si1', econfig='[He] 2s1 2p6 | 3s2 3p3') + assert isinstance(parameters2, orm.Dict) + + data_regression.check({ + 'parameters1': parameters1.get_dict(), + 'parameters2': parameters2.get_dict(), + }) + + +''' +# from old, test idea for create_corehole_fleurinp +ids = [] #13924]#, 13925]#, 13926, 13927, 13928, 13929, 13930, 13931, 13932, 13933, 13934, 13935] +#ids = [479, 480, 481, 482, 537]# O12W4, O12W4, O6W2, O6W2, O36W3Y18 + +kind = 'W1' +econfig = '[Kr] 5s2 4d10 4f13 | 5p6 5d5 6s2' +para1 = Dict( + dict={ + 'title': 'A test calculation of Tungsten', + 'input': { + 'film': False, + 'cartesian': True, + }, + 'atom': { + 'element': 'W', + 'jri': 833, + 'rmt': 2.3, + 'dx': 0.015, + 'lmax': 8, + 'lo': '5p', + 'econfig': '[Kr] 5s2 4d10 4f14| 5p6 5d4 6s2', + }, + 'soc': { + 'theta': 0.0, + 'phi': 0.0 + }, + 'comp': { + 'kmax': 3.5, + 'gmax': 2.9, + }, + 'kpt': { + 'nkpt': 200, + } + }) +#para1.store() +#pprint(para1.get_dict()) + +for id in ids: + s = load_node(id) + new_s, para = bs(s, atoms=[], site=[0, 1], pos=[(0.0, 0.0, 0, 0)], parameterData=para1) + #print new_s.sites + #pprint(para.get_dict()) + res = create_corehole(new_s, kind, econfig, para) + #print res + #pprint(para.get_dict()) + #pprint(res.get_dict()) + +# test create_corehole_fleurinp +#fleurinp = load_node(14039) # W film + +inpxmlfile1 = '../inp_xml_files/W/inp.xml' +inpxmlfile = os.path.abspath(inpxmlfile1) +fleurinp = FleurinpData(files=[inpxmlfile]) +species = 'W-1' +stateocc = {'(5d3/2)': (2.5, 0.0), '(4f7/2)': (3.5, 4.0)} +pos = [] +coreconfig = 'same' +valenceconfig = 'same' +#pprint(fleurinp.inp_dict) + +new_inp = create_corehole_fleurinp(fleurinp, species, stateocc) +print(new_inp) + +etree = '' +change = [(1, 2)] +res = write_change(etree, change) +#res.write('.outtree') +print(res) +''' diff --git a/tests/tools/test_create_corehole/test_create_corehole_para.yml b/tests/tools/test_create_corehole/test_create_corehole_para.yml new file mode 100644 index 000000000..f4f937d1c --- /dev/null +++ b/tests/tools/test_create_corehole/test_create_corehole_para.yml @@ -0,0 +1,24 @@ +parameters1: + atom1: + element: Si + id: '16.1' + jri: 981 + lmax: 12 + lnonsph: 6 + rmt: 2.1 + atom2: + element: Si + id: '16.2' + jri: 981 + lmax: 12 + lnonsph: 6 + rmt: 2.1 + z: 16 + comp: + kmax: 5.0 +parameters2: + atom: + econfig: '[He] 2s1 2p6 | 3s2 3p3' + element: Si + id: 14.1 + name: corehole diff --git a/tests/tools/test_io_routines.py b/tests/tools/test_io_routines.py index a32527cd8..2cc7cef7c 100644 --- a/tests/tools/test_io_routines.py +++ b/tests/tools/test_io_routines.py @@ -60,6 +60,8 @@ def test_compress_fleuroutxml(): testfilepath = abspath('./files/outxml/BeTi_out.xml') dest_path = testfilepath.replace('.xml', '_test.xml') + testfilepath_broken = abspath('./files/outxml/special/broken_first_BeTi_out.xml') + dest_path2 = testfilepath_broken.replace('.xml', '_test.xml') niter_file = 19 xpath_iter = '/fleurOutput/scfLoop/iteration' xpath_eig = '/fleurOutput/scfLoop/iteration/eigenvalues' @@ -94,5 +96,9 @@ def get_npath(filepath, xpath): assert niter3 == niter_file # check if no iteration deleted + # test if broken file will not generate an error + compress_fleuroutxml(testfilepath_broken, dest_file_path=dest_path2, iterations_to_keep=25) + # cleanup remove(dest_path) + remove(dest_path2) diff --git a/tests/tools/test_read_cif_folder.py b/tests/tools/test_read_cif_folder.py index 2c06b2bf2..d1143851f 100644 --- a/tests/tools/test_read_cif_folder.py +++ b/tests/tools/test_read_cif_folder.py @@ -1,13 +1,11 @@ # -*- coding: utf-8 -*- '''Contains tests for read and with work cif file routines.''' -from __future__ import absolute_import -from __future__ import print_function import pytest # read-cif_folder -@pytest.mark.skip(reason='Test not implemented') +#@pytest.mark.skip(reason='Test not implemented') def test_read_cif_folder_interface(temp_dir): """ this test set reads in the cif files in the ../files/cif/ directory and subdirs @@ -17,32 +15,46 @@ def test_read_cif_folder_interface(temp_dir): import os from aiida_fleur.tools.read_cif_folder import read_cif_folder import aiida_fleur + from aiida import orm path = os.path.dirname(aiida_fleur.__file__) - cif_folderpath = os.path.join(path, 'tests/files/cif/') + cif_folderpath = os.path.join(path, '../tests/files/cif/') out_filename = os.path.join(temp_dir, 'out.txt') - + add_extra = {'test': 1} #read_in - structure_data, filenames = read_cif_folder(path=os.getcwd(), + structure_data, filenames = read_cif_folder(path=cif_folderpath, recursive=True, store=True, log=True, comments='Test_comment', - extras={'test': 1}, + extras=add_extra, logfile_name=out_filename) - structure_data, filenames = read_cif_folder(path=cif_folderpath, - recursive=False, - store=False, - log=False, - comments='', - extras='') #test number of structurs written #test number of cif files written #test if extras are set right #test prov #test + for structure in structure_data: + assert isinstance(structure, orm.StructureData) + assert structure.is_stored + assert structure.extras['test'] == 1 + + assert len(structure_data) == len(filenames) #read_in again - # test if cif files are not rewritten. - assert False + structure_data, filenames = read_cif_folder(path=cif_folderpath, + recursive=False, + store=False, + log=True, + comments='', + extras='myproject', + logfile_name=out_filename) + for structure in structure_data: + assert isinstance(structure, orm.StructureData) + assert not structure.is_stored + assert 'test' not in structure.extras + #assert structure.extras['specification'] == 'myproject' + assert 'specification' not in structure.extras + # extras get only written if structures are stored + assert len(structure_data) == len(filenames) From 024038a00dd1ee7a495b23be041a811971ee32a2 Mon Sep 17 00:00:00 2001 From: broeder-j Date: Mon, 14 Dec 2020 09:52:30 +0100 Subject: [PATCH 43/53] Fleurinputgen allows position names longer then 4 chars now. --- aiida_fleur/calculation/fleurinputgen.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/aiida_fleur/calculation/fleurinputgen.py b/aiida_fleur/calculation/fleurinputgen.py index 62102006b..cea3ed8c9 100644 --- a/aiida_fleur/calculation/fleurinputgen.py +++ b/aiida_fleur/calculation/fleurinputgen.py @@ -261,7 +261,8 @@ def prepare_for_submission(self, folder): if self._use_aiida_structure: input_params.pop('lattice', {}) own_lattice = False - + #TODO check if input parameter dict is consistent to given structure. + # if not issue warnings. # TODO allow only usual kpt meshes and use therefore Aiida kpointData # if self._use_kpoints: # try: @@ -355,19 +356,21 @@ def prepare_for_submission(self, folder): vector_rel = abs_to_rel_f(pos, cell, structure.pbc) vector_rel[2] = vector_rel[2] * scaling_pos - if site_symbol != kind_name: # This is an important fact, if user renames it becomes a new specie! + if site_symbol != kind_name: # This is an important fact, if user renames it becomes a new atomtype or species! try: # Kind names can be more then numbers now, this might need to be reworked head = kind_name.rstrip('0123456789') kind_namet = int(kind_name[len(head):]) - if int(kind_name[len(head)]) > 4: - raise InputValidationError('New specie name/label should start with a digit smaller than 4') + #if int(kind_name[len(head)]) > 4: + # raise InputValidationError('New specie name/label should start with a digit smaller than 4') except ValueError: - pass + self.report( + 'Warning: Kind name {} will be ignored by the FleurinputgenCalculation and not set a charge number.' + .format(kind_name)) else: atomic_number_name = '{}.{}'.format(atomic_number, kind_namet) # append a label to the detached atom - reg_string = ' {0:7} {1:18.' + sf_p + 'f} {2:18.' + sf_p + 'f} {3:18.' + sf_p + 'f} {4}\n' + reg_string = ' {0:7} {1:18.' + sf_p + 'f} {2:18.' + sf_p + 'f} {3:18.' + sf_p + 'f} {}\n' atomic_positions_card_listtmp.append( reg_string.format(atomic_number_name, vector_rel[0], vector_rel[1], vector_rel[2], kind_namet)) else: From 570bca9dd59b064e53bbcc09560799a339056353 Mon Sep 17 00:00:00 2001 From: broeder-j Date: Mon, 14 Dec 2020 09:53:15 +0100 Subject: [PATCH 44/53] SCF workchain now checks on code extras for some default options This is usefull for iffaiida, to not enforce users to give the right queue. It requires a certain layout of the extras of codes. But this feature should not change any possibilities which the scf had before. --- aiida_fleur/workflows/scf.py | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/aiida_fleur/workflows/scf.py b/aiida_fleur/workflows/scf.py index 5d606fe94..71125e248 100644 --- a/aiida_fleur/workflows/scf.py +++ b/aiida_fleur/workflows/scf.py @@ -60,7 +60,7 @@ class FleurScfWorkChain(WorkChain): like Success, last result node, list with convergence behavior """ - _workflowversion = '0.4.2' + _workflowversion = '0.4.3' _default_wf_para = { 'fleur_runmax': 4, 'density_converged': 0.00002, @@ -89,7 +89,7 @@ class FleurScfWorkChain(WorkChain): 'num_mpiprocs_per_machine': 1 }, 'max_wallclock_seconds': 6 * 60 * 60, - 'queue_name': '', + 'queue_name': None, 'custom_scheduler_commands': '', 'import_sys_environment': False, 'environment_variables': {} @@ -155,12 +155,33 @@ def start(self): 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 + if 'inpgen' in self.inputs: + inpgen = self.inputs.inpgen + inpgen_extras = inpgen.extras + + defaultoptions = self._default_options.copy() + 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: + qd = fleur_extras['queue_defaults'] + queue = user_options.get('queue', 'default') + defaults_queue = qd.get(queue, {}) + for key, val in defaultoptions.items(): + defaultoptions[key] = defaults_queue.get(key, val) - defaultoptions = self._default_options if 'options' in self.inputs: - options = self.inputs.options.get_dict() + options = user_options else: options = defaultoptions + # we use the same options for both codes, inpgen resources get overridden + # and queue does not matter in case of direct scheduler # extend options given by user using defaults for key, val in six.iteritems(defaultoptions): From fd057c446399f77b91a9301a78ca8be9ad20637c Mon Sep 17 00:00:00 2001 From: broeder-j Date: Mon, 14 Dec 2020 11:36:11 +0100 Subject: [PATCH 45/53] Refactor break_symmetry so far the structure breaking works The parameter adjustment does not work right so far. Also this commit corrects a small bug introduced by previous commit --- aiida_fleur/tools/StructureData_util.py | 429 +++++++++++++++--- aiida_fleur/tools/create_corehole.py | 1 - aiida_fleur/workflows/scf.py | 2 +- tests/common/__initi__.py | 0 tests/common/test_node_generators.py | 47 ++ tests/tools/test_StructureData_util.py | 271 ++++++++++- .../test_break_symmetry_bulk.yml | 33 ++ .../test_create_corehole_para.yml | 22 +- 8 files changed, 734 insertions(+), 71 deletions(-) create mode 100644 tests/common/__initi__.py create mode 100644 tests/common/test_node_generators.py create mode 100644 tests/tools/test_StructureData_util/test_break_symmetry_bulk.yml diff --git a/aiida_fleur/tools/StructureData_util.py b/aiida_fleur/tools/StructureData_util.py index 5cda5e1e7..905a431c6 100644 --- a/aiida_fleur/tools/StructureData_util.py +++ b/aiida_fleur/tools/StructureData_util.py @@ -14,12 +14,9 @@ """ # TODO move imports to workfuncitons namespace? -from __future__ import absolute_import -from __future__ import print_function # from ase import * # from ase.lattice.surface import * # from ase.io import * -import six from pymatgen.core.surface import generate_all_slabs #, get_symmetrically_distinct_miller_indices from pymatgen.core.surface import SlabGenerator @@ -351,8 +348,266 @@ def break_symmetry_wf(structure, wf_para, parameterdata=None): return {'new_structure': new_structure, 'new_parameters': para_new} +def break_symmetry(structure, + atoms=None, + site=None, + pos=None, + new_kinds_names=None, + add_atom_base_lists=False, + parameterdata=None): + """ + This routine introduces different 'kind objects' in a structure + and names them that inpgen will make different species/atomgroups out of them. + If nothing specified breaks ALL symmetry (i.e. every atom gets their own kind) + + :param structure: StructureData + :param atoms: python list of symbols, exp: ['W', 'Be']. This would make for + all Be and W atoms their own kinds. + :param site: python list of integers, exp: [1, 4, 8]. This would create for + atom 1, 4 and 8 their own kinds. + :param pos: python list of tuples of 3, exp [(0.0, 0.0, -1.837927), ...]. + This will create a new kind for the atom at that position. + Be carefull the number given has to match EXACTLY the position + in the structure. + :param parameterdata: Dict node, containing calculation_parameters, however, + this only works well if you prepare already a node for containing + the atom lists from the symmetry breaking, or lists without ids. + + :return: StructureData, a AiiDA crystal structure with new kind specification. + :return: DictData, a AiiDA dict with new parameters for inpgen. + """ + if atoms is None: + atoms = ['all'] + + if site is None: + site = [] + + if pos is None: + pos = [] + + if new_kinds_names is None: + new_kinds_names = {} + + from aiida.common.constants import elements as PeriodicTableElements + from aiida.orm import Dict + + _atomic_numbers = {data['symbol']: num for num, data in PeriodicTableElements.items()} + + # get all atoms, get the symbol of the atom + # if wanted make individual kind for that atom + # kind names will be atomsymbol+number + # create new structure with new kinds and atoms + symbol_count = {} # Counts the atom symbol occurrence to set id's and kind names right + replace = [] # all atoms symbols ('W') to be replaced + replace_siteN = [] # all site integers to be replaced + replace_pos = [] # all the atom positions to be replaced + para_new = None + kind_name_id_mapping = {} + + struc = is_structure(structure) + if not struc: + print('Error, no structure given') + # throw error? + return None, None + + cell = struc.cell + pbc = struc.pbc + sites = struc.sites + new_structure = DataFactory('structure')(cell=cell, pbc=pbc) + + for sym in atoms: + replace.append(sym) + for position in pos: + replace_pos.append(position) + for atom in site: + replace_siteN.append(atom) + + for i, site_c in enumerate(sites): + # get site info + kind_name = site_c.kind_name + pos = site_c.position + kind = struc.get_kind(kind_name) + symbol = kind.symbol + replace_kind = False + + # check if kind to replace is in inputs + if symbol in replace or 'all' in replace: + replace_kind = True + if pos in replace_pos: + replace_kind = True + if i in replace_siteN: + replace_kind = True + + if replace_kind: + symbol_count[symbol] = symbol_count.get(symbol, 0) + 1 + symbol_new_kinds_names = new_kinds_names.get(symbol, []) + if symbol_new_kinds_names and ((len(symbol_new_kinds_names)) == symbol_count[symbol]): + newkindname = symbol_new_kinds_names[symbol_count[symbol] - 1] + kind_name_id_mapping[newkindname] = symbol_count[symbol] - 1 + else: + newkindname = '{}{}'.format(symbol, symbol_count[symbol]) + kind_name_id_mapping[newkindname] = symbol_count[symbol] + new_kind = Kind(name=newkindname, symbols=symbol) + new_structure.append_kind(new_kind) + + else: + newkindname = kind_name + if not kind_name in new_structure.get_kind_names(): + new_structure.append_kind(kind) + new_structure.append_site(Site(kind_name=newkindname, position=pos)) + + # update parameter data + if parameterdata is not None: + para_new = adjust_calc_para_to_structure(parameterdata, new_structure) + + new_structure.label = structure.label + new_structure.description = structure.description + 'more kinds, less sym' + + return new_structure, para_new + + +def adjust_calc_para_to_structure(parameter, structure, add_atom_base_lists=True): + """ + Adjust calculation parameters for inpgen to a given structure with several kinds + + Rules: + 1. Only atom lists are changed in the parameter node + 2. If at least one atomlist of a certain element is in parameter + all kinds with this elements will have atomlists in the end + 3. For a certain kind which has no atom list yet and at least one list with such an element + exists it gets the parameters from the atom list with the lowest number (while atom 0: + new_parameterd[atomlistname] = val + ids.remove(el_id) + symbol_possible_ids[symbol] = ids + kind_found = True + break # we assume the user is smart and provides a para node, + # which incorporates the symmetry breaking already + # but we need to see all atom lists to know if it is there... + if kind_found: + continue + for key in sorted(para): + val = para[key] + if 'atom' in key: + if val.get('element', None) != symbol: + if val.get('z', None) != _atomic_numbers.get(symbol): + continue + el_id = str(val.get('id', '0.0')) + el_id = int(el_id.split('.')[1]) + ids = symbol_possible_ids.get(symbol) + # copy parameter of symbol and add id + # this would be the lowest predefined atom list of an element + val_new = {} + val_new.update(val) + charge = _atomic_numbers.get((val.get('element', None))) + if charge is None: + charge = val.get('z', None) + idp = '{}.{}'.format(charge, ids[0]) + ids.remove(el_id) + idp = float('{0:.2f}'.format(float(idp))) + # dot cannot be stored in AiiDA dict... + val_new.update({u'id': idp}) + atomlistname = 'atom{}'.format(i)#id_a) + # Since there are other atoms list also find the next + # free atom key. + #j = 0 + #while new_parameterd.get(atomlistname, {}): + # j = j + 1 + # atomlistname = 'atom{}'.format(id_a + i) + #symbol_new_kinds_names = new_kinds_names.get(symbol, []) + # print(symbol_new_kinds_names) + #if symbol_new_kinds_names and ((len(symbol_new_kinds_names)) == symbol_count[symbol]): + # species_name = symbol_new_kinds_names[symbol_count[symbol] - 1] + # val_new.update({u'name': species_name}) + new_parameterd[atomlistname] = val_new + break # max one new atom list per kind +''' +''' + # add other non atom keys from original parameterdata + for key, val in para.items(): + if 'atom' not in key: + new_parameterd[key] = val + elif add_atom_base_lists: + if not 'id' in val: + new_parameterd[key] = val para_new = Dict(dict=new_parameterd) else: para_new = None + print(new_parameterd) new_structure.label = structure.label new_structure.description = structure.description + 'more kinds, less sym' return new_structure, para_new +''' def find_equi_atoms(structure): # , sitenumber=0, position=None): @@ -872,7 +1177,7 @@ def create_manual_slab_ase(lattice='fcc', *_, layer_occupancies = get_layers(structure) if replacements is not None: - keys = six.viewkeys(replacements) + 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:' ' {}'.format(len(layer_occupancies))) @@ -897,7 +1202,7 @@ def create_manual_slab_ase(lattice='fcc', *_, layer_occupancies = get_layers(structure) layer_occupancies.insert(0, 0) - for i, at_type in six.iteritems(replacements): + for i, at_type in replacements.items(): if isinstance(i, str): i = int(i) if i < 0: diff --git a/aiida_fleur/tools/create_corehole.py b/aiida_fleur/tools/create_corehole.py index ae624efcd..a9a94b347 100644 --- a/aiida_fleur/tools/create_corehole.py +++ b/aiida_fleur/tools/create_corehole.py @@ -26,7 +26,6 @@ def create_corehole_para(structure, kind, econfig, species_name='corehole', para :param structure: StructureData :param kind: a string with the kind_name (TODO: alternative the kind object) :param econfig: string, e.g. econfig = "[Kr] 5s2 4d10 4f13 | 5p6 5d5 6s2" to set, i.e. the corehole - ! This is the new (MaXR2) econfig therefore :return: a Dict node """ diff --git a/aiida_fleur/workflows/scf.py b/aiida_fleur/workflows/scf.py index 71125e248..b8810f45b 100644 --- a/aiida_fleur/workflows/scf.py +++ b/aiida_fleur/workflows/scf.py @@ -89,7 +89,7 @@ class FleurScfWorkChain(WorkChain): 'num_mpiprocs_per_machine': 1 }, 'max_wallclock_seconds': 6 * 60 * 60, - 'queue_name': None, + 'queue_name': '', 'custom_scheduler_commands': '', 'import_sys_environment': False, 'environment_variables': {} diff --git a/tests/common/__initi__.py b/tests/common/__initi__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/common/test_node_generators.py b/tests/common/test_node_generators.py new file mode 100644 index 000000000..cd09fd2cd --- /dev/null +++ b/tests/common/test_node_generators.py @@ -0,0 +1,47 @@ +# -*- 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/ # +############################################################################### +''' +Module to test all common node_generators + + +import pytest +from aiida_fleur.common.node_generators import generate_option_dict, generate_option_node +from aiida_fleur.common.node_generators import generate_wf_para_dict, generate_wf_para_node + +ALL_ENTRYPOINTS_CALCS = ['fleur.fleur', 'fleur.inpgen'] +ALL_ENTRYPOINTS_WC = ['fleur.base', 'fleur.scf', 'fleur.relax', 'fleur.base_relax', 'fleur.eos', + 'fleur.corehole', 'fleur.init_cls', 'fleur.banddos', 'fleur.mae', 'fleur.dmi', 'fleur.ssdisp', + 'fleur.create_magnetic'] + +def test_generate_option_dict_defaults_wc(entrypoint): + """Tests if the generate_option_dict function can get all the default options from the workchains + + i.e this also tests if all fleur workchains have defined some _default_option + """ + default_dict = generate_option_dict(wf_entry_point=entrypoint) + assert isinstance(default_dict, dict) + +def test_generate_option_node_interface(): + """ + + """ + pass + +@pytest.mark.parametrize('entrypoint', ALL_ENTRYPOINTS_WC) +def test_generate_wf_para_dict_defaults_wc(entrypoint): + """Tests if the generate_option_dict function can get all the default options from the workchains + + i.e this also tests if all fleur workchains have defined some _default_option + """ + default_dict = generate_wf_para_dict(wf_entry_point=entrypoint) + assert isinstance(default_dict, dict) +''' diff --git a/tests/tools/test_StructureData_util.py b/tests/tools/test_StructureData_util.py index 7c7f92f9f..f4febcc5b 100644 --- a/tests/tools/test_StructureData_util.py +++ b/tests/tools/test_StructureData_util.py @@ -157,7 +157,7 @@ def test_rel_to_abs_f(generate_film_structure): assert not rel_to_abs_f([1], cell) -def test_break_symmetry_wf(generate_film_structure): +def test_break_symmetry_wf_film_structure_only(generate_film_structure): """Check if it does not crash and able to destroy all symmetries""" from aiida_fleur.tools.StructureData_util import break_symmetry_wf, supercell_ncf from aiida_fleur.tools.StructureData_util import break_symmetry @@ -172,11 +172,70 @@ def test_break_symmetry_wf(generate_film_structure): ) structure_broken = out['new_structure'] kind_names = [x.kind_name for x in structure_broken.sites] + kind_names_should = ['Fe1', 'Fe2', 'Fe3', 'Fe4', 'Pt1', 'Pt2', 'Pt3', 'Pt4', 'Pt5', 'Pt6', 'Pt7', 'Pt8'] + for kind_name in kind_names_should: + assert kind_name in kind_names + assert len(set(kind_names)) == len(kind_names_should) - for kind_name in ['Fe1', 'Fe1', 'Fe1', 'Fe1', 'Pt1', 'Pt2', 'Pt3', 'Pt4', 'Pt5', 'Pt6', 'Pt7', 'Pt8']: + struc_b_fe, para_new_fe = break_symmetry(structure, atoms=['Fe']) + kind_names = [x.kind_name for x in struc_b_fe.sites] + kind_names_should = ['Fe1', 'Fe2', 'Fe3', 'Fe4', 'Pt'] + for kind_name in kind_names_should: assert kind_name in kind_names + assert len(set(kind_names)) == len(kind_names_should) - # Test if break symmetry adjusts the parameter data right. + struc_b_pt, para_new_pt = break_symmetry(structure, atoms=['Pt']) + kind_names = [x.kind_name for x in struc_b_pt.sites] + kind_names_should = ['Fe', 'Pt1', 'Pt2', 'Pt3', 'Pt4', 'Pt5', 'Pt6', 'Pt7', 'Pt8'] + for kind_name in kind_names_should: + assert kind_name in kind_names + assert len(set(kind_names)) == len(kind_names_should) + + struc_b_site, para_new_site = break_symmetry(structure, atoms=[], site=[0, 1]) + kind_names = [x.kind_name for x in struc_b_site.sites] + print(kind_names) + kind_names_should = ['Fe', 'Fe1', 'Fe2', 'Pt'] + for kind_name in kind_names_should: + assert kind_name in kind_names + assert len(set(kind_names)) == len(kind_names_should) + + pos = [structure.sites[0].position, structure.sites[1].position] + + struc_b_pos, para_new_pos = break_symmetry(structure, atoms=[], pos=pos) + kind_names = [x.kind_name for x in struc_b_pos.sites] + print(kind_names) + kind_names_should = ['Fe', 'Fe1', 'Fe2', 'Pt'] + for kind_name in kind_names_should: + assert kind_name in kind_names + assert len(set(kind_names)) == len(kind_names_should) + + +def test_break_symmetry_film_parameters_only(generate_film_structure): + """Test if these break symmetry operation adjusted the parameter data right. + This basicly tests + from aiida_fleur.tools.StructureData_util import adjust_calc_para_to_structure + for a separate test we would have to generate these structures again + """ + from aiida_fleur.tools.StructureData_util import break_symmetry + from aiida.orm import Dict + + structure = generate_film_structure() + + structure_broken, para_out = break_symmetry(structure) + #print(para_out.get_dict()) + struc_b_fe, para_new_fe = break_symmetry(structure, atoms=['Fe']) + struc_b_pt, para_new_pt = break_symmetry(structure, atoms=['Pt']) + struc_b_site, para_new_site = break_symmetry(structure, atoms=[], site=[0, 1]) + pos = [structure.sites[0].position, structure.sites[1].position] + struc_b_pos, para_new_pos = break_symmetry(structure, atoms=[], pos=pos) + + #assert False + + +''' + + + # old should dict should_out_dict = { 'atom': { 'id': 26, @@ -194,10 +253,16 @@ def test_break_symmetry_wf(generate_film_structure): 'bmu': 1 } } + + should_out_dict = {'atom1': {'z': 26, 'rmt': 2.1, 'bmu': -1}, + 'atom2': {'z': 26, 'rmt': 2.1, 'bmu': -1, 'id': '26.1'}, + 'atom3': {'z': 26, 'rmt': 2.1, 'bmu': -1, 'id': '26.2'}, + 'atom4': {'z': 26, 'rmt': 2.1, 'bmu': -1, 'id': '26.3'}, + 'atom5': {'z': 26, 'rmt': 2.1, 'bmu': -1, 'id': '26.4'}} parameter_data = Dict( dict={ 'atom': { - 'id': 26, + 'z': 26, 'rmt': 2.1, 'bmu': -1 }, @@ -214,7 +279,205 @@ def test_break_symmetry_wf(generate_film_structure): }) out, parameterdata_new = break_symmetry(structure, parameterdata=parameter_data) out_dict = parameterdata_new.get_dict() + print(out_dict) assert out_dict == should_out_dict +''' +''' +def test_break_symmetry_bulk(generate_structure): + """Check if it does not crash and able to destroy all symmetries""" + from aiida_fleur.tools.StructureData_util import break_symmetry, supercell_ncf + from aiida.orm import Dict + + structure = generate_structure() + + # Test if break symmetry adjusts parameters right with simple parameters + + parameter_data = Dict( + dict={ + 'atom': { + 'element': 'Si', + 'rmt': 2.1, + 'jri': 981, + 'lmax': 12, + 'lnonsph': 6 + }, + 'comp': { + 'kmax': 5.0, + }}) + structure_broken, parameters1 = break_symmetry(structure, parameterdata=parameter_data) + + print('para1', parameters1.get_dict()) + should_para1 = { + 'atom0': { + 'element': 'Si', + 'id': 14.1, + 'rmt': 2.1, + 'jri': 981, + 'lmax': 12, + 'lnonsph': 6 + }, + 'atom1': { + 'element': 'Si', + 'id': 14.2, + 'rmt': 2.1, + 'jri': 981, + 'lmax': 12, + 'lnonsph': 6 + }, + 'comp': { + 'kmax': 5.0, + }} + assert parameters1.get_dict() == should_para1 + # Now test if it also adjusts for complex parameters right + parameter_data2 = Dict(dict={ + 'atom1': { + 'element': 'Si', + 'id': 14.1, + 'rmt': 2.2, + 'jri': 981, + 'lmax': 12, + 'lnonsph': 6 + }, + 'atom2': { + 'element': 'Si', + 'z' : 14, + 'id': 14.2, + 'rmt': 2.1, + 'jri': 981, + 'lmax': 11, + 'lnonsph': 6 + }, + 'atom': { + 'element': 'Si', + 'rmt': 2.0, + 'jri': 981, + 'lmax': 10, + 'lnonsph': 6 + }, + 'comp': { + 'kmax': 5.0, + } + }) + structure = supercell_ncf(structure, 2, 1, 1) + # Test if break symmetry adjusts the parameter data right. + structure_broken, parameters2 = break_symmetry(structure, parameterdata=parameter_data2) + kind_names = [x.kind_name for x in structure_broken.sites] + for kind_name in ['Si1', 'Si2', 'Si3', 'Si4']: + assert kind_name in kind_names + print('para2', parameters2.get_dict()) + para2_should = { + 'atom1': { + 'element': 'Si', + 'id': 14.1, + 'rmt': 2.2, + 'jri': 981, + 'lmax': 12, + 'lnonsph': 6 + }, + 'atom2': { + 'element': 'Si', + 'z' : 14, + 'id': 14.2, + 'rmt': 2.1, + 'jri': 981, + 'lmax': 11, + 'lnonsph': 6 + }, + 'atom0': { + 'element': 'Si', + 'id': 14.3, + 'rmt': 2.0, + 'jri': 981, + 'lmax': 10, + 'lnonsph': 6 + }, + 'atom3': { + 'element': 'Si', + 'id': 14.4, + 'rmt': 2.0, + 'jri': 981, + 'lmax': 10, + 'lnonsph': 6 + }, + 'comp': { + 'kmax': 5.0, + } + } + + assert para2_should == parameters2.get_dict() + #TODO test break_symmetry with several different elements in parameter and structure +''' + + +def test_adjust_calc_para_to_structure(generate_structure): + """Test intergace of check_structure_para_consistent""" + from aiida_fleur.tools.StructureData_util import adjust_calc_para_to_structure + from aiida_fleur.tools.StructureData_util import break_symmetry + from aiida import orm + + structure = generate_structure() + + parameter_data = orm.Dict(dict={ + 'atom1': { + 'element': 'Si', + 'rmt': 2.1, + 'jri': 981, + 'lmax': 12, + 'lnonsph': 6 + }, + 'comp': { + 'kmax': 5.0, + } + }) + new_para = adjust_calc_para_to_structure(parameter_data, structure) + # The parameter data should not be changed + assert new_para.get_dict() == parameter_data.get_dict() + + structure_broken, parameters1 = break_symmetry(structure, parameterdata=parameter_data) + new_para = adjust_calc_para_to_structure(parameter_data, structure_broken) + # The parameter data should be changed and should be the same. + assert new_para.get_dict() == parameters1.get_dict() + + +def test_check_structure_para_consistent(generate_structure): + """Test intergace of check_structure_para_consistent""" + from aiida_fleur.tools.StructureData_util import check_structure_para_consistent + from aiida_fleur.tools.StructureData_util import break_symmetry + from aiida import orm + + structure = generate_structure() + + parameter_data = orm.Dict(dict={ + 'atom': { + 'element': 'Si', + 'rmt': 2.1, + 'jri': 981, + 'lmax': 12, + 'lnonsph': 6 + }, + 'comp': { + 'kmax': 5.0, + } + }) + assert check_structure_para_consistent(parameter_data, structure) + + structure_broken, parameters1 = break_symmetry(structure, parameterdata=parameter_data) + assert check_structure_para_consistent(parameters1, structure_broken) + assert check_structure_para_consistent(parameter_data, structure_broken) + + wrong_parameter_data = orm.Dict(dict={ + 'atom': { + 'element': 'P', + 'rmt': 2.1, + 'jri': 981, + 'lmax': 12, + 'lnonsph': 6 + }, + 'comp': { + 'kmax': 5.0, + } + }) + assert not check_structure_para_consistent(wrong_parameter_data, structure) def test_find_equi_atoms(generate_film_structure): diff --git a/tests/tools/test_StructureData_util/test_break_symmetry_bulk.yml b/tests/tools/test_StructureData_util/test_break_symmetry_bulk.yml new file mode 100644 index 000000000..345341d53 --- /dev/null +++ b/tests/tools/test_StructureData_util/test_break_symmetry_bulk.yml @@ -0,0 +1,33 @@ +parameters1: + atom: + element: Si + jri: 981 + lmax: 12 + lnonsph: 6 + rmt: 2.1 + comp: + kmax: 5.0 +parameters2: + atom: + element: Si + jri: 981 + lmax: 10 + lnonsph: 6 + rmt: 2.0 + atom1: + element: Si + id: 16.1 + jri: 981 + lmax: 12 + lnonsph: 6 + rmt: 2.2 + atom2: + element: Si + id: 16.2 + jri: 981 + lmax: 11 + lnonsph: 6 + rmt: 2.1 + z: 16 + comp: + kmax: 5.0 diff --git a/tests/tools/test_create_corehole/test_create_corehole_para.yml b/tests/tools/test_create_corehole/test_create_corehole_para.yml index f4f937d1c..44a4aa10a 100644 --- a/tests/tools/test_create_corehole/test_create_corehole_para.yml +++ b/tests/tools/test_create_corehole/test_create_corehole_para.yml @@ -1,19 +1,35 @@ parameters1: atom1: + econfig: '[He] 2s1 2p6 | 3s2 3p3' element: Si - id: '16.1' + id: '14.1' jri: 981 lmax: 12 lnonsph: 6 rmt: 2.1 atom2: element: Si - id: '16.2' + id: '14.1' + jri: 981 + lmax: 12 + lnonsph: 6 + rmt: 2.1 + z: 14 + atom3: + element: Si + id: '14.2' + jri: 981 + lmax: 12 + lnonsph: 6 + rmt: 2.1 + atom4: + element: Si + id: '14.2' jri: 981 lmax: 12 lnonsph: 6 rmt: 2.1 - z: 16 + z: 14 comp: kmax: 5.0 parameters2: From 3cf6ffa6b327838d22cea9c3f9d212191467a20f Mon Sep 17 00:00:00 2001 From: broeder-j Date: Mon, 14 Dec 2020 13:03:24 +0100 Subject: [PATCH 46/53] Add break symmetry tests for parameterdata changes I fear that this is not yet at the state we need it. The simple and most use cases work. Also I am not sure if fleur MaXR5 will break this. --- aiida_fleur/tools/StructureData_util.py | 11 +- tests/tools/test_StructureData_util.py | 190 ++++++++++++++++++++++-- 2 files changed, 186 insertions(+), 15 deletions(-) diff --git a/aiida_fleur/tools/StructureData_util.py b/aiida_fleur/tools/StructureData_util.py index 905a431c6..fb65a31f2 100644 --- a/aiida_fleur/tools/StructureData_util.py +++ b/aiida_fleur/tools/StructureData_util.py @@ -353,7 +353,7 @@ def break_symmetry(structure, site=None, pos=None, new_kinds_names=None, - add_atom_base_lists=False, + add_atom_base_lists=True, parameterdata=None): """ This routine introduces different 'kind objects' in a structure @@ -372,7 +372,7 @@ def break_symmetry(structure, :param parameterdata: Dict node, containing calculation_parameters, however, this only works well if you prepare already a node for containing the atom lists from the symmetry breaking, or lists without ids. - + :param add_atom_base_lists: Bool (default True), if the atom base lists should be added or not :return: StructureData, a AiiDA crystal structure with new kind specification. :return: DictData, a AiiDA dict with new parameters for inpgen. """ @@ -458,7 +458,10 @@ def break_symmetry(structure, # update parameter data if parameterdata is not None: - para_new = adjust_calc_para_to_structure(parameterdata, new_structure) + # TODO This may not enough, since for magnetic systems one need a kind mapping + # i.e if the parameters are for a partly 'pre symmetry broken system' + # and we want to keep track from which 'old' kind which new kind spawn + para_new = adjust_calc_para_to_structure(parameterdata, new_structure, add_atom_base_lists=add_atom_base_lists) new_structure.label = structure.label new_structure.description = structure.description + 'more kinds, less sym' @@ -480,7 +483,7 @@ def adjust_calc_para_to_structure(parameter, structure, add_atom_base_lists=True :param parameter: aiida.orm.Dict node containing calc parameters :param structure: aiida.orm.StructureData node containing a crystal structure - + :param add_atom_base_lists: Bool (default True), if the atom base lists should be added or not :return: new aiida.orm.Dict with new calc_parameters """ from aiida.common.constants import elements as PeriodicTableElements diff --git a/tests/tools/test_StructureData_util.py b/tests/tools/test_StructureData_util.py index f4febcc5b..21c39bc8b 100644 --- a/tests/tools/test_StructureData_util.py +++ b/tests/tools/test_StructureData_util.py @@ -35,6 +35,7 @@ def test_is_structure(generate_structure): def test_is_primitive(generate_structure): + """Test if is_primitive test can distinguish between a primitive and non primitive structure""" from aiida_fleur.tools.StructureData_util import is_primitive structure = generate_structure() structure.store() @@ -52,6 +53,7 @@ def test_is_primitive(generate_structure): def test_rescale_nowf(generate_structure): + """Test to rescale some structure """ from aiida_fleur.tools.StructureData_util import rescale_nowf from aiida_fleur.tools.StructureData_util import rescale from aiida.orm import Dict, Float @@ -83,6 +85,7 @@ def test_rescale_nowf(generate_structure): def test_supercell(generate_structure): + """Test to create a super cell""" from aiida_fleur.tools.StructureData_util import supercell from aiida_fleur.tools.StructureData_util import supercell_ncf from aiida.orm import Int @@ -193,7 +196,6 @@ def test_break_symmetry_wf_film_structure_only(generate_film_structure): struc_b_site, para_new_site = break_symmetry(structure, atoms=[], site=[0, 1]) kind_names = [x.kind_name for x in struc_b_site.sites] - print(kind_names) kind_names_should = ['Fe', 'Fe1', 'Fe2', 'Pt'] for kind_name in kind_names_should: assert kind_name in kind_names @@ -203,14 +205,13 @@ def test_break_symmetry_wf_film_structure_only(generate_film_structure): struc_b_pos, para_new_pos = break_symmetry(structure, atoms=[], pos=pos) kind_names = [x.kind_name for x in struc_b_pos.sites] - print(kind_names) kind_names_should = ['Fe', 'Fe1', 'Fe2', 'Pt'] for kind_name in kind_names_should: assert kind_name in kind_names assert len(set(kind_names)) == len(kind_names_should) -def test_break_symmetry_film_parameters_only(generate_film_structure): +def test_break_symmetry_film_parameters_only_simple(generate_film_structure): """Test if these break symmetry operation adjusted the parameter data right. This basicly tests from aiida_fleur.tools.StructureData_util import adjust_calc_para_to_structure @@ -220,16 +221,183 @@ def test_break_symmetry_film_parameters_only(generate_film_structure): from aiida.orm import Dict structure = generate_film_structure() + para = Dict( + dict={ + 'atom': { + 'element': 'Fe', + 'z': 26, + 'rmt': 2.1, + 'bmu': -1 + }, + 'atom1': { + 'element': 'Pt', + 'rmt': 2.2, + 'bmu': 1 + }, + 'comp': { + 'kmax': 5.0, + } + }) - structure_broken, para_out = break_symmetry(structure) - #print(para_out.get_dict()) - struc_b_fe, para_new_fe = break_symmetry(structure, atoms=['Fe']) - struc_b_pt, para_new_pt = break_symmetry(structure, atoms=['Pt']) - struc_b_site, para_new_site = break_symmetry(structure, atoms=[], site=[0, 1]) - pos = [structure.sites[0].position, structure.sites[1].position] - struc_b_pos, para_new_pos = break_symmetry(structure, atoms=[], pos=pos) + structure_broken, para_out = break_symmetry(structure, parameterdata=para) + should1 = { + 'atom1': { + 'element': 'Fe', + 'z': 26, + 'rmt': 2.1, + 'bmu': -1 + }, + 'atom2': { + 'element': 'Pt', + 'rmt': 2.2, + 'bmu': 1 + }, + 'comp': { + 'kmax': 5.0 + }, + 'atom3': { + 'element': 'Fe', + 'z': 26, + 'rmt': 2.1, + 'bmu': -1, + 'id': '26.1' + }, + 'atom4': { + 'element': 'Pt', + 'rmt': 2.2, + 'bmu': 1, + 'id': '78.1' + }, + 'atom5': { + 'element': 'Pt', + 'rmt': 2.2, + 'bmu': 1, + 'id': '78.2' + } + } + assert para_out.get_dict() == should1 + + # breaking again should not change something + structure_broken, para_out = break_symmetry(structure_broken, parameterdata=para_out) + assert para_out.get_dict() == should1 + + should2 = { + 'comp': { + 'kmax': 5.0 + }, + 'atom1': { + 'element': 'Fe', + 'z': 26, + 'rmt': 2.1, + 'bmu': -1, + 'id': '26.1' + }, + 'atom2': { + 'element': 'Pt', + 'rmt': 2.2, + 'bmu': 1, + 'id': '78.1' + }, + 'atom3': { + 'element': 'Pt', + 'rmt': 2.2, + 'bmu': 1, + 'id': '78.2' + } + } + structure_broken, para_out = break_symmetry(structure_broken, parameterdata=para_out, add_atom_base_lists=False) + print(para_out.get_dict()) + assert para_out.get_dict() == should2 + + struc_b_fe, para_new_fe = break_symmetry(structure, atoms=['Fe'], parameterdata=para) + + should3 = { + 'atom1': { + 'element': 'Fe', + 'z': 26, + 'rmt': 2.1, + 'bmu': -1 + }, + 'atom2': { + 'element': 'Pt', + 'rmt': 2.2, + 'bmu': 1 + }, + 'comp': { + 'kmax': 5.0 + }, + 'atom3': { + 'element': 'Fe', + 'z': 26, + 'rmt': 2.1, + 'bmu': -1, + 'id': '26.1' + } + } + assert para_new_fe.get_dict() == should3 + + +def test_break_symmetry_film_parameters_only_complex(generate_film_structure): + """Test if these break symmetry operation adjusted the complex parameter data right. + This basicly tests + from aiida_fleur.tools.StructureData_util import adjust_calc_para_to_structure + for a separate test we would have to generate these structures again + """ + from aiida_fleur.tools.StructureData_util import break_symmetry + from aiida.orm import Dict + + structure = generate_film_structure() + para = Dict( + dict={ + 'atom': { + 'element': 'Fe', + 'id': 26.1, + 'rmt': 2.1, + 'bmu': -1 + }, + 'atom1': { + 'element': 'Pt', + 'id': 78.1, + 'rmt': 2.2, + 'bmu': 1 + }, + 'comp': { + 'kmax': 5.0, + } + }) + + structure_broken, para_out = break_symmetry(structure, parameterdata=para) + struc_b_fe, para_new_fe = break_symmetry(structure, atoms=['Fe'], parameterdata=para) + + should1 = { + 'atom1': { + 'element': 'Fe', + 'id': '26.1', + 'rmt': 2.1, + 'bmu': -1 + }, + 'atom2': { + 'element': 'Pt', + 'id': '78.1', + 'rmt': 2.2, + 'bmu': 1 + }, + 'atom3': { + 'element': 'Pt', + 'id': '78.2', + 'rmt': 2.2, + 'bmu': 1 + }, + 'comp': { + 'kmax': 5.0, + } + } + + assert para_out.get_dict() == should1 - #assert False + should2 = {'atom1': {'bmu': -1, 'element': 'Fe', 'id': '26.1', 'rmt': 2.1}, 'comp': {'kmax': 5.0}} + assert para_new_fe.get_dict() == should2 + # Deletes the other Ids because Pt had an id ''' From 2e29eb5119f346f47c532aefebed809d2fc35d57 Mon Sep 17 00:00:00 2001 From: broeder-j Date: Mon, 14 Dec 2020 13:27:57 +0100 Subject: [PATCH 47/53] Prepare Changelog for release --- CHANGELOG.md | 15 ++++++++++++--- setup.json | 4 ++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7e08b438..0f44c7d3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,19 @@ - ## v1.1.3 ### release compatible with AiiDA-core 1.3.0 +- still support of Fleur MaXR4 version with inpgen +- Does not support yet for Fleur MaXR5 and new inpgen +- Set_kpoints was moved from fleurinp to fleurinpmodifier +- Break_symmetry of a structure was refactored +- Implemented feature in fleurinputCalculation to set significant figures +- Implemented feature scf can now use default queues specified in code extras +- First implementation of relax type None, which cases the relax workchain to skip the +relaxation, becoming a usual scf wc, which might make it easier to switch relaxation on +and off in other workchains. +- Fleur parser parses now the total magnetic moment of the cell - Introduced common constants, for bohr and htr, increased precision - Command line interface (CLI) `aiida-fleur` with various functionalities exposed -- For devs: Increased test coverage - +- For devs: Increased test coverage, codecov is now added to CI and linked to badge +removed some older outdated code ## v1.1.2 ### release compatible with AiiDA-core 1.3.0 diff --git a/setup.json b/setup.json index b92d1529b..8f9ef0231 100644 --- a/setup.json +++ b/setup.json @@ -50,8 +50,8 @@ ], "pre-commit": [ "pre-commit>=2.6.0", - "yapf==0.30.0", - "pylint==2.5.2" + "yapf>=0.30.0", + "pylint>=2.5.2" ], "testing" : [ "pytest>=2.9", From 59b2ed8f9d8f6c95857254b11ae1fca881467ff7 Mon Sep 17 00:00:00 2001 From: broeder-j Date: Mon, 14 Dec 2020 15:47:18 +0100 Subject: [PATCH 48/53] Per default do not extract charge from fleurinp, because inpgen will not set the atom parameters if this is specified. Also make pylint dependency more strict again, newer pylint version have different errors --- aiida_fleur/tools/xml_util.py | 12 ++++++++---- setup.json | 4 ++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/aiida_fleur/tools/xml_util.py b/aiida_fleur/tools/xml_util.py index ad26089b2..7325303fc 100644 --- a/aiida_fleur/tools/xml_util.py +++ b/aiida_fleur/tools/xml_util.py @@ -554,12 +554,14 @@ def get_inpgen_paranode_from_xml(inpxmlfile): return Dict(dict=para_dict) -def get_inpgen_para_from_xml(inpxmlfile): +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) @@ -661,19 +663,21 @@ def get_inpgen_para_from_xml(inpxmlfile): atom_element = eval_xpath(species, atom_element_xpath) atom_name_2 = eval_xpath(species, atom_name_xpath) - atom_dict = set_dict_or_not(atom_dict, 'z', atom_z) + 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, 'ncst', atom_ncst) + 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)) - #atom_dict = set_dict_or_not(atom_dict, 'name', atom_name_2) new_parameters[atoms_name] = atom_dict diff --git a/setup.json b/setup.json index 8f9ef0231..6921007bf 100644 --- a/setup.json +++ b/setup.json @@ -50,8 +50,8 @@ ], "pre-commit": [ "pre-commit>=2.6.0", - "yapf>=0.30.0", - "pylint>=2.5.2" + "yapf~=0.30", + "pylint~=2.5" ], "testing" : [ "pytest>=2.9", From e7678b8ab289126eab8f3aa78e2c437056dd617d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Br=C3=B6der?= Date: Mon, 14 Dec 2020 15:50:46 +0100 Subject: [PATCH 49/53] add bash script for manual regression tests with base export --- .../JUDFT_WARN_ONLY | 1 + .../_scheduler-stderr.txt | 0 .../_scheduler-stdout.txt | 0 .../inp.xml | 368 ++++++++++++++++++ .../out.error | 1 + .../shell.out | 0 .../base_export_regression_tests.tar.gz | Bin 0 -> 87920 bytes tests/workflows_regression_manually.sh | 29 ++ 8 files changed, 399 insertions(+) create mode 100644 tests/calculation/data_dir/mock-fleur-88edcca7024f2f2c75ac80044aa80644/JUDFT_WARN_ONLY create mode 100644 tests/calculation/data_dir/mock-fleur-88edcca7024f2f2c75ac80044aa80644/_scheduler-stderr.txt create mode 100644 tests/calculation/data_dir/mock-fleur-88edcca7024f2f2c75ac80044aa80644/_scheduler-stdout.txt create mode 100644 tests/calculation/data_dir/mock-fleur-88edcca7024f2f2c75ac80044aa80644/inp.xml create mode 100644 tests/calculation/data_dir/mock-fleur-88edcca7024f2f2c75ac80044aa80644/out.error create mode 100644 tests/calculation/data_dir/mock-fleur-88edcca7024f2f2c75ac80044aa80644/shell.out create mode 100644 tests/files/exports/base_export_regression_tests.tar.gz create mode 100644 tests/workflows_regression_manually.sh diff --git a/tests/calculation/data_dir/mock-fleur-88edcca7024f2f2c75ac80044aa80644/JUDFT_WARN_ONLY b/tests/calculation/data_dir/mock-fleur-88edcca7024f2f2c75ac80044aa80644/JUDFT_WARN_ONLY new file mode 100644 index 000000000..7a019b2b4 --- /dev/null +++ b/tests/calculation/data_dir/mock-fleur-88edcca7024f2f2c75ac80044aa80644/JUDFT_WARN_ONLY @@ -0,0 +1 @@ +/n \ No newline at end of file diff --git a/tests/calculation/data_dir/mock-fleur-88edcca7024f2f2c75ac80044aa80644/_scheduler-stderr.txt b/tests/calculation/data_dir/mock-fleur-88edcca7024f2f2c75ac80044aa80644/_scheduler-stderr.txt new file mode 100644 index 000000000..e69de29bb diff --git a/tests/calculation/data_dir/mock-fleur-88edcca7024f2f2c75ac80044aa80644/_scheduler-stdout.txt b/tests/calculation/data_dir/mock-fleur-88edcca7024f2f2c75ac80044aa80644/_scheduler-stdout.txt new file mode 100644 index 000000000..e69de29bb diff --git a/tests/calculation/data_dir/mock-fleur-88edcca7024f2f2c75ac80044aa80644/inp.xml b/tests/calculation/data_dir/mock-fleur-88edcca7024f2f2c75ac80044aa80644/inp.xml new file mode 100644 index 000000000..4d34c362a --- /dev/null +++ b/tests/calculation/data_dir/mock-fleur-88edcca7024f2f2c75ac80044aa80644/inp.xml @@ -0,0 +1,368 @@ + + + + Si, alpha silicon, bulk, delta project + + + + + + + + + + .0000000000 .0000000000 .0000000000 + + + + + + + 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 + + + + + + + + + + + 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 + + + + + .000000000000000 5.167355275200000 5.167355275200000 + 5.167355275200000 .000000000000000 5.167355275200000 + 5.167355275200000 5.167355275200000 .000000000000000 + + + + + + + + + + + + + + + 1.000/8.000 1.000/8.000 1.000/8.000 + -1.000/8.000 -1.000/8.000 -1.000/8.000 + + + + + + + + + + + + + + + + + diff --git a/tests/calculation/data_dir/mock-fleur-88edcca7024f2f2c75ac80044aa80644/out.error b/tests/calculation/data_dir/mock-fleur-88edcca7024f2f2c75ac80044aa80644/out.error new file mode 100644 index 000000000..16cc26986 --- /dev/null +++ b/tests/calculation/data_dir/mock-fleur-88edcca7024f2f2c75ac80044aa80644/out.error @@ -0,0 +1 @@ +_aiidasubmit.sh: line 7: ./local_exe/fleur: No such file or directory diff --git a/tests/calculation/data_dir/mock-fleur-88edcca7024f2f2c75ac80044aa80644/shell.out b/tests/calculation/data_dir/mock-fleur-88edcca7024f2f2c75ac80044aa80644/shell.out new file mode 100644 index 000000000..e69de29bb diff --git a/tests/files/exports/base_export_regression_tests.tar.gz b/tests/files/exports/base_export_regression_tests.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..93e26be5375d7337aff36ad5b835137ad46a196a GIT binary patch literal 87920 zcmbT6V{j!=8?9s8=7}}2?TPJ7bYg2_+qTU!v6G2y+qNf|BzL~=qVC^&tGc?ncE79k zTD`mb$GacWXgD~tr>-9mUna&bz#l*lM+axuAI?AvXP}FVm4p2cSD=fl3$v@SGuUM} zVhUhPhlI}ytSHnI9{%oI3Bv3eXYvw$;*EKSZf7HZb*g*wz;jL0ytT$5Wdr}uDtft0 zorNuaf**B#u(wA98$@U;fADx-h=Rc&aX0YfGX6*k*pVZ_taXWM8G$G5-gEtlAC(50 z0hfyzsynrk+xgj3<6kKEqlJ534&t?bc6NSLT`b>KM;;~J&Py@VwV!{c0(3Gz%VyA3 z)qP}ndR$>v08zDQMGkz)Gl__V{okI$&2X)f!V)6^#nGG^DrTk^s@}efKQ=7TU~5tN zstKw}=-Cz>G_>3%S3G+@tF_w!6&5;QSKr7U@Mc>n(0w15zU(Kyn7&&7`mY#*u=Tbq zij10!KUc6yv%V5hzN$;#SWFE%0W$wYH>#h%xA38v&-9##-kt%}Di+dhTXX2c5woha-{ZNcou}QTleWvSLjagb ziaC=VCSohd^g5@jWH7*S)Jn&eoa4ZrsIs3F@OW#uU7Y0BAZHM+L3n*!z{1-QO^D#h zNjOAt`Fa0%T_)^YX)fs2$=*cqhDc~IrmW$?-4GUH(r}~JRFqsujI`SN2p@YK@d6vp z>K#IM%>Sr!NK=IXF#^8%0?x!MBF=7xOs8Jp-4zK3`V z?ZHg)z=(SzkEDjAO=e_3kh^9N-1=js$=6V5jBO=9-|@~F~%{uv=CqF#PlT(9JU_hiL2|L2ht`*j&UGoeeFZ>dQ+uo_z|c5(pQgJLi5&==dn z)mknz5jCXS8aZdI*94)p!m$8NEPs>YBan2crYa!i`p!}02TXECpC267oPxt{J~R9P znS2yi4v*6xo_pBAikqv(eRg5($xY*ojH<8CT_zbmAsO>c+3dwmk6a5i71sA zVph;B0UJW`x1>2HVI(PWwy5`3MlxUT1WWEE&2>B?o!~%7yR|7li5xOxH6$*64LN@v z6fxzcB-H2< z1WxqjW_xnes`zYX@<@OMzAse`QamXk4U=o00zd|WCTt=YP;lPre4p0C(gTcjW$xtS z$Rm7EZ5Wb-$2}<}f`9xC(#>~vGMpcJwfe#M@z^G^IQI3lzd|=pHTWxB0GWPTE6c6m zhnSDfFM=Vzo|oqO{G}S|!h9BAgG1a?1-rwEGT$E6Wvq$&qbM#0Ly7DGDSf24h-0iv z`E|{{GPXYgdfyQTqfZ|zad9K_HjBty|S# zNXv|kF=2)L!yt>8pTdNB?XjDn(Q*U-vaRmQfX|h=cP_{Va(|`X3g?MsrSVT%LXY7> zeVyrV#er75SGTOIA;EVy+c<uC)l5D9zXlL`RTeE6=gDzQDQ6sYYOrP+lKe# zE=jSG2YSIHWvlB0Z3z$SYhu5SX z#!0D)RC$$serBY5VZGjsnQjX`#gre=Mn>oD9g!l{0-j_P1ZGj=WIP=E^Xh3nY5ghs zi}`C7Yi|Pt5+b%?Q%Ao$ahQm+sIsxSxDcHj&;^UChXf8EkS5GAahYWY$eZrxQ8C{I zqeuFlRFTfBB;SJX@FGD96EwWQ$*oa}sv?)z*Fykq^6*^jR|b=1edi(%x?J*mVJx7y zy2y!UMKp;*g4l^m(NEh_dWmLjiKtM*P(%C7aso1UsF{^n;M8cGWMG!>k{!&3Tn>hj zkFk_OlNN#g;q7~{E5U>4v6gJD$ZxI6582<*qi?JoGWfHtC>frndsx`0@SznQ1qqYD zt^oBzloE|Rc%-^&M>mZ%@^pnaP{kGg0+@`xPO4HlvB9c2&ku*2%m8HN?6@rI$PiV3 z4!mPJGRUM zWF?tz{xsyqUk_R44HZ zbMjK!8K*lOnQk5%Eu^=lI>5u9H095&6T$wQMmqO~9YXHJ_RDWtG1R(F$)xO1*)?$B zlbH0)6J65uLNn3weN1}I*C*ptN4FXl^dzBrKgVN5Jztpe*-I)o4x`{)b{ns=oZL&F zjkSvv;7tpTLIq)%y*Xw;Y=2r!^Q*shZ3v!n?F>#`Y$@)K3>rY^KAdrW+Lsa+O;O4m zu{i*vkhGk&lCe{^!D(0@6)b8wLc>==g6?QOys+yUH5gM@w=5G0cR{q9QJOemi*8o` zyUm;m2Xm0@Qn%w5Qmh~$IO(HREOVNUZ0Hzkaj#j75M;ps{iSHxwlgm7xf2mm0Ce(X zT~v&-qF@8ebuYm>GbM6nY4;$l18_koiqb%A>PE4M7X8P%uXmU>%m+D#`$0}1OpN8e z(}%wd-HvMS*!dt`K$J)QAPb}Ply+HDw9L2qHN@+(46kcRI}(N< zt;71yY7>(7Umk!Mpld3Foo^;am4`~=+uN^mwI7R1`&v1Qx6Q?h#Wojc=^$O5rwZn9 zt=gLPU*|UK&OfNQqt8w67~)*g{#tVl{JUm)-c+asX_}Ra(Rf5$^z1)qr@tXUSMD$I zO;?^onsm?`s+UxMRRV+Ih+wm^@Hxpqr6B#(E75}*#MK;_R zBZ%?Y$|T|#W@%}0I7?SgyYP59kkjlla?ndoUl@2wH2AjGYI~aeD1U~%pGeNsh zn2(l+wby6r4LBr%hMD`D`%2V{+(-4Bq=VRvV}foX$G4A;eD?)DSr}BAgz1Q=Ldd(z z#Ui4|?xGnBqIRLi(?$@uz!(wq8l`*wv)J|T=UG1*?=&}Y<#{*yCm7Pghxz2c3E&s` zg-sIzaB&#vv3-uFjasB-hH)z=56(JAro3mQy6D+8BeRO=XVPG0E~C`PR3uUJuFq!H z+kvE{ikF8{W^<1Q&eb0I)7x@>G(P}NcI>SaT$Z37UnZqpA@3EzxK*OnsUfQz@tote z5jwdOG#b8eo;%(_x_q4+b@QLu=+18;Q@6Ex)(x)>lgA4LF}@}YCXnDtg3!L#ZK#2{ z;x?`#v|~Hdxit4Rolt5JT@S83+Wni0Ch#n7VMhyZg*!LiG#|Ex!Up78yuSW@KU3NN zw2v0mAIX0?Z<^-Z7D3{}%#j~LnS?^*os9Be@wW?)qR8C&0 zAG_1L@Ta!tj&CXg?f*Z9q5U}Qx7E%6-_(ESI8)aoBHwoi1i4Y%p8>>Y6aL~=bv)n2pY0H-=@psAL&3!1WiORh>)7%^Sn6=q0Mhz}TccJ(>ubU@0Gr^J*Bjx%n ztSFU1U@NGr;AuWr7h1<5+qYxJyQAI5DgSl7_L(NpO=R_s&(?8}xKPc$(SJPlamsFB zy#=^{0~DW=kfcbml;Ofjj7+>hQ(&`gHK+U#GrW0=eSGNKb6Jz!+3bT`H{SXdgZ*=! zJ67$c)6!3h~(vnEUfj@oJ>;u6om$N3aDx3T%W*?ae%UML0dTk#S&N7e%gM~E%hL4<#j;e_oYCU$qIvl8jgFq*VC9Oz*F)s^ z3D{L}M0&FlmrGI+mCgO;Q;bs#T*eVga7FX)7mH+@@5r*v3IkPyjWt?*Y1XquJxwEl zu_xZXUX0KjnAqg^+#mE~P=&lAQB7nd3D6C5NLdFz%Jxb2CfSpXeN%^SR5_4$GSLlL zq%E)*lE+w(qy-4MsXES=FbADG1a$)r&IXbAlAy_m>Si9gP{vw_iNYKb`AZvarBlX+ zXS)EQp**4Ir2Lnp!uY#IR~;1vAP_CRraJLb;9`7mhVN7XJus;QY8Mj`1o_z4md_iEMwlAHp;*0Z+gt&% zxAmJQ;~8i0KC)7pgehj+$d|{Mm|#V=!Qbdrfc1NqWu06+0ce13JZq>-i)s_UA(-@0 z2g&*Jh}X$%DdtJ)cE3aG)-se+kohw3-Z4r-f0xowz*m+fJ(W>&(^+O#a!O7x`ELFR z7MNx;&jH0o9upKrUO=5uri)cL5jE`(ZijSCB2u&2U1r7iC89AoakTH#q3uj0OKY%6 zXVEi>UdlGs%i1B6NQJ#WpnUZNg89k{qo@2}vmtjg5G#`vEdp6c1|O?(%>jp>>AS!& zlviKvM-%8oI4mAfvTsL}lNxtcYr|Zrx|5Od8;dtrF99W)a0_oNKSdMeIaQK^h6M}~ z|61$P(#aLFj#kqMHEQ(5uooond7SN8_xuuIhL`g;F_^CQfsd`hNWy` z=*XEdB^vNh$Q|Rh1J0)IM}CpKF{?L?-pE=QA!R@17i znB6&-9MSt*&OJd^V}ogKmsWXd(0FhXHK5Lt>nr)=Bm_!y0 zIFw1KGb{F3QMiXUjdG`&w@nwf8t*CwPp)~}wfZy9 z$-MJS(7A|KpQAco#ksKr-`_n?sTPk%mZK^)6nj0wU&uxq9`!829>kr z2?_C;4uc7)&PYzdr1%0(O#%iv103b?lS)_d$k*640GWBfQ!Y&fS}$9Xp&>A#BPu=C z@4@iESU`d;F~Sfe4y(Z>9gM0gzADlZ#sxx<0OTIzM!+!xbAbIRK}sc%zTndy#9r2* zVn%;a1->Lc=eEU$C6^lLV(vINI*NI$MKSyNLT zRvOsAn;-=+UTL-!cN0{W6z_vlb|FHI!nes1W1RQ~tdT+Kh1S3HT748KK6GL%C*FMk z!18Q3VIPVl8Q@MR$J-7-_$f2VHUuPTfWhgqj%E~NQgo(J+3GkEZO>?@fi7mn@EWRV zvtqF=&-AybnvS!Kv~`jy1CBF5l?1c;5U)nxuoYxO%Vx=XYe=qd<0g_^waR0W0s*2_ zp>P69Kk3w}L?RNjLkiXg;*8f}!XoU{xD3{s3RH%<@W?@i&QvK2pwR}?u`ug)9IOH; z3OH?RqXrSgLKC?ak^F*8jY7);=gKfs?<&E~Po1DD~QqJ=>ygX#&{?<-t zD4mOV(H&MwOgGN;q$pRxt`FOz7xRW&0h<)T^0No1hW?jMEjTcIPOdNELOxMz)DGEa zGhMMA`33PWk8!uR7a+kF%HHmTI3a`;63^Sn5lqqg)B*Mwl;w1|7WzivoAy>Uo zq%d@+Re374bBJDT)?O(Q8be&Jyi)EXHw_{dq5}`bkWQGTa)7gkmu0a@Ox7^$ek1ULWbK? z*q}X`=9?U`ub8aJo(z~*2fd1cK@(siU|Q%KxHd!{TuTR}0lQLtZ-Su+gpvD2m6x$h zK%DK%qD=w=AtCakG_2U?d5Uda*6Gw71le}!Re6aVfwpUzPh3;)_a7=s(umdI>-Phd zucDG(TA?HjAW?^b8VlIT`d|~Qs3UT5*Irso>{G-joH>oeHW^hb+=bM~P-+O;s3;aB z{ufF<5quivUHyys^74{UDxm$We1KQPskY#AH0|AdE&RJ)b1B;$< zzF}U@-V>`hO*fegZbztHfE(U)N2g@p+3)oxNKTrFCWJaw962@ACW@gF%gp7rG+Xdv zW;m5gnp{;dl(w?$G@8!Lg;uy6epr1b-UtmXW-}GbK(&$-eUEJ*!+Q9ekF0-{LJ2`l2j0P`Ye6x`Zc=HQYFcSmQbfhOWWYSRT15!bcoNNzuxLF`=1P6HLAn ztg2+3kAt8AWS@X*Nxpm^l#_OIh7=9`(hM_1chMP%v=}k8_*SY)*q zU+G_{W1tTM$AO;5QdvQUuEeO<2xn}d<*2E|`#}^P^knePJ;M6rVt^cWAZtt2SqX=} zg(P`PX)G^>YP+Td$I&05aO?5-HgMT4q9#Ww@O-bHh@Dhx6w-@nA2n7ZM=z7C z$t^NX9!O|LUE@~x5{Ox6mJ#Nhha*=MSA(IMJD@InW4GgBr zyG2?UqGy#zCX-ahT7&Lyl%H$ifSrhwHghOskzBx1_$^~lF&K<$Ek}u_jRcYFAV3C| zZscjCi6}!m2{P=Sam}LTlHtpm52Z+uS6D&U1o*_|rAZJfZpiT=gP81c)^CD6@5s>+Y>1#}9@o@$`w$|*0_L%Gx47dmC_iC7GN!Px=JA`F zx{E0?tY&J1$3qlT`Cno9Am)G-j?f0UHHi!rvu}K2E$&hg!TYw7ds7R%kq*~zLyvW5 zkz#D3Y6S~FWJlSOsAHx>vGS;>6R^Tvw$BBpT0ExPJh*riE$6q0kWO>voQxxx?)h7t#Ky&j=|hk^`+w!xp$OV9eYuHszk`%7%j*^V_5L9Eb<>Y zC}LcBBku{&bdo;xGcR!+lS@t(uNhyb_Pj=oEL?%w#fbH*l`+?FhdhU@EBtT@FV>tR zTtcwvc!O;R!tLy4z3$NIct;sN?g(9<=HFw};7b(UN|SsFxqt-bajbjnOa`}KE0LBi zL=dIm&k@E|d{R+0RZ%^2#)d-`ROmUC!)JU^7rh(qo{AHIvZxMMbh>5v+%qNL{P3R+ z>nPN#v&e}cMGWi__grS%e?alIJFUfO=)1)GDO9U={aNoTeG|kWQ9RD7I0w(bszpRM$QbM8tH%r>5YBjXq5>xe5 z!KVl~cBd5)QRRIsn4LwY?J~)np_Z#UW|m+q_@@wx2#~2kFp&#whjV8qVN=-cylh}0 zXCSQxv6C}Vt38({;-q=QpGm9wWO^7pRPeMINu5>J1fvC=5=H_RZ|%c(0XA}3$8>oz zwsPp$Vy<(p3C!nEwvru_nxY;(nFX$ipoy}3{R|>{03k}CAvdo=N_qz`7g$bb1KmPt zQ5GHD>ZYxhn8vwXaLp`XKHIqI8b-fWErh|4s|Cec0u@r488+#x^e;J*P3BA4gGKIF zJ_A-e6E1`#7MCZa%#1XqtkFaX$){5`E&1LoWk3Y!TE~DF+JKh@jWTXhVoF{l9Nc^^ zAUd?YBFQ!Rs%73_YMv>7!1cGniu>x|@552y)T=)km(khRU+SATjM2Q6pjR$){;?Kw zS9W$kQ)COnKVypk4b~{5v8XF;8Fq;h{+5ciDDw5J(#TP$;2#hbNHh9iIM?R{z1I33 zb+!x?r3${sMWGsybk^uhvOk$toHXXZnJ};q1CuKGFx*ZD_~IDO@`P}is5N2A7VnUB zQ~q>Del>c>eu)1eFeJ%tnr4;?_ZxMn*_+H)fie&Z74K_s)!-sR$DD%Bol~C!bTG_6 zjiGQZPzB*=e$z>%g48J9eN)`@4ft#x+q59uVyt3yZrEg+>8{2nVI}V1!-ZkmV}Qo9ZTp3cmh${56%<-PCre0o5^n2hGNz42Kw<1`KwC zu_jwbp$#xQi>s`{X~8teoWQNB%It9J0sdkd6*O=*It?4M5~3iH%9HA4Y^K#SVI(Lk zizb*q<+7J?q5A1}O*Nhd^IiRX)7gR1ngOd(4)>>Q&hZ4B6B@)+x^0&CQi{Zcl=dRf zOg8PDFDm@lMhM$x6b3~1ZiZIz&hu`;`K;Ffq_qY!EPAeRj}G)9X&ASf!T zuCf%V40hTR!6q|;w0YiNXQ|YkSI)_YIgNJtb=r`Jv~lPS1-3Rpu&O$^%%pNBpwq+w z8wmxVS1!kzO2%#Rr(e}f(p~seP?{(xm(Z8|6qZ>F>0?_zox4 zPv%$?xa++QF_eI#g(V*BH4`jWcaee66s^sArVWd(FtR9N^$ zLp%F%yt!UoDMy_?oJqtCe(y8$b3QWik3;?rs8)U{fR>1_F1@sX1Gt zD}tquOp(ORiDgCoXi6ZC^g>@8lURjkTrGqh_)l+Lp>{MK`k+nBdcjnU8+XK@w==5pCz}3d&=qG314Fapmx~aj_VZ>`3SuV;a zhYX^ui^z!mY}Tf7f-6Cgk$?KL><^oG-)VdRZK?Y{^Hed9(!Ufgeat`;r90SJ>FL=k}7} zB~9ReDZWtqH{k*-ypn7oG`zWva1_Lgj?m;v2)VuEBooN35ShSb!xW=@6t{n5eb8XY zw(`UOZg4SfAn$uGFAw+f)AqO{NOE-vcs3QzJN(Gtj{KFG5O`CUOgQ$9m%IBxo}0MO zC-qOwi~PN4dJ;sEB_fsA5W7M`o;|3Kyaq3`l&J6kPiK-_*(&c_R%H`)-Z~{ zr8!DHX<5f0a!V9Tm(aPExvP|H<(iyZ@Rr1SIY>_!uwrS-Ks>6f;PvhC6y=*&3h z>@N8-WAPB(myY`5X~PNaPqg&XwsCn@$6zCE;tYQ;*B{-50aB`w;A`Z^dn^O;A}T$; zZ`{vFQ{o8J-<{0Cg6owO&iHGZU_MXHQ;6{&u>|3*a)?AaE_aPhoNDGTCZB`^{n_t= z>oXy(WT~qqtboh_%e8f0h1mw33|*z-KG}a*NbzU!3Mn&n1XU0*yWaQ31{haz6*<0j z+~Sm`zok%27tJ};mO9b&O0$n8m6x15>CQxKjr+&CUGZ~-|G7Z}3 zNQ?fHDvD+hu)G>4PFOBBh$q-o@anlHlV)|v7imgctwq%%eb?w{a_-i1_xP=RL!Z*A z8lJWkVP%r$Mm~cFMAUKC`8mUPp8RZ$V?EW-&S*K6gvA<+XmgdWlCv~n4fFwR{~4vs znVyB5rr*`+uFwoC@2DWyJ3iKZALnc4<6(Gko{!a&O4Mo%O|uI-X;c6r|1bzncavhe zrI=D!I!CLmnM{4E5=&1fZqgTbD5HX1f%T;WN=!W*IgdA z88m_>CoCE;r}t_0tUnwq;jGe>*v}xg>cl_NS&OrvN_)9%a3*|^)RGmeUiq6kwRohr zn98&Wq}iRs6Glym$q$QJO!c%}qyK#wu|dGKI?H$cphv=Nf0&;0(H!S*+~wn}C^}bt zem{?Vo)py{b`DkOzZ;JczuRBcT=U5JrZ=WM-v7P%()Ns#fQ6vREMUoHI;o%AoIo?E zsl#fuW~aQPQoG%u!`Uu>hg+da!0iu7$iMCOcO!?Lc9+WT>GyN{pRGUKw)p%w>kC>t zGI>f4tKPMe%z$s%LeerScT{^Xj9?kFOx$E|W$dp+?RDgJDm`0f$P|KnU2^fS8fL!L zwZx0DZ0qQ$#EG*q$i&s(di`KGI)qDq}Vv=SJ9V3ai0e4U9nT>b39 zjyx^?^|_;XLR#XD$XZ-d?Yici_-Jt`+GErj2u(z5*g?8?!@gW~_L=n0l5 zxCgtlR%|=^@q9V^HkrYPsEns*@#ilspEn1)^K>50$Rr}<~GQ#e$Z-1)N!H9~QGKgTiYUv_zm zq9rE#wX^&a10K+tkF*)F$r12rM^aV;Wa+dZ3|S;_t7OhGV{xI zy$tQswq4<1RE8cMS7HDs?ik$Ye_$9wgGc0p2b}4az5Qd7ERwoIFqjKHZYdN?S>(%; zOG>C~AEMitZB=I(i<1+-mf;PSQFrP+6;%?YRmFDO$-vEYIUnBT2_Il(`%YTbl#``H zT}}`U=m`8<$$$SmrdPkHxb((|=dNYeqR7YtZ3|6C?0H%Pv~* zZ;4V}O9ND%o8J0m$@=aC{>Hj)M_{%at^B+2L=iYzf>&lJ#?~N{@4TNIfS5%1Zr^D;- zqc8Sje|I7H<%ANW(tpyJFI;3*+g~c2uD;M;s9`rpXyGx{otbitXceR!boYY;596iTCXJULk{#>QU;mvW?Q7$L;=%u0BzNBz9^L=t zr~XBFjcJp#R+IDi?swcH#+Rw2{p*)J=sn3`JbA$@1d^>t4<5Quc~wo2mn{%&v^F!+aXvaj=Bc@4!rjTzIA2kzF zN@7<^?nDi~P4AfkSM4(k9d8~Q(MXOXsGHlc@%-EF>%XUThp#hP-;sg7D2?Kt53RbA zsiv=nsP*SBMi7Iya++Aj(1<;3R%@Jyl;ZANaq!kOCO0TjN*XLe3d9K~W!B1$z~|jv z%V0b6D?F3++sEsh{J&qAO1X4)6T=z7tl@f3t+@jnA`8xlJ8KdHw8}dmFQ&2L7f&+J zZI_J4)hyrk)u@3WQ+3=ens-oHtoQxtSam0HWAuYX@z^X3fMV3hf*+TVcbii58_r*z z#3J{QLSe7o@(SJnJj|#Ju$SGvjF3O!eUXSoQ*pbfzHlx(aOx}wQp9}(F{n%gQd1Pf z;e+r}*KQq3;F35blBWKLSMIkQ?F)-MqB0~zXf!Bib7_jUum5tz#_fjQnzy>tQf|76 zu@&f`owhy(e+HCx(VANl$H-hti|^qR5Srn;g^2oN@qlAu5KEdqo9-g}JXeFs?9Q^2 znlemzXT^)>calEtbxJI+b_~hkg$H{7#(qt71PkRV3FTx8<-rS>Kb$Q(q~rZx_=*HW zu!l3!n3%3~1ua_^GK{4}GCCFh8R`f?o_(pjJa7o!P+82kGvjf4QM2=Ynf4qzMz-+y zPpaLUG0w*`$LC*sKzhhsaY|fhFr}sdx|6*QdHf_ed>8;liG)zR`bs!kJwtTIlM|zVdrnM^-fu z$A!G@A0ShlK?LYbL1?W3SDsXt`^hgwUN0VC)anAa1dE~AU-(-XfU|XdB)S_FjF~3H zp@+u(3-*)N4=M=$fNUKWgG>NUdQg}rhQW}h&kXCvokMY3-HgDoHQ9~IOl_X8`%p2_ zjk9@>!2x7TBbJsFlMpv4-!NJ8rXV9VTsDt{bEqSuS-zEjSw_yG&P?FGpbXDN-)?w8 zpx_dU00Q7oMA@qEZ`PPCe@p=`LbIi)9(aC%+@35Q-zE_pk;k683h^%rRaiU?YUTdo ziVaP5P>sUdsB_aNttHqm${A{L^Ne*Z<0CI$o8!yws9%B5mX+a`?$+=p5vYz6#Eg}_c7%?Ras^C&+4vuZ1lLQ?&#K637>BA@nN(n}5 z;>jcLb=ku^zfGP=4JoL-3FzfuCpAhh&guHt{zGyaaNhwq!^w_B5@xLf`Zr7VOVx`P zS+9m?j0lqFaInjcmQE-RI6#$`#mH&i)N()9W2&CKtMk7R!jA_sWWl12O&jQ)QrFK^ z!DAUNP&7`4Yak-wj{Sg$Gq{nlZ%_ReG!EDchz_KL>1@O>(iG?9q8Q%~AjYEC;+>Q* zl$S=@_D$QRkBZjffhZ%+{l_Iu)c5Bp%Vrn0s`MbZsy2u!nSw93rDJ7mt$=?Wx|QRF z4m#ujAF40z^wlYi8f1WbGJza)^b|(lvAR2CWbI*EbY&w0)pM6yOPB$3$v7xw*=2ck zsjYz8!XoVAdy_`xmW6ibtP5Q|g%`*&u&se0!t(W8BX(fTQq;F^Sw~-WAQF47ECD-P z@$$QF9eo4mkh^B>3UBo#f_a~3^(9OocJ?nrLZ}w2*xG?Ad`c;SnL&^_F&7BtiE1O@ z$ag!^Km~O=v3(kQIrbTC(8aEEy4l_i^yS<0w&mZ8zF9Bm59!EHknY;qp6R_YP~#R!WBJ&%t=T ziyfP@rK~n&Aj!|34g8G~$~w&14?RDqLJ*9x%qsVsEBypE14`Jt>HdqEZkUR5KEnDR zNs01Ow2qX_ZFeYw(f_iGfRKBQB&yE$dTh*u+F9r!*mxLmdjlqe_)g(xY1btO*3SQe zlVTJhX%|Wq9X@#YIt9VAKi7~5JwT=%i`+c_0Vl0=J22+I+we6Ci25x6m&PbeAp7IU zE)K+H>WBS*WMyN*qw7`ioBMZOI>ehtBXSrZ3dk%&h_{^mqKucsLGOsc$`eyihtk(} z?I6b9?P)Mz221mDcl_HVaxX1Lz{Qkm;#E-b3pN37qO7`tA+2QKq=wqQQKMFD%0XF% z+LCIn4YEWnLCaf>DwpYI3U?i|g+Ax}>K}OOgSolc)^{*nn`pjM@Amr@;z@t+^8UL+ zc5U7@sg|Gsoe*P+Mx0M!6GEs9RmPy}02sFnj?F?O@N$G{Vk}P*GN1WJnN2eBvvUXa zX95p5au-d(yL_7xO&wKad~)h`f5hkaZ4`(TJfKJY2Il88-h%^T zE1CId$e^^S$HK%9c}xy`&%OZr2S;HeI>66A7IjjXmQJ)+f@3(JC#?>L4m%PKh!fAS z;Iqf4BDZJ1%#iu17^$5FWJqvSexm#r1UCAOdL#`QPd$)*zyrG@OjfM79H2%W<+t=O z7h$g=%1u}Y6I_9XUp>FPTQ89;^Nk<}(80mzn{n;CtN$dftI-eZbMg)8)DR9nP!J=4 znXD4c;r?&rU5)dPHAN0wdr+4cRwy?bxgcb&eIl91X8<6z?jn8|;^2e_%rh;wTV~yN zFE0X3+22A8KU1(%3r&(>B-PZPCfQSfD5faa)W6jQZ{#5N>Z-W7_&RYDxj)#BQT89_ z8lKF;-)}4;D|OeqSt> zGm0vl|3)A*#M`>Qa8)_Gx4teIL4m>){531{HkLM^T7AH^hCMEQjgNxlcy}r{z5zh> zthVdplp@jE;V4!XH*(QS@+Qvl&4ym}rnc+oj3QA=$?`ZWio?6jI8o-jwqD7mS!Pks zbVbuoxU;tG&*(qQu^MS})%)8nQ2DE>duU;8sIIo!;z>zGw5L}pKjpTk%M_p*sA_1!3|Eo9u8t^)fSG6J+-qk$-pE_0y~5mnI6RHME3j*_VWtP z7W$;cRt2$fG{gK`HWMi4+`%&NND}J`D==_akBIxhn{^ZXE=mVs(WjVXUW(1dsd7}T$1LO%%X45zWe#fyG`e>ip}w*zg?NE zjfB8UcOnqfJCeTl3-fKIu@P?Z?>2@#G~}ts!sNU@;$k@Yrl}jvR_o{=#rnaHbT6Y!4h`VE;3%c`ql@uUP*ymAz`ECnx@I>s%Y@QI6PEY9*DC#~k$` z-r{HymHDC1;%E`7RSI^tW&p7N@XQR+}gc3Dm5O>{s;xnLR>tvUXjJC6m@PTtn`Bv~m5y`2-!m#Q7u7JYUzyVo+INA=7!Y$hB5Z{m*Fn zc!cmV$J&e%Rq?i&kn>wl;Z+4uUZrCis;}ko?HGjEYL42#kZyM=JCg3GZp-+~6xkD%$*O&x%x;D5G4-wHrLqAX} zy^?#l#t(0*yxUm*WWSEXe{u=cC0r*NT0f!Q?7anDsB(JZ?8Qvf6Wd8-9;LAqk;lk) z$h`;t(-IBVSjXVNQH1>os*q~qHTG7L^G_P(JT&~1z#nVsZ&`F|>Ljo02L z_P*KVQ+PsJIiM|+I%%7cI`hyr`jB(ZxG6Z_&OZ~P6-_tMtXk&1XB{4vdb%3t zJCZQ3Gy3l$I~q30`#!+fj!In(c)l4=E)w?X*U!}Jf3+0$ zXeWPEdA4OV3RE+%-TXlNGPk{gc?Xjux~=hzN}=-AMST3%%$TQsA9s0#oLR5-(C}_n z5z?tbokiMl;hB6>Vl|u#vrG|AtDDGbNai6r#3xBj)%b8iz8$i5*AaJr_~-K9LV(V9 z90G(jn}oCKaZmP&rthoPx{&AU>=xuZNA|v52oLE#*qJ8VxtnG<^PD!mk=gaSThEr+ zfIjlIl+3TxO6+^hEoalLP!9fM^=a7F!^aVu*|uOyFm*>c&HPb3F`I-`zp&P}7X>q` zP6@~{Kohln`(jk=X@zEr60Y-AY0Dw_yFhfyzG-@DAzsJ7ijmvFl*{U!Sy-w^>`OAQ zS*gA&ut%%WxBti%-x#^m0CZbUPk0*5xhjpNxg1aR=5}A>OK+2X=EAXrtLwKtPw;{3 zxooJBg4~pf+_D!twX)~<&3ps53w)zeHXBsgZb^jFc<>rE*A+Wkay7r1Q_cG%k!b!R zp2%g&&S5^m!FA(A!rb_&2F*En5F01R#X~**pB1OKUJ%$m)HV$^Eg(X{{#2{9@-FYI zACejoI9Wy_<|I5P+G_g{^?&vY?e z3%kJjz1$AWEGH@%dz*6`)JDv^sW^AML^7K%%}dItXSA#d!|W37JLnsYMQ8TmMn}NjEmqPmLiM|0$sc$el@*q5_Do_kJ($Y zNs{qkx|wgFoX;>`i7rBQ&c4{)9Ia5Zi3gJcdonO)zqF)H+2?$yu4}^TGnK9j%NoI{ zEDA|jKQy+#ZtRh2A-{&X)GeB2%*z&uL@G@-#T*Nltz>cv>sP52j0$l zU5Nk=u%@a`CHZNrGS2KR=}ajqlR}{yR5P<=LSR!iChyX0)kot-SFQJysfqv{S7tp@ zr^@C#_Gn>`JhK>|hQZKY6INZBR&E>dNN>v#rZCL%-vGZ|&tlr*c$GtBUSZj4K$Ld_309`&=OmCJ0% z%k%Q}2r8(oS{Q2d7WgXcDAPQiLhr!Ia$I#Kg=(g}Wskm^{$Zn4v;R26=PrL6uzCGU zU>K0NWX)k0@_@A0p1E&xy9hPPmf5tWqbHRA(e%F~v*L46|7y?VvcR8Oa}c#wH&lBl zV<~;DL04pq_p)V!1RU*jwNKE)himz|SYX9)G>Z|Z1~rG$*MUp8BPg5KnvY>s)MR0& zitZUt5@w|Qyhl2FVArUc(`P8WZv}e}S zkM3dS@3^w1@}Y@TNu|Ol=QnJVHUfR+?nQ~+*fx6G385o$oy_+6f?0Oczv@IxG~9+!)=Cqng>g{<^q~+cu2rm5J;Lli|>WjkIc& zor1-~tf}NCWQVN_(SwqDp^eQo$t*>M$e`9KAn9Bmn71R#sIA_dLc|7cJPW0#qs>{c4uKpif z$8V`;bFP#^73OU)pYV-#hPuYKx39xo(9UA>o4$EHI!}>ht#jnk|1h7)okkshwRkJP zIHN(uy!<=k$OCu#YY+dLz-`4(sm#E4jM1qgQf2c^#(z}p&AotT$ZIA&5Hp8BHQu#P zH|Ur~5GWP6FplbyT!%5Dk!h=!$v)fVJ!pFO1k$~gE_|Eyj~AX%T%{Fz76Y%r9*9I< zx>a`Q<3BS726^UFxssj_UY@fZ^Jr36CxtZsg%c$G{r2XXn!k$!k7Mv@e5~`fC3Y?U znxpupUUg5lWdF`5rc_nX)S+Bj#r&T{-LtI!#p97uB&LjA1Y^3G`5O$~Z^dpQi|Wxm z`)4d2ZGlY*L-~VN+pp-?=v{qDU9A56Vk177jp~G)0SI?X|Qs^lTUXX;OpF>(=Hnmwtr^S2eQ$M;+*-bhdKc?wr{ffUh9u~*R2Nz-(#f< zIZv9`)}KFg_lCQ+1Z=+ET>ps!xlOwx2Ib**LyA2?>e<{A|K5J+Hu5z_WrX=aSI>)K zA^7m-TDFBX6aQplr$*Lkt!68>Ez-Bw_HBh;Go{#xuzfa5cAD-r;OINL-FI2+3JX&I z4Kli;ilIiABgpUIa*4cDFxY3pvoor~%4F<04Np`;xJ%IR;BcG^q7lXQ?;Nd*QPl!1)(+|4B)1;aGiT5J6|D zxBh)7_&0<+cj=;GD}D0N@X*$c!S4+Dc%W12D)?c6S?R@lYv0_#R@XB`OvXW+y|JWs zl@M8Sot`9J*SUM{8_YU3qBoUpGFd}rnGZ?-9JyC=326sGPAfO?i}~Jvut_6aQ!aL` zHnE>3?AY6{mE4(&c!|mkeB}Q2iv$OjJOW7j1aD=PYhaQ7*K8MBLsyitS+#-YKouA8 zkUpN$Rg{_*R`HlpjUXIMTua~!%gpyTO4#SE{-77@Z>`PE>6S0--?bI`)%3b`lY-!J zRTEpyF=;-98pLN?btI9vC@Wqm=f0Ob{^ChcnV#4IL4K5Ol`!VJXEh zjVR*)RBB0W2D^vkshjv|Z}f^solB>~kom}Bl`5*LE6<}xN_(wnw2OJF)jz+ao=?ta z^M0F$V~&+43l~#}yo+`qgQKePTy=4XG-a$0ML6=RAJG2vNng_CLK@CTJg%V-IT_6l z#g5I{2P_s$xkXkjxpNMg(bO&?)EU3w`;#xHInpIZgs9d6rw+8A6@!j<>%%9ZUH|%eOf4&T#e*^-Pura)NX}2=QLEdtjY$jDiV{hw zm&RQc)yQaJAhCNyMm$3&6-f>v%&X##M0n?+l87ve9ZKHdbswAkBzqWZL#H%Sha4d;@Qj@WwBDLb>Z91iD!p^J_r~-$JAf}DAU?=5J8M#A*7q1a#A4Q+t=dye`@3qFK8Urh|y$- zW-dE)=n4a{%~-htwDsgCPBG0k5F{!31DsA1kaIls-jp8m5Tmv49~_h%V^&XyHwyrG`Vb6KhD`8Cj7={Rw-Q$dc}q8Tk$!QjXHJGSdjyKdm>uR{1ZN z0KWf7(qz~G=2WRm5bf#ZzkuBmP#)Li3=I0l*`dyZAoo-AqMmN$nK7YTl`M>r`G+%x zx-*t_IrbU|!=j2ihQ22?L!gDNV#z5lqOaX8<;1n2@gh%@FJiw8)t_yA$0v6G8fM!A zs>{$E`KKz^)2;|A_$3G_gQITFS^nSkr8OgxdXa;&*4LHek;EcH+&_&k|HB5by?%B) z{0|YKwzAJQ)o3WCNq$TImQ9%z?kYaDGCN7W zu`GoZIHoM`nDG}NcJZ#^Kiq~_Qo(%4CWHs-s`CU%d4MEF{+GG{L_dh$0%?&HuM$y& zmtEkgXW*jse>~IGF&+qTZ%aG?&Vtb8e>f7B=RiUVi$ocft@jwz$7vUwO4d`q6=U;LRSPs`~egG8ySC6Ow_@3ec zz%SJYmLDNHP*E@t7ueQXlo9%89B5y@2YJ22!8cjV-}*1tIDZgEA_M%$-CKMGG+n=5 z@UPeYw(|XJ;?Gs!=RfdGWB@Y{IVG?Cdt7#TY(4|0&lCT}qhmZ2z-(CsivKz9+#SH4 zYb(lAXr04?S+{>yQ5qvq-jU1C_s+IzOABu-m5XGe)b$wu3V;f~12g~l;2&QOYyzAs zK(R1T{%>a^z|0xolX}_3WZ3)y(Ruox+^VYEglyGc{|EKTJ0MM8l9x)khZ$Hme~ybk z3{OG1UCvrsD5s?V#o*9ieyNlP!F|BqcQ|MkbiJ>;vl>W=%%yna708gl&# z!T+Ve2)u_jv8DlCuR#7cR`T{mO$vm8OO_o7x^-yy*RV4nmni?Ic1F08{%0y(4~z++ z*GP%{Uyj@Q*Kv8k;(sj7i2`J2bbyJ~0f5jE$Z-e4{uhYcw-ToSWfL^#pJSpRAdX70 z)}rwl@`^*0h9p(2Z8+LwgOyb!)lw0qwm<@2#iHdQ;5Y%;|3mEgCraQh0pdqs`5JOg z19hB$2=PLz1OV04rEAbR15}G;?HO8})6EJmDa*@}Q{5D%+X)q^}=U}Irix1X6fExY?(IFKPdum#QXuraaitx zDl5%2??_?E-?hw<6D6#U(;b}LHdcDVbD)CAVrXmrACgTIHA*l3tH{&{odvJcDy?2Y z4!Q`sx0no7{T4YMiS33Gk|st^2&Je99@Dx9v+?G@)&h%+OCh;UPJfa!!7aY!tKbYF zY89!@P#bAWd9nE{4w;F{o1JX%&CcvANfZTqyjl7lQGbVyTexm)?nVY@3y3yMQ;;f ze+PNN@*}x9DQX+Wl9n64I!`CKLJX}Ep6nDX}Prl@vR3wj2k|rTyve)j5;$b$$`6!@0#(I~%s5AwhO$E+ZMHDmsZKRJFWjR!tiF1e4 z9+4H2dm&4ijLQ-6g;bP3C^?guDBC}b3@@l>^aKi_kDzk9@M7lWxlC1OhALF6j@;_Z zTV*h+J5i5((KIW--M64t({3^AaGEJGBio;Cs3gPun4=9_`JpA7m&;U`>xjCl+>L20 zo$btG6?Z3?yX-ywh@BkoKZoCEnHd~MBiVY?MgMRtbJs$#EkQV@=G6@x;4#-9A(_8q zwoCds+iLG2Jbql{HIh-w@9V(z`&hwXnt?CGY}~IhHEV^-Y@VATy-u2!&P!7@qt+#} zKIi!M0nc8+H-j;5(WQzGqUYVd52Nw8Zv4U)A zMvS6RCxa#ge&4;X1zSb*zS+# zZohr1E{GUE%kou+N=CVQ*3m`v7JvLYUMh53z{$$)s+^P-9QP`Y7>mF`gdp2ST4TH8 zV91dc%`fYOyPtA8dRf4T6`mIBl!c+jDkPsD7kL=c6c?kIjuk|Sa~n^NTId|GGjKB0 zOp97_WU6;rvr^zPbuz>eX3B5u`dR0#mz#oZtsBp4c?&^94_1R}5|}0^#Ze!?H^SHI zZFs16P=F^l907h)ySw7s!bhzB82sF0QYTf-pRy`u!q~EFa&)2)A?*15r*SHJX)R;n zZ5FpXH%rOLV)|w)uDyoM9q}B?jh;e9cs8RBd1VC>g-)lp#&r4eXWABL%!NXO63F2E%bCIBUwa!(z(C zeMfkVymCBDwEhISnpL`nxD^N^TwnkRI`H}q zQLC0P$(&n=@kSf3TXw5tsP4(WdvI?V*U;i!;TIaptGre@+4DhAjw839R;v%FcbJ}Z z@ELY>)lb{wc6a^Ww>KojJAZ5-{PU#IBD)74%+dT1jz82X8C=p{67#dFehWuG+cM^3 zbBK$()oShYfhr@`%eDd@nn|LUwhf_mYem_D`cd|;K5L%)Euy^-EmAHPCM9rfxTu~& z(ma_siN9Z(T~72eqe*md=fq$owxYCXj>5$@LW#epbT9 zyS;_MHC_CQ?>3P5^*(F1-@dU-qvQb-Z3N*b)jJRWuV_~`j%7dUb~_##R0rcE`Tj*Y+8FobLy{PFGYbE?ADWcd=sx421Ip&VxR-#FJvNL3+kexFq z#+(%#Gge^-?P4$R{5R(LH1ojTco9ktvqVCQBJz?_VO$(MAQg)|(noPLo`+8skBobA zm-lS7F=giYD#o|*glzV8?48r7)KrzX$h#?Kh64-uIioiz6s8(W?}WZt;iAd}B}t`* z7GlqIU}u&4$(1YI`=679!mgr!_)skiH;G7vzoncl%AT!p_B!eB_5M!L3eUelf(tpr zpkc|=r9#OTC6}i@c`T^uzURjh%Qm-UO>{SGrlmmmv!4D*#$w_OUn*hHd3Rxb#Ec9Z zxxi|FVf~b3@Y2%~{Pg-N2d44}x-fdLh`Z!>wYf7E>qLb6DT>MgW$ut&oD9ZNcl@_~ z^J+HQnZ5pE_SyB6?H$siRm;+K&g;)&0>X_a&9kK7cXt958rENnI)S0K|MOsF*$3>H zd;EL-4UoPTa2xIZ|Mb< z1w#%0X(opdpxglF{&~jl2P{4RIqva4%@f7|PYJGaDkf>i9y|>Htjw zCcYUGH~P$VFKOxF7#Fkvnv$OkyzrRbXAKsePz0EvP^J62^O?3gAz-?&>CEBfswp<- zuB;3C7yG5R;P3p|R1>R07c}kW4t+*%64G^Lp32g(=(Q{C_;{Ohwt77`@tBqK(35lc z^2GaGo{H6VYYEP|J9Gq!mJOd=J1k#PpCg{}Mc1DKrX<(z4ZBW6?>dh;D@A~)DroB3 z?zwmAQ<&(jp||J*?fFB>tP!xxXbA}9`f6QdO&r~J+Qj~ z4PYPp+4~%r`)B~2W_^fRYcqI=oPUnG17sY}1uLIdM8o~+?izH@{nkMp8%^G#5pCyB z&5qAMv-uw1JiLLPu64cSIr?r9u3s;AbhU)|ooUf@%{&DMjt}a(Q*$c?C7N>Ddf|Jj|b;j{tvH4R0DjPY`g|C1Glg$nz5R zO*UW+dhCqj>so(WAne?pe%m2>p#OL!yY{l3^CSvhciERc6=Z!hIv?wLnh=G4hd_JQ ziiMo-13aLYpudygkTY!H5_H?>w?oPbjcj^xo_OfSZr_MAxX)Xg_kDEi+LMJIhIx0W zv^(qHw`1RRHoH#T<$~^=&ZmLf^tK$|ZORTy=sjbT!3{0w@X+uosYzu2J@DghWc_jG zNXzhLW-X^}f!z!;e(U-BcFwwnr-wj%1$Siow13EVW91lTT?}#U! z9v!`M9(X&NI-iS0@0AVjyS%%;DyHnR-Y>7w`aR!0?{c!X)ZZm|Kw@Rr{H)KxtiH?R z9_?FRVC?72^Lr!at zi+VjNLhmLP!8h-ri>!VRT7GA1yG^1`wN))NCoj(`z^$&qvup1f_DdV^(AEVF)jIJ7 zfQe*nY4ZnU+st?e>o?L;Z&0l-eYfr=0~I{vD9IJjiMS%U0+SUKqJkC16y+3nr&nX- zBA&XiI~`98v^neMI*MW|`4gSrXt!c$PR!priEP7072@~mM{B`27TBknj^Jj3@2<>f zacM|_*{B?)bxmx=qVnK0*|$dMc@MYlbc!in%{%09)n&x&Nm|XCR3O-(4Bf^4rdJK45N4^)7T#Y-*)^LFOULP z>|n(uQzZ<6DC^c$&ovRj+%&i=aq)+Lu2Zw7bRAwZWPE+>p_D7vcge1u()sP15Fr%z zg*H^rC6jP#26LGCzuCwd7tBue`(p_eaqv|oTYmPD5=K0Ny<*>Bk#ZuJp^c#4+_k9m zd~;OTK~WRY=8yU0ccfs@Edor-RluGt3y~4E#<8bwFl_DdajSSwEzAeqDpAvK@UoJe zVS0!zxk}`j*?OvE!;2q}#wSMLM}HWs)@Y%T+Y8&JODo|hMcGJVszn;vKV1?@XW@D# zm8wsa7Gd1r%0xd{JHo@BUfdPIH^I_aNVg{y!EtFzqNlBDh;x17C0B2QkLL4*pOZQZ zbifwZeK!a0j(5or2~x-ONz-yScrHhZ^hjY^0LtJ;DvIc-#Na1?xo=de+60E2sVKy4 zbHkb&DP4@cQ% z{)?>!_bQWGm>f$CL7-3@CL-W_xtqzi4a2XGD}@A#Y;JGxei|7g(&eA*yP;GBcnlV@ zRK?Bt2L)da90g}lC5Kc+B8IbiBf`A(a5-HmC!XSFB5Mb8w z2k4;Ybu%znPMVI2jN$HL*kKWCOn=dXv9E4x&NKJXGLY-on#6b`-6 zC2-hy3aRg(hB%Oq0%-~qGo)GV!gHBz+pta^hWhJ#S1=8;9MB209RD+`hjs>C6}ER8 zB|I*(=sue#7&HdgaO3#DBxB9j#5Q7r^FI%(Rreg3FIqt{tm>((YfLA4uzxqQgE|Q} z4#uUmKG1+BQ!LAjBsS40s-&+BsQ<$EtFQQs^!ORoO+6@uFM&$ht$dvc&av${Usj|a z;JRzgrDi3}VK(9d9Y?JZ11+SS28gNoPnKXkJsT(;P4+4>7$5~&EGAaXZR7RM&AIjyHj zRwYrA;MoF7p1uTFM27fm3zbs-mf}A;MobgPMG>X6F=PUMS0{}WV;2O-F&ap_3BI%X zb{mqn#=ddg%AHAg_}=J|X2PHO60=!RO@o0E7d`7$Pf>f>1a`*YGDxS5kNmj}$x-Q= zAlOg4yC%5{@z$jblxDvjfQ|oP1S1P~LFgxj#}N;MFzfhW-A@g7rGh;_5|bzvZC8Bo zXVz?^Rvzc$*EEde=)puz@LB-8ie=PG{vG*niq4(RGcwzvi19XA8l3m4HT^;R=bpOF z)YL^MYa=axm*jRD%nFp2wHL}vtUvq~cc_BXJswFlwoNe{-SpR+yR)|#Z}2aI!YSz9 z3SrTK96N7t{vefqqo4If2qZCK&#|i&TUa%*WO=j`lmBfJJ7gJ@bfcIt5)o9xa%wa~Ia6l|6qM`j9Jm6w+GY4l76f+|y2TkE*wXtGraegkbxQ zg2#M0B=?|RW5{BQ)AJlEpq<}@lI$u!Vc5isL{eF7>!&!--GrgQ;K2mTzj#Pv+odRG zk3Aox8&K$P6bNu;AEL{Rn63Eb0EnN8S~cw$eGrwt_}?z9Xmx*2`+a_W^j^+xsYtLz z6^TrkvFIj^4jxbNE92LxU8A}3X>Z*YxMU`z>t3o9BOinI?2ZQ9Ntm<0TL%oeeLV{- zMw*yT5Mb?rNfkbHRj}y9sw)T=sYbC!cRipg0(=;=sZ;H#Wx(5sid`AwyY9sK6JFJC zh~&Z4Ew_V-)Z1DITLfo|gmq#zD5<~n$&vKEKZ|qz;h`#gZialzG43473FDRMls*&| z&QSlUTI&}6Q={O=H4ZyCU`yr^_|6qTef!<0{FlYXmWb^cFA;Yc_K4{maaizc;00p} zai4;It~uxQyh`r-KJE^*BzY5T9{R&%yzFcRUVw2h77CDQ_nN=XC}&E1`880nj!>-iW>K zm9lW5u`&Kh1?iS#o~)MGS41C7h7*rEGp+eqzS4}u_(GD5PhF6W~Ctu z*kKGA6}uq%;s^blrsIKe-!K6Q#r)Wv*5AJHu=@5w$YV5?^9rw?<&wt=^#u{T9+gl* z02=tM?pcVvn3KE|JUlsmk1Yq7r?~!nM9wd-+q7bbWBSmOX0}NUyGK0wiJV8=KSD}4 z(>;-@KT?zylQ#6NiS=6st2ww2b3*N*Q7}?OIY`AXr)Ctc98Jf37#w_D67arT1mN$UqRR(n#8F zkO-W$kF38aO*F~VtbN;#_+z2bv6q?AhNGf`fxr0t|qN!}V z&2@s~Fnh*=F3z8)owRkW;?bv8ytJ7d6Vy}=G$wbNkaHwP>N)AZ8=rG&ssFC|q#8id zaNj;A=7VUJLt3Cdw=bi*3{1yWG3SFU_(evNMcic{W8^s&m)-C`nJV%MXZw99>>=?ba*Z(7?4?*dsMTts*%+rSoTV(U%*f%V@BmBC8#yncyB2Y zR*HGB+=KWv&3MOX{sttV^i0M>?w3wBV>jhAuk~Xn8+68KyD()6X}md~37Ixk7YDy( zy*^|gI{(Y%Wq zY?9{sR$25zRV7TiAgKt(7qultvyp7rr}u>A?gp8#iuQ~+b3^P#>`WIvtJFv*I}}qT zmyj2SxuRQn=1EgTWAm^g<(n@c}i_-q6xzc@K8sVU1Q9 zE@kgmV3$$e7UVu{TuHT3@=HyC=?WflDZ)zn&AzKz@Q-|y@49mP_>p*23gh$m%2w`x zg5jBuS6p#VXh_z!QiT=UU^-I&VOK}r%P$l~GHw~Q^(TfiEN%kskA7NaNoeI>Dw<&C zP&9bIXIJ|~^|X(Ir3pQB)I})k+j}>K{k5Z&$HZ5vCW!9007e@<+v-^&NK=KLEnO{t zg8Lb7K}=)NZjFtfCsV;}7f&H}vCfgZ#B?X^E@tf7Sk^2}&Q2!5oS1lT)pS|;Ts~A; z52yY(V7Qt;HU*mCYW4+R^mUC%yFfs2DRQ0+C!p7044Y8m?^qqA~+XSFt}FO9Cp zYYXhC1b559y#85uPx_A*tuaY>ob-uCBZ*W4i9ZampfeN$5|x_tv%(K5cM;a^t_<`% zhD?>UQ}U6g>UjI{Z(lj~TG4vn%!b)|{AWm*u{nO7XxhU6hP;>+?8}D5j<6kUCc)}@ zxiEdaam-f4;}6w|qcvAaG|k9zCmYDe`(pfYs7##=(cSl>kJZ;`xNA#c1%E2~nz7cx zj}QvFnkYD8)5Wpm1+(P7!+Ifnf|{F6yi|n{>DP}^wD~H97?b5g)(=0KUJm|1Vh;JU zFqA|h{ucLL^v)kC%hhls!6zPL@rQIIwPd#f4dH^GX0`;56D;OEb%J$b64|I0M8zWr zapAy8$)8(${sT7jcV5!DvSIe0?mQ<#XN-{|Zso()534WDX4De!1x}_L#HP{BZWbCX zCA%=ug^Sd&Qf;p%P2G}GaG2*0YcBT2<`#JmYLxdU`*BpgnJPCQK=?S`Cu(iI8)%}! zdwqVV3b2I2JiqJ?32)Y4a8GHalt44aEWddlWZDj@r<{V)g3JV>pSH%v&@8@w=`IL- zhW#j@1oFFU&I==WD@aG)yeS#wC6DAfc0Fy9Sgp`3M~UgqR6aHCs1zhDPp|9S{4pj5 zD2SI9F0Ik{Un45A7_JdGr1~^$BR@vcB zK07RZ3&4XV_$b_227Umc5PMqPSv<4KHS{;LK==@w5IU%Xw zBRI`n*EZf(d)Qx;R&*irJ}}jo68ei(T|J1Mk?EO;Pvo##u9lrf*mP7vg&&!h5jn38 zuM8n){%Fe+g_^d@Mut0|ueq*yhNq-E>b7`c^L{#MlN}?`?Fu+8+_xj$q=G4yfvHmCTrXYAKu$RN*r&(3 zCk7TRFc|7)3TLd?v`4DzrYVgY+hPV`0+}(aNliu@&qlK85xSC1A5tCejd@E8%4&pDF(5O7!x?Bp4et&IaEGdi8rT4VI zVXGIe9lTtzeth>*v|;=egx(TRo2&4iu=fl z2__g-Re4taDd~70n}eBW9~`EwucLOl1hBB_vD1i;)NI3hovmd}2z#q^nMLc&0gnfy z;@?WC#Vmer>J4{b{E8Ay`J2uf1ei|&sdhjWU^!V8{J4um)jnh%y=yO?Ib7yGKE-o$ z&h0m%WkSdS`5bJ9a3-ErIWjRb_xHm@dDR-Kh=6}6_n(gkbl7=qLcjMDK(dqEqcaPG z*tC-%tfl0s{;v3zp5xWaE68L{R7BE)C4$p&Sj?arJ;j&oc_nqxc%r{HIL*23K3GtC zH}uPD>X37hsnxLuStO6owTIlk1^(PN?)J!%r5lu^3{S35uIf1dR(nkj(rBg{;hlxt zu(8mNqhQvYurMjNOe|8OO`Vx_Qv_0_>JO=*L-`cwB(CiZ#!pfBWL$G-ZJ$UAEmxD@Z*!j8w43F_65GqavIae*2P>ZckhqD^wdyE;fF$OYtjln7mp(;-t)%EoH zW8GRLzmg;t$Vrg<9(=5xq)5LhnVnpgrC-z%OeL#u zu}|^Jo6cNOErUH<44`dt@9`#p{`uF{;kAn;Cm#e$i(|gPOc=XDIjH~MuPO~2IB z*5pefi>#mdMI5f7rVwp+;L_QXn#Xe~3-6E~dcfvu-pdsFV&36BWtwD^zi_U7nF9c@Kr^t{N8+uO2 z2N=KaRNm|Zq3gAbPsJ7X5v$r6U;asF!?(BTs-Kf>=4<-)mi||uY$)%#n!#wmVS5)w&CYIb zV+_^%WsmDk;R(Hu+D9tY4M)h6oq8y?YB*rwEY`p}T0eW~x5?dgQmXkqD}_$Q%W^qs ze+l`N=hO1I4;fF&@rK_*pWdJJ8G<1P;`~f3;wJ<3y0A&u7F>;8KLht^Pu{K_{ppTj zFqxf~wlzLOl3+F76W}`*@EWtt_D;*{PgYcKgyrdyw5K>-LQ#8{B4ow%1iOxvLGYv4 zF?8|s8s5@FW4DJrX>f@AveDd;O98aP{XU>tY%5V4nW>Z^SDA4YQMBrsh1TRO;Cx-o z;)&&NFD-pLO=Z*LM#%nma5>Kl8>D~pZAS&{7tG>#b~ip0L;lF-1uJ$8^q9F|+h5V$ zszsRAwgz2BgTd`>T=PT5@;Uu#Awj5LVh@j>f0ojx&G~4lJ377_58E00P^;|;@RLSG zPLixF#~Xl5xUp?XTkY9=4g9rE2UZrXzHK}{KgK!$=AKre5j4pb_FC_}>k^kjij|pzOzzExmOgiY16Pr_knd>bUarTyRWo`VojkK;w!`o%-( zzFisjitnJv98-%*D@A;{FkA4ZRr@f~_sq(rVHGdiB@%bTHu*uHJ9a=ry0kYVCYFs zTyvILe}LUtjj!nPmedzQ{@8v|rw{Ndln>h#EFUDYf8mx9uM7t_`yWaznM=`j-{sF) zlJ)Zx7m#o4`W@tI zWuK+qv`i>@t$h0s{bov!QvY?Y@K?t&A8-amB-7biUAz*m>%P7E@zt^@g{*S%3^goejq*Q@MX*c6X`c!F`#^$HG z+-9cDP94f}uS&z$9#@5y!cX-(cwefqTr?{{^%mt(_uurg zseesMF2%S(W*iPFV{7PRPmV%SC^lGK>!wf^0kKP}t4dUpoSzoOO%9Ov9_UwwSF~K!ZKId9ASF}c zz6HXre!Oc3*ou8GGA)PWBgUGWq(ah}j`wm4W$&-|ZQ{;k!?v3)OW)UMhc9$Za*Dh*M^)My}W-U8Npxm9;P@7`ea>5++crCL3Vm2^Rv!RE@w_G z_`F$^Lczz!(pv-u=Z)p|sQ{+b{d?EN-0~ijbiPEV-zU^p4OtZAO&jbVx+N3g}#tHb-aLNB|?r-k!T5_>x`he>3p28Ry*b5E*Q0!4HT8a z+nCcvB?K%6q)}9y_n@I)Lm&EjX>@2)w-N%(GQc%)Z{KW-Ghgy{L<~=$?=<0QBqh9W zkPg1-TKm9*X)6&2FG5Z!T5#4}t6q{rJFL-vbeqEu{QtM>yakVn!}h*_pAz zoj!bvHf1S&3`(dB{30zmWw+zK$Nk|to`ed4#%M=iSo-l~#4QnZ3iCmce21p81h4(A+L6Un4cS5zH|`Nqp`Lusm%wP zL8xzuHSHxuur9I~*KK6i(wA6?zkMaIJT@F^Gi{AO2|juuNg>-I7yFBzD$nZTwm{Fl z)P=2`#l*y0btapp8)rXUij+{`G#ejPDLE`(YS`Ly#^!Q%$$S_dt{QAoIiIWKj8UV} zRI}X07U1I_h_KtVe$PJoM97&wlFScS< z@;u(2_4Fu3In=jf!@ncVgV&gOvnQwnUdMd@v#l$lhPnpxZ%uupjXlq>ep zA$|<$PRT?_BcmB1J@{OKP`Ev;zXGMhP#Qv+PYoZT;f5@S3U~?SEq=hI`g3Cva!tg3 z$&o;01lu*WmDj%^om!jNC z;B)}2KEc;NWYyvI3GpB0w|+!i?K@a^SkFoH zIZtj{5|bvspcdxpN3py+kKgS)u8eCvEbF98=9QA{`>Xy?7&E0x`SU#WkMb0)sf995Jo9w`%UIP_%6v)i8FR_ddUZ+eibX*6F{6(;I@o)ViI+tqYp-Fhh+(0+z_7!8Qf zi7S*pw+7Oio=we{Kczc@y#f)D5q!!qV1pb-^U&(4&69eF!^8b<|82ypewxl;qI(wm z?a6__yF^rcU87T^g7Q{kcO4~PTWzg`&K|r}d}(QAIn$xVjvzuuID)@4_UvbcrG^_( z-XQ8-sEVUog(3Fj+{v*ACth;Ytmm^jZ^RX%O7Z;;%J|2H9+LkZ;MSSCwNFc;$Ix&! zX#V2K4QJj)eIwPwgvL$h{bFoN^Ntx@t589Ik0kwEq7F}CG8HvVIDbpGp0Dc8{mIor zh#~K>)#f)JPD*0AugZs%=-S|FQPj8KxZ2N6Y@ZK)Tg5WQ4X0Fpy&>sq zW1AbnNZwKj?WjMJ1B`kW0#qyNvE|c6Y8)RtId|BoDpoKsYH<^>|$L>D{>C6 z7mX7X?+Y%QP^o>wBhb}pM@v7Sx7v5u2Xgj^&&)hK{OBJG3ItPhQ;s-Ggy;7UO10_^ zM$uul1A6$mGN=Fkt^5V|jz8g10631liEASs|m4=FQ@)SE?wJ6od%z$|7B;^vt1?|Z0dFO=`x2Sc{rwTKx)Sd%iRg$zmRsrnl(nb?|8EK+l;K9?-c0e z%Aa)xs^swVLG+Um_&Gh*& z0Uz{+Bj;Ng<0i5LYD7;b23&4G7ysyoi${oChy5oiLOA|7njpVI)F7J)yhqVH2N;qr zvh&|>RC9$V7vAi#Amyirg^=+@!j~F#dyHwr{h3UoZ+(yt^lR)XipTz(ySMJ~{WpO% z`GA5@|3`B8PH5g1>Zex3uM2!-YHOP?x8u6!g^)Kyt%*z=8=?N2giIIrAUq-)pfD_% zt-H`^OL2e&y`K1;T;gAISZ&H%!*NyqYVB#4ZOb~L3nlL(-oN^=L?bn_k8=!xRT&04 zia!-L8XXL{(>@`=KTOn2CoSh5bp+ILS1}m5`kif;LXa^o_UjWnI?(%>;xK|~&S15m zAIE~;ABm|XEY?fN!}n`xT7CuD!Z6gpi~v&NJC9!061>b#>rGD|W|i$PppRkF+;F5u z_xfjQUa44Yf;{Xvqmzgc$jBWyD!2X$YgWGLog!&BqcgtlV=#0iKe_e{I1o-T5YYGO zV*H)T6lOXkDKBDs^seU0*h}Q(r@!WKz)mzm*e$U#;imE%4J~}{#4|Hy@G6%vtQOH( zqn9(_Q=?j(5u^4tGHQ=<1a#3i#On5^8mwSZnsBQgmvrE~@-JW}5EZK**w0%lQ}FqC zP~!k0bHw58_uz^5+VBQ04$hGxwii@cVc*0Tk?geFJ(R1eVVRz$`sVNL#P86$%AMUn)2JVzQ8P2dm?a&o*F@5$v z4?c+6=GAhy+`^Z)Eh$9QD3B<2>gV(z$!T7Elu03x`U*S1q@ee5HUgMR; zTPt6Ph2u3tI1MhOM&hHcg)+v(N8`TLW$^+?V)&Rl$4WJk?- zcpg4@mVi>8t^iNp&E6N1`3YYT$W*GW`Wc_?Jmh?hs2D%YVHL@_QjFiCnK*C#s|2r^ zjr_A4D#gcMrP@rG_=5jZd@s~y`ztq0vOAPN@K^7}aLbZ6;oUgK@+}ye@qV1;H_!N5@YD~a$h%cq z@zq(CSn|_0ybSaHx@1*5p3y>rI*6hJ?_=(m{ldNz|Nq!D(f?u7xOZId+wW%K21VDa z#H4d@CeCbQQ>uA5n@by>MUe%#sH^;jZ6u#@f~KvyhDOD>!M4%+!aXH8t`#2vng?HS zXM~~|bCMN8>kMwHi@p9*(kyO}h2q2mp2uC^(Sk$IE#ibA z0q^MjC0r)^iwa`;Rh)GRkoaV@j$_;}Jjxo`#D)JLW|nN(#{DGxaWZOie;>T~gG*}u_BuKI2sb!KK-kiDf@^*^#JVbThWpn(3=26CK;DnF zu31eHLekM!D{`EOp$nOR*y?ylAeOx7kfIY(C_dXDy!tOWG$z-T=psY~z2bi?Q5Z%8 zNi}8P?WUuLnBlr(yC)2g+`arH&eQYISZN9THj@b|e$iG~>%;;z$Yk2rS+POk7&jSt zAP01DkdjfY_X4zVT5tbe>k{<%eRg<0$z_O}D1PmN9}i^XMni?;xbFhiXmtpK$$xj)p?Rf6oOvVF6$+mMMl&&MfURmd=C)h;6OE_9ld zXY-0$9m-lGYCQR+0dY&uITqj5g7#v+dG}9fLzWlyw<%+Eq2;$9t)=Ysq1v8LX^-s< zp~9%S&rENPA+=`qd!1XRP|)>z%T1^`G(v-GxRY)Hsm%;4wq3S@bTtlh>l&<~DzY`z zm6x_qrhXo^q>CK{;bMq-f*hcQ$<~)QnjE3B_02Cd(oT?blHj$f4QHrx-hh3^#1(QG z8DSb-b%WmB^OX@x_JDY8l+8Xr@`P0KMV_kLc|(7Ei)FPUoxK^K&W^GF)8Oj;M-;#*Mr#zTtiYMM}n2ihhiR3Y-?l`jy{8$)wy=Y_tiG z8E0^0_faBrk}F$GO8XAVdj4FST3s&2+7=*E z$Nv!uu^oQ3-j@Z9q`&`8o}L3eGWIlEjLU;kB(CX1mlQy4k7;$)j|-vED=X&uw~C?J zACFhCs1hh9Yc6r0;0turTIBmF`YUuO)REJTl|eMa=imHhtblY6^Z%?+S3!WZV4zr} z29kXGus-y8Ep#bas^|219aPB{)^9mj5AnRn~F z{gs~kkz^2>rlg2gC?A4CrzOR7!bYGb_45NkLBF7yPQj*{tZ~TkvylPk?j$7eZu^~2 z*)*iv(-=TNGYfU>P71vAnS*xql6|hxEI@7*tIh(|i_lVzEcu_%WoSC;XQ_zqDkP?N zuRkq$9r|@3@kVEQ6KeK1J!#6@hQd<6@y(y?K=GDs&JXPNpxKitGX0%>Xd`LdcqZ`x zQv2#mX=ZTxedJab2r@sL+5ielt#(wWY#a;5ipOW1nRIy8aT}t?;Hlfc-L@di#m~Z+jlN`j27$O#c;lCA~IR z#PBM7jpZui(g`2@Wot+XuDu3FvOEY2$+!-8a=LM77YM-tJZIE+3)YTuHO?r5 z;49smlak#CZ2x4IZG{Yjn<>A_l_Rq7&zsSO$)R#E6XmZarEPhbHBh26(peFX4RIE- zqfv&JCy%z*TW`Y*enVdOvsB@(7d;x+vhKqCK90BN+SOnpre9{C!ZqM2hiLZ3Hcfc` z=t{80MQyl0lSiK>TnE-d(w_7&>A~222ZMK2`fz%F+shDNLwN5a7=CE0IK}B~ z3VQ^8BOXdKgL{f;JY4>o!wE=9^GD1Qwzu`^{rJubMyCbmZ_wMo#p&z>SJQ0a={vNPGYK`Flt0#;QU?CSeyx=#VW;LACp2AnA3Dd79Jcsi_t*QD; zePEq)hcWce{NSUZE?R<8f4D;WM8EA{04$d>|B=%+2=1Buc&B+W7?wY|B32#v3f7mI zwr2pZ;o}H(I&#i17^~z|qu_{uMH&Z73ehO|SxofkR8urO=`quMiRcYHZUdEm(T#-< z_B_V*8{**j8xJq7s>H)b?42Qwn+Y(Piph2I(j>Ux&gK>Rgm*A;GvoTXv=4C3XySN6 zcPbplr5vQslnxt@5|>EoXTWzwue@U@&4j=EbOwIs%Yyqzyle%_vtjw;u5NdmT-dwq z$@aKJK73)cZo8lN6Z|D*F7z1p8Fr+bJ$wTf!=F^$NyJ@B;97PILW1w5@NI(V^v26y z;cFZG4iD15!OqN_NvB9TeBsJ>_1dEf__f&*0rPMboK;yUkUUZY=eCO*Um(Q8x82OU zdh_bw0jXn_>XmxfAgpGDR<04&l+P7J(x0&Wv_7Zl&rVpbdq8-KvIa=>U_QBJQatum`{cye6?XVX< zgRmplOG4b0VVI+f>f6rp2)rNvCse)f7i=_jBl+g+I82x=bGd_a3YH=rd2*|M8fLk< zle$4Y3zunZa}c@B!Ir_Q3+cb-;V@YKV%cmnlP^F_1uQ(@62b({n&vEqGXNh1ovQmmK3@-#rrVf_p5DUx(D#T=Eqwa z7Y^Z{CN9bo$n#K?`_ z&!#?(64|xFJ#^KlMxKA~`|&HE77>H~{d$l{ zkKD_vl$$`69KMaQNHS7L5eRH%04bq+~q*<89AN$d$(zAMes$Mmmzj+<*G=Ae0rSse|;qh(z+n)89X?BDg`yV(nCZ z$xLlj1u29q8~m)SvqF$$)*7mte*m(1)A5YfN){Qm zQ9gILDu?V;I|b>+Dm6s>_-0jUE~1o7)$4` zj)-3KMV784wuY2xh>3)KGMPJ=?fMJ9dX0!7lo{V0MVu zh$`&`F9(E;>iw(ac}GN9UrcW?)Cu`3<=HADd&~c3wlY(CnV(& z!s!c&knn_2oIP6!g1%^P%Klo4+|an-((m~dQDexy{?4Wh;bJ_dAquEK>U4S%9DA#f z`YpBOT!Zh3R{7YywNlubxj>){j=X$$fhhs4y2 zwINo2&N(&2{6q}QM1GxJ??O^2Up%t+??J+k&DX-|`jGIp;)`63{fOS53p`1wgGiZ! z+FW1mFw#|3U3xG(icC-O`p0UGBT^xoqnW3ZNb{!i;GMl`q@N+h<3`vlqF7Ozj8mCM zYW#vE|A;Oka(XwVx!9^8@CX<(#hMo_ji!n?0ntZ zXS>KyUD3UjrhTM{>82oe`2lj#SxxTA&>@oOts=>H?F31C8*#s*_%E_6w2Lpt5upA_ zvb0U#3DMwIZG%v2VpO2h-Qu=5395c*?jCxb43&#ZS{BlzKwI3xq^$F)&^jls)E_un zlmRd(WChZr<-YVaC6wn;CLZ^lXWtl6jflo++$(1E=6DI|<3LvQE515mu8DHl;UqQ4$w<=iM4_XMLU;6X)p^?qJmyMk)e&eOmAeih~3&|F)p|bFeM&<=rnOtB({EXoml%xJ-F_@k*^Z+4hl6LFEf^ZksG{^oSq`=0lSofo zl}EEL4Ajk*D57ch8Y;ijlu?^ZofNTZ6_g`Fof12D7o}NIRbuv2LtCoMC7&m0poI2J z=|AYTP~s5o1gl(a^v~N4QEDq)RJ$x}B3E7?jlVt?ZFbKP9b*cH;scG*w7SOFKP#rF z(3h7J3Y|A zK(qVQA3agw)0EDqY2Ii(k;Ns7=4Ys?`BBF-s}K72UO-yuT|ZPg=6f?|jz5}J9VfuW z6^I(g?rgLd2ciC#bR_RvzeI&8?2K;WLeOQ_X#Hu)P;>;Tm!#7VN57~nr{-oxq7NCo z@6AL;qrxZRncDL)=#XmO+?sYQS~f%T@zqoun(R|UapiS9`q|RR`j=KB8tQu3zlJ8G zD$&lz73%NNt^)G%)>kQLtzDAGyn7n@LnG+Q?V5B{et4TEnllr9As@xA9rqFassB_P zT+2pDI}9c`MsrZ9_@?0>xp`>(7fluEqykie9w7+)REP%L|Lpi{z6iCM^pR2EDM3lJ z-kv|PEkz41UQ-;b|AKOMh`hZj`3)VF*;ZG>m!Sl{VSe@26=<=DBNHKC6)IdBzm{^Y z2IV!-?}dqKQHz_^LQ3>?==6BMT8?Nv+B}A?30O6t(<`Pn-*X$$Q=%F&1EMCB=6u-e z1LtPcQqbzjyWcISqr$4YT1*?-_iW0JQ}HKC;y&ugaK01mavM#RJ?cUqhilACzUV<+ zdtr?`5xuCav+bov1%0SZq2f~ONI%+5z4d8|c@Rx}NfIY)HH1F8ZE1I{ei#+x`h0?j zj-t2THfr@1|3dqe?xY~{QUR$1nMc3yIPzwg?_v8T#fqcG#W+AW>20mgK}FY z)+r~=qAxs)BFmEIPytSJAeuRkib!RPz4*3(R!3xG8a<0B2@ef}!rl_<`SCZ`2-gaF z+V{Zkk@_n7Mv>i7{?!_)?b&MV_G2Brv%|f#PO^zUCoY0qRJTy>{ZlsO+uP{f{nBT5 z*S1jtIDkhdVh0Vn^NdDWco!8;seQKGwu{oq@nC%xd+6i!tv2VqJyc}m!T8zpebi{u zV$1JzA5Hb9lc;n0jjn6I6jdDfjVfdt6KL@tphb8|`f-~BH2y`aQ*ihJ`e)7T!`p-d z)M>z_Z71LWT|Ryu!lZhDdIqjuaZjRaelrLRaq@kj$gZ9IG;3)nn*|tFANGHrmn9 ztlN#o4LP;PV-5b>ZG*M;ZuZ)T9~Np&*LRiW=C^C}?{}q|{rFS+al^iTI^bWee1wFH zJI6Wv;SuS>oI`Rvc|QM^sU0oe?rYEmH|RXRUZ$<-ZW1#-Er8s_QH%a`yV06*XNol-YS5!@E;4M6-ZtAY8EsT%c9gHjr19(Jun=RiE6HGwnY& zHSu@ZU1u{UweUX+ZFEm_bnwYfq&p(w_3&Ze5~ipN4e&U-MjO*jBm5&DapyQwQ~Ut& zmB^F7W_VfrdGSzt3;f+WPd&A5OT6&spZ8VXTH{F)74$wB+2TL=FkX5iXNR{=@E%W8 zcfe7wZg`c-Ae|as4}43` z{FSQVC-~XPb%X7EFFb3|OVv+lPw~FxKln_mpX1Hh|GB>T>x1vZ#lQUf)erB1 z7=UMNDA2R84aD=tnk&Z52jN+N^>eMic!{5xzn6JgG6XNTh}4i%gyP4lg+c6D82g;$)|q;(Rew9%^?|+82p0{y%IwPCAb@ zeAu?4Csf&;zudmUp7w;ma@Vn9q|HCaNY%N5&ZF4&g|2G_GjHktFma#nR6hV4z{wV} zXIAzsqeI3yvMI7NLr92Fwq(!9%rCOCGqcWj9(z^x&MGt6D@E}z(Fu>#btJ}!=1z=M194(Iz2R0n|MA5gAfcgMmSKU@L z(0tkZW=%pIzRM1D5TUU|THBip#56LM+-pq*lDs=BzfYtCk1CW87NxU*haCGq zqDQlV9pzQZE1zYXM3VXOd# zI86lR>8pTL#-^7Ap0xmn9Sb3Z^CJ*;{2+#0sS)66kj0I?YX)Q$S!<>4000#~R=OAbG%Ho5g@txW*j`z+V+ zMO+xXL`w2|V^I{;*Vs3=+mHa0jgQi{y38}9vLFZu| zKQP7y{H#T=ul)J|WE}e5D09aV{I34%b>2fK&?sj_B@A)_D@wd%Us*o}-*5qz9(O#z zgTD>yJ#V}~#m-P2K)?sA2Q`b^<)48Y!F*g=F@7N3H*&VgtAXJAut2UIk`R#jkL6Da z%`ouBncI(r!*I}k!=cUZUnB?#`t7=D#ei+(LwQ1DaiDvegfyuNF^AE>B zrJFBLU8*L))vF$V?kP@#s@f*auNY=Q0gJrY6Wjv0PqFI(tbGG5V9m1^OG{u<+FyQe zj#ZH4&McE_`Z~DMUT~9 z6`>-w2Nl;fm7&MBeUBeHV4;H{iD>gpH7M|JT~+$DI^>cy|2p=cCX{3K;g^?#4pfyr zD5^}U4~eJPL=^nJ3ms-YPGu1=g3j)wUR*yhhSYKuhSn}jp`FWJrfeD(P(YvKH_cv4 zC_nA#-wzo!5Qf1g)42Wt)c3BbmVwC;8u`><8g=myl4;TM`FqU;T6_97^-Yv3Bt$%T z#473zX$Y8E5wm+jaJ8K6q?9-06gVX+b?yUEhQ?=$TzU@e*vn%AasE(uKdX)p5(vFS z^0`^VLm=kTa2x6LFzE7LPhri!aOnF9zASqw62e5a$q4?7h6d6dTrj3_kb&im67$Uj zDBRh^vh~X==p>h_EcoIzRMi>AqtKHEb*YNqMhF?uX$!G<_f8hn>#b+;(Dpy5w}A`3 zOP33=9cUb!T+W9ieQc%1Y73z^tZW}~vF{;*dH{!6S_!0>-xgIjQ3iR>+-rGdRSB6i zE`A#osD?O?qD4hj>L8d?hds*tBP8lz*M)AmLe($HKIWP(tXe*;UMU=$C@?i`&f0(0gpUJh|{H#7=t~f8xCkeVMI@T^&7j8TCk(%_u75P#NPfm@xMceLO(9xpye2H!5@osT=@+>`sO@} zt2~7sXtJi-CH{k6`92bQWJd%?D|zz9^IwAh49w5mVI_sP4BeT3s*%Gn-;KB>$|+%m zes*pDdzWFFjwI4$H9FW&&-A3UogS{>X;}LBnh{QTPwf2YH8X5Hw4}q)!wO51ytiw- z#Q`gsiP^U8alz@eJdxoCJn&PUQ~EFge%LdZvEtR)HF)XE&WRPd5G`fdQ@+(THHl8s>XJ5z6&Elgnd&j(8BMrJU_3TAaL!UFzp z$TUW3&l0vQ3OU^_vVmRXzaM7gKY;%!Uil+>;sC#lT{Gt&cnIIGr%bs$=?w3=U*JWsCj0)Ai0Li-vM4)Zq}E?@f_0iVGlE!~GvFm?AY0eXR0 z_`yq&K-JWESj3ew2&u3uYJEzDAFAEei7iftwWi3sqCRHAv;R4L zkUxC`n|bx7;iBKddxZq+EBD{QRqvg!KZ6V4_-U`fqPasS{EoY`dp@HZ-U~4u zOQ7t7>kQ`Q$a+4()vwrn*2f3ng)4D}j|+z3{NNjNnsK9WUrFoM>z!# z7;rHM)z82wOq((i6>~6086>0rXA#z$ViQxI`VK$05(!=3ScVHj{u6nmyat9KsL}Hij(!7-kXX3D%eR4QI1n zealaA2D^NVTM|<~hieh1XPg^E2nTaHg4w!+RPWk;XqO^G?y)9|c{NiYlC+wCR+Ffa z-3PR1KOkB}%qC3(D5FOPQeKs8mNO!0t3c=|Z zfi@90kY$I@Rn`fz2*(HR>lUN($jMyWr+8-tBr)_;x{{s}a?hQ5YdKLBk)yon_8~_N z870i;?tM^4i0<2__y5yG?9?f1Kj!Kn&m*L(RZ{d3onpFyTi*C-;$UUYjNPzt)KJx10-drw7PSB8;5pRY&B|CduJc%L)17 zo*=E%;(}y3KJ3=^e~eVvMg4V6^+4WZ#FCWgpCZ2Rev5iV_#miboZM^p8M09Lcys~l zkBAtB>y0o6A;KYJL(ICNNIq$=9klWiS$I~kBJ(!_>80A-Ro06}4DNCiOE1MDLnb+4 z3&RP>QoM8bBfezBDq8tZrB^EQo<47SBQYJJxm_Q3yp@Ugohr@F|udey-!F*$($Aa#{oouvGL2Nu3_Z8)R5!c-Y7C| z6v05`GJ(_uQeI0GpGI;haO@;^XOZfg+$Q%X77%}p?Wt|I?+ByK*Av&OWu)$X2vn@F zhWKR=mFlu@ARgI~0xFhU$N~)y$13q25^`1LKR4zBWc_(}`%8}_qzH6d`g`RB`E?j2 zJbn2TIoeK%zUTfA$s5Y4pnOM!&i=WY(A<6r6>Hz(!by{%ZC()w`jY}hzif~C5?@Bo z%c!32yr4q|nI8|l!!V$(o&A3=J!L|Nb5Ck~_E^wJ$mcvAP^po+Ep!yC~RO@QnJL*0)RO64r*W%b4-se}X>ymb`j*{VB?saqnm7kq?^tDbADj=5y5j=DZsbDP4}nw3u8ak*GlzA+k?9 z*7c|!&SBfgq5-`#cfQAy(u96C(x7yv!lN~&C4#?)K(u79$@|Su1bsF<#1taahIW0n zxpc_ifx?zF+Rb;nP-glV@F~!PI`@Ssr9A0JqpsXNGW7X^K5ov%PY(^DeF+MEV*MlN z=am1}iI>LEqBHWvM#V{#l$nN4a(o&+=~OUeMP^Y6X6(Y=?*+8Z&{Cg4|2vvdmsV@S0ovB? zbRR4|LMeZ)-%$DY3q?x$+cV$%K^5!2S$_=ti<(a#$K9y9KxthMk`!}^33>K{4I^nJ zgwn&ZBToVu!C-kQKSP+3(AB7b-W#SSi2tH|eY=B}@b|9YgRuyDLcmb5guEdmA)bfa zC{v%Apf}SyR1v~TuKGM34OzqAE^302*o3U z4M4>sf^`z=M?~U4coSnpi?x#dz%_IXb&-@dxbzjjpN)xGAQ|Y#}_1Y|KfV{P;kV`IB0pv+?pee%UJEpk= zoRnbOcM$w1qW8ENkiEA%a@XWZxXN< zIpa*{*E_JW?9noQ#@*N)GpI}XM<4bC4G>x-8Tj9PU46g54rB9&Qin(q#;~W&@noFY zli2hVMVB!ux3T=Uz8353?qTtF&F6RS9%BFS+YyERKE`@VMDf_3{l=R1YCa`#_=~NK@y@-% zL4-5f)^oJLUBXq9yji~TffTnEs-&#tNP+u#qBg$bNsZf~J!t(pLyKEH|4Lw)rpMia zAvFG+5w{n^`B5ZY`1XQm@r;k`=@?g+b~#WBo3-v>Z*G zCV_bL(}^~2kxn+EG+Pf>Nm5eJ7;zUDvsIj23E#u5SFrldmK)=~1YL<0DK^EK`=)Hx zkDKFKeg-lAxNV8EnPv!T-?PRY8<4NHuiN3I@tz;avK?^eCp<5kT^`~T_16e;&dxXv zxA9of>_@nQZRtyuByPA1B`H_6IuG3Pd{x|^0xz6!@4ayO9dF#^%o6Ebvo8)@%Q%?& z@Epe_YHe?;?$$o91Gc>|5IQ@nck|9*Pjx9z@*=V-Y> z+aG9z{~LB-c`$2&PsaHL>1Lbb|A~%Dzrb7KZ~JuLW4~jIUkw(KwU%+fS1r>+W$_R3 z)W0wH-od!wg&96w{;2&JFYbB{llFPwvw9C)LO(slvxJJr-n-$8HxawP@@42bK3hXX zd3Y!QA5)=6D~%1o=hNGVHHp8(i$Q6X;8+Bnlv$o7&N&+I^7Z|VWYajjH0>e&a*VU??^Y}kE0Cy?!4cY^XMD=)&F9Pzq04x+lTc+lj2KIYkuX6^uTcE#57|KU=lYP7bTY)A`%0p9$9Bm90O|IaSo-x$EkE{HYu9 zCIbpANuAC3-O!~`qHh3RpL?kM#1p{}xL!N$#kS!ynNz&prgq>jw{t!{z;xs3Z(cbc zQSHOu3b>lP(eN2x&J&&ZwR8wSQ(iZx$~=nq-a7;{%g6EG^Ol8E|C_?QKWuyCd-N5r zk0Tzn{j`9$VbaX+&-#vk1NgYEu&m(i=hR2xy)`^J^TGXVazF70*nk0Ga~uDzZmcw$ zWgp*~KA5Ewd5CXUDy!+${)G>7N>UAe`Umeja9#c-^*{XD)`aGt3?g7#f~THi`x1~4 z>J-nJO9p%hJ#XKxqy%CcX60o?XaEbp-EIX2dca=)igt|!Bd~2r>BX?a3|zebG_bnF z1~~i^6xLPe0;>CD7bUoO0kOMzh(RkqAZsg;K&&PR(4AcrD~LWct_%c%*wP@kQ$+fbHW5 zyZe?Kp!h|T$v)8&IC}ayG-BKvAWsocXOwydBqejpkj(f2!>U|mUzY;`A&kqpf zPXk=;{l}_%B@^JUy7jGk@(u9hhsL55Q4WA7oSUn^%>%TK)_C4c7XpKW#T<}iF`z5b zNjyJV3e?7=MBpYX0G={~)?$rnV5ZOHMi+BE(5Yx|DSNj87z^t3@*ime8moNkuh-#$ zUgZW?uYCv*mhQl>mJ$HzvQuhCuXdo~!{5q{(N3UTDC9S!I2}lJJ?L{D4+Dt!Q#GJ&@uKx{e%QaU17E&I%NI4Tff)OV_i~^hXlAVY%`-v--1zxksJ=oRWMy8O%TSgE zO{ABLqy%Nar1$s3GoQ+Vm8g0kFBJx?dvqh^+L0i01nu6D; zOm5D7G6$!({yX=twFJ!(vhObrZ9t4)QgVHnJ@^E_{X%)u5e)m=%K1vq8Js0@V-7ue z1YQ#;ZSuNs0|j|kc6mKL!K{QtryphBAg6d%qTt$cx?XF@9f z?Yki$4OezS-S;q1^hM~YQ$PerF^v3*X^#T)VpLVn@5h3{-df2unhBu7nqfbf{tEmZ z`lj7RBo&-^U*q~jBONrmU?WQ_%>--HQp!gyvcYkJIQq~r2c*3%G2S+u2daIuPCJ+` z1aD41Rs^Vu!Nkj)RQr*oV8zp1tDgoH;H5x!cYePr5HyQFaU-n*1^lSSUqyce3G+Uo zSNj`5=UI%Hon;Hi#m_)FrwoFnthXfX!VvI528TB>Ya2K-pnt3))&a^h88&2vcY(v& zIqr~PFW9ruUj2;s6F4s)_MkUp02G^^w5Kl~2BU&2Ky1$_xFFfWW^{c5TxfJ&5nP`F zH}jYqk&~~W>E~U0>n97KW7mC~Nzw0M{zW$Xx%Dzwl4zq?H@*rU9i}t?&i?`WpEC(x z?%D)X-!s-a-P;ABk&WP!vVG7;^sNo|`XTsO%d67N`xkiMa9(@z&L5EG73~;F^j~oB z<*>UpBN62G->}YI?n}`8)$_<;cT#9ZOZ}J7B?`#CFQ2t;n+h6AY%tXv=i<{G}q4$H(0rKK#PMM7_FWh2H%FHzb(8ftkHm!UDsM? zgtehdy=NlNc6FiX66JIohP#l3So2hC_C09(R`LC}y~fb*o~+ibOJ>m4(Xi`Kj0ME@ z7MFO_$_lDevoVm3vxP93m~Y9n4$yU-?z$QJhmg?>!%U@_GxYL!Zk*x!BZxd4Q59`< zg9`X6okadTfkMn*o$DHSLk=2~s?wIeP@3TMKyt}*h`%=R$vdTh|6Mn_-ERbfArDG1 z!jjtyh`ayy%bZu?&=vdKBD;b}$iK1lqVy;lny=er8O)4>E{yzlR1*@R{+T9&|Dn#?9g)f22e4_Qyu{I$2N@Mc#YggKUWYl&#O=FbA?T=O3Oo%ZDiX&7$5h z6hS}l(QPtfilLf&8MjgkN}&J>jl@BV3P=dNtXJYv1%()V_$WG63w^zsAG2fg5!&Ym z3bqp)q3@kLYB5ADkcbe2drdb0S!ub5M6bY5y#RIYv&XFvLWYfyu4{*eALHtse|AF7 zE3I1eUOf=cA_je?(GP7dv^tUaeSvcQgA27Oh9Q-_#{!LXqmaV-F2T@p9I7py_?NRa z2`MhOYRgZ|KpNOS`je~k& z_J{w3T-1}7IhMB}R6Ij4>gyiFwfRu_)uls-kjNLl|>gslo;c8!U|sUuPxZgp2;pl0SCkhUs{yXk-ic;Bq6y zs)oU*Wy!m)M6UwsuE;F^0r79H*n;nEjm+9jLLaK&=Zznv~uc->B@ikQR$4nJaj|F_W# z#@v_OAbRctzb}c=Z=HSy7x`k?+tU5vdEV#7)U`n{%j8mOz=5Ox>=-aR5$!u_|sO-e|_+Za_I{yzAvy!h{Ln%EW@y@lgy;8%P1^e zyg3qec>?BHy3e4Vgimo@$IE29u=Ag`@`x8zSZ$r^o`L8{LU?9EVShmu9PN% z&U4RUFKZ8l;{;*^tJW&8$w7)N?Pe}&Qd1yuey^;!+_i% z*zuujOvv*YRY`_XR)li+R!5WsCz4p&@Wq;h2cbUawz`JlN4(>G`({u95Sh(Gw+4#H{IE>PWXYV*lV8DUsSUD+f;7QY;F2U<(JSP(@QEM%E)~fo z^{^rN9I{b^6dv2kRNJn=HFj8BP-Okp*0|YG;mHZ zf3iZ54I;9jCbc%CkRb=;df$Q6Jbc}4Yuk;Gj0b+T4DCZCd?rUe(|159so$AjN9;ov!zj%*5o(4SviJJCNN&_I%g2p<+(HxJrkr zdnbI$RA4|oLStQB>X=ZMMT><$;jCymp=5Xu;6Te|9_UuOaHC%2mW*wm`A`jKiK)aJ z0_ami1>E4K5Sn#cGVm>x7^-5Fh%3pKL?5*D3U`^@KtI`@OGUhwMPXf;C2p;oC<%4y z4?1l{^!jPX#r+0l^eTVE-S+=*=)GZ!0V*Vf)MOuD{${}f%6rZbp7^+6NB z4^9(a&(ZHZ9_A;Q05s~hI+4MHV6@+sg<5a_1)A*cQ}^dQ96ipi^P|;|LXEc@GkTU{ zP&q+nD)F&+)N!M^;)U=l^!|o)uGhwEw7%hthleE{^^8r~cov$8O7)kvHpyk9TE76I zKwZCVULR}bpg(Tfc0|K@XkNdg48>?6`f>It?L+cn^m*Arb5~?3`ug^LETd5c>iB@_ z*R8-RbTMw6!koMgwc(_Wb&C3kid(C#)OR+btj9R%o;xk*RLUBSf*^>J|%4Hba9deCQ=7!<$f^rM=48ko7>FQ`zH;4egc z7;XDN^Fd2w6qSyrdHceC9Brj+KRlS3M9aL_rDyVHP~hI!K1<^qN|a14Bf{_v9e^TD zgHOMsMB*Qy51cEgvSZFilYli8U2UF?W!*p{Z;*-3oo}K&&Ci)U6?ReYI`@+ur+pM7 z>b7>>_7Ls9%{x9Fe~dcyj_1AJIY9*otRpQB0 zxe0{!r)PCPcnN>s^3PO@^AkpC$V|B&Un5}7?eV=80t6Ost1mU{f`l{;MNMZOVM4R; zZ03Zf2tm8iB67<}l%QxT?Iw#6BRB+HqqbWWBX}~zY4+a{Cme4veqJ;YC%BM@c{?nM z5t3KcBvOY(34iDH9kecl375a9IS!}@626T~8kZXL5n5E33ocGr2pH=6p)l_Y)XBej zi!elr)f|0Db!n0hyHI`QE_uLptQnrEXnsNwoB2olbuy(o_BdTDD_}zxJ1tW={IbLd zd%zbL%GY9!rMIIY8y>O23SpKIz6?h!HA9fs=#dMyzP%B4lJdY>>TTc8uJ^`rC@5Bag39Hd)x@?(u5}a&NIsul4-CSLb2P7y9F!o)%$4s{)#beM_(klgwq% zUOCnRQ0)ET zA|qF$c5K+)mg5teF09Jel&AADy;ukHx3OyjpRhkudFNL52eB1Nj~jxgN3gBKJJOD+ z?2c?dEY~%Nmx;n!n><{&%?RohV?3G(j{NIP4V%_G~LhNwo zSY!kcXHX-?0jf{>e^rv;Tz~qW?mZ&M+1|5x-xEQFJL7prn@diMWAh@}eicEFQ)NAr zH|u1?6=)^BH#BC!?aT-FV{fwKbQPvG`GUD{igV0zcO`joI};bo!`1w_Aqr7+j8YKi zE_vhSa)U6gfM$)qrAZ8jujZ-cQ{Tj?SFp*~ zb}Qh#;}{vsrj&5Y?x{Bq^|82u&ro4F%Wd4hQ^~7p>KZuhx5%Z=1})sUCX>yKmoAQi z-ELgsTypmbiI~&1Xy5 zHaPPI3YhSeY&oQ|Fdbb|7X({j?UT%F6TETSS!@^pAlnp|DnIOC`d7#OTkb2 z{*q&)nst_|?Wr-Nu1Q!YH9E|j<%v%(EEzCgI3K~(%S;$6qDsk<&#agU!z&~0yquVB zvqklu9d1nN!9WGS5I^PxTzp@!SO7z3VVJoPDvX)`_)+>{s~E8 znV|Aut`;Vw;MPD!qb|lhac?$S`7S0jxY@mm-w5-(Ae+n2%LEf##I>kTXO6L}5$Hr| zEHUPV`L##>Hkc99>$6KX_LvAh`)2huM@+Rsb{MmlGe!WaxMpkQikZl}WS98X9aCz_8r-FDkM) z9Mg5&P~mbIiSg;?6_m`3!Njv#+%8Xz#~fKaJh*b4gsCsbr?_b7j8jv-ch^%1_sT|)t>HUE@gD<1Ob(P&Lk2Y&Ic5bM{=PQ( zFJ^`BdMsb^gIN>;gX3EqDp?dF0F|agXBGu}h&_)kfmtDK2AZbYU{sK~59iPMFmKl3)MIJ(`iHC5C)3lcQ_#fe`ukiH7_{yvi+~#?c&UX64RN*#{21}J-m9i+Jbl#Psx0P@ z_??BIwxPqy^psdM(yC&HD;~g>gFQ;$v-;r^`0gC z?v1ht7Ung)NR!-yrTq7}%~8~gw~O(t4KD+n0$TycK=Pz2D@y-#RW zmjIf5fqVH~*8!lIS&TkH79f?Y1D18#qX|^^04Bvt0<{z-fK!vi*!6ugph>K%^SQAl zP*@=qE~#t_1p8s_P8=Nosr-d)i%U*`g0n;%H|--pGsjwXH{A`0I8kCEDfa{_f5zYF zy6yuoi7i^uZaxF1t1q1jFb4n{cewAcAi+Sa_fb~zuP~s~`H>v&b_5{#ca8l+TQtD? zAa(YfG#;3?e6t?)ISHu#Eg&JtoC?rtJ&L*cHXVq1%vr7*nFR>?hUVcH{sUg2Kkmu4 zy#s>pRa(8uD+I2Vp5lDuih(kp)th&&mjTv=%I_)*D*^GR7J2?}Yk*hcCNrXJAAvJP z^NA3+5n!w&Vz=vU0SNSPO0+%%1iiV+PkEmJxV+&LnGJ6T<|@AM;bgi1T#kS3GyPs* zHC#;1VD1y}Tg7kc;`bncSqkhzzK#I&8vJsrnd3mQwdd*pFa;eU}->6d77pvReJEOBe2k?h7k-*s*WTIW&zuNDytcMWCtxDefTEkbQO#t?hsfs z;seKg>Nxk>u7Mf!zqycPA#hH334hup3fhJ)OS<_>f;@66I+v|)fUCdiQ|$U>!7?9v zWxA}JU~B6qY}+SAP$GUdlRa1koLHCLiMCb)XQw&%uU(M&!rpj2H`>}Kv`@W@)_g*NsH$R(w>wjl2fo=FZ_+eisE`jtl?vXb%f->Trh`v>KIN8hXMvu=QPtH-|A9EOh913vT##<&^49!X z0cdR)0@WG32g_;BKFt%Ag5(^IGXk6y;EhKg7m6yXKx&#EE�qMMJ}yYB3EUDE3kN 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 Date: Mon, 14 Dec 2020 16:06:46 +0100 Subject: [PATCH 50/53] Bugfix in fleurinputgenCalculation, which was introduced in previous commits --- aiida_fleur/calculation/fleurinputgen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aiida_fleur/calculation/fleurinputgen.py b/aiida_fleur/calculation/fleurinputgen.py index cea3ed8c9..a54741eb9 100644 --- a/aiida_fleur/calculation/fleurinputgen.py +++ b/aiida_fleur/calculation/fleurinputgen.py @@ -370,7 +370,7 @@ def prepare_for_submission(self, folder): else: atomic_number_name = '{}.{}'.format(atomic_number, kind_namet) # append a label to the detached atom - reg_string = ' {0:7} {1:18.' + sf_p + 'f} {2:18.' + sf_p + 'f} {3:18.' + sf_p + 'f} {}\n' + reg_string = ' {0:7} {1:18.' + sf_p + 'f} {2:18.' + sf_p + 'f} {3:18.' + sf_p + 'f} {4}\n' atomic_positions_card_listtmp.append( reg_string.format(atomic_number_name, vector_rel[0], vector_rel[1], vector_rel[2], kind_namet)) else: From fb1231a437547dadf8e616bff4adfd677770ed56 Mon Sep 17 00:00:00 2001 From: broeder-j Date: Mon, 14 Dec 2020 16:52:18 +0100 Subject: [PATCH 51/53] First try to fix corehole workchain again, kind names need to be persisted --- aiida_fleur/tools/StructureData_util.py | 13 ++++-- tests/tools/test_StructureData_util.py | 61 +++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/aiida_fleur/tools/StructureData_util.py b/aiida_fleur/tools/StructureData_util.py index fb65a31f2..1ce02a3ce 100644 --- a/aiida_fleur/tools/StructureData_util.py +++ b/aiida_fleur/tools/StructureData_util.py @@ -387,7 +387,9 @@ def break_symmetry(structure, if new_kinds_names is None: new_kinds_names = {} - + write_new_kind_names = False + else: + write_new_kind_names = True from aiida.common.constants import elements as PeriodicTableElements from aiida.orm import Dict @@ -461,7 +463,10 @@ def break_symmetry(structure, # TODO This may not enough, since for magnetic systems one need a kind mapping # i.e if the parameters are for a partly 'pre symmetry broken system' # and we want to keep track from which 'old' kind which new kind spawn - para_new = adjust_calc_para_to_structure(parameterdata, new_structure, add_atom_base_lists=add_atom_base_lists) + para_new = adjust_calc_para_to_structure(parameterdata, + new_structure, + add_atom_base_lists=add_atom_base_lists, + write_new_kind_names=write_new_kind_names) new_structure.label = structure.label new_structure.description = structure.description + 'more kinds, less sym' @@ -469,7 +474,7 @@ def break_symmetry(structure, return new_structure, para_new -def adjust_calc_para_to_structure(parameter, structure, add_atom_base_lists=True): +def adjust_calc_para_to_structure(parameter, structure, add_atom_base_lists=True, write_new_kind_names=False): """ Adjust calculation parameters for inpgen to a given structure with several kinds @@ -542,6 +547,8 @@ def adjust_calc_para_to_structure(parameter, structure, add_atom_base_lists=True if atomlst.get('element', None) == symbol or atomlst.get('z', None) == atomic_number: new_alst = atomlst.copy() new_alst['id'] = should_id + if write_new_kind_names: + new_alst[u'name'] = kind_name atomlistname = 'atom{}'.format(j) param_new_dict[atomlistname] = new_alst j = j + 1 diff --git a/tests/tools/test_StructureData_util.py b/tests/tools/test_StructureData_util.py index 21c39bc8b..a6ceaadeb 100644 --- a/tests/tools/test_StructureData_util.py +++ b/tests/tools/test_StructureData_util.py @@ -211,6 +211,67 @@ def test_break_symmetry_wf_film_structure_only(generate_film_structure): assert len(set(kind_names)) == len(kind_names_should) +def test_break_symmetry_corhole(generate_structure): + """Test if what the corehole workflow does works""" + from aiida_fleur.tools.StructureData_util import break_symmetry + from aiida import orm + + structure = generate_structure() + sites = structure.sites + pos = sites[0].position + kind_name = sites[0].kind_name + para = orm.Dict(dict={ + 'atom': { + 'element': 'Si', + 'rmt': 2.1, + 'jri': 981, + 'lmax': 12, + 'lnonsph': 6 + }, + 'comp': { + 'kmax': 5.0, + } + }) + new_kinds_names = {'Si': [kind_name + '_corehole1']} + inputs = dict(structure=structure, + atoms=[], + site=[], + pos=[(pos[0], pos[1], pos[2])], + new_kinds_names=new_kinds_names) + if para is not None: + inputs['parameterdata'] = para + new_struc, new_para = break_symmetry(**inputs) + + #print(new_para.get_dict()) + kind_names = ['Si_corehole1', 'Si'] + for i, site in enumerate(new_struc.sites): + assert site.kind_name == kind_names[i] + + # Test if the kind name was set to the atom lists + should = { + 'atom1': { + 'element': 'Si', + 'rmt': 2.1, + 'jri': 981, + 'lmax': 12, + 'lnonsph': 6 + }, + 'comp': { + 'kmax': 5.0 + }, + 'atom2': { + 'element': 'Si', + 'rmt': 2.1, + 'jri': 981, + 'lmax': 12, + 'lnonsph': 6, + 'id': '14.1', + 'name': 'Si_corehole1' + } + } + assert new_para.get_dict() == should + + def test_break_symmetry_film_parameters_only_simple(generate_film_structure): """Test if these break symmetry operation adjusted the parameter data right. This basicly tests From 28cbafb5046b3286db6328a295390caeda00dccc Mon Sep 17 00:00:00 2001 From: broeder-j Date: Tue, 15 Dec 2020 10:49:38 +0100 Subject: [PATCH 52/53] Make super and raise from right for python3 In python3 calling simply super() is enough, otherwise it might lead to infinite recursions, in python2 the class was needed. Also to keep the stacktrace one should use from when reraising exceptions. --- aiida_fleur/calculation/fleur.py | 2 +- aiida_fleur/calculation/fleurinputgen.py | 6 +++--- aiida_fleur/data/fleurinp.py | 15 ++++++++------- aiida_fleur/parsers/fleur_inputgen.py | 2 +- aiida_fleur/workflows/banddos.py | 2 +- aiida_fleur/workflows/base_fleur.py | 6 +++--- aiida_fleur/workflows/base_relax.py | 2 +- aiida_fleur/workflows/corehole.py | 2 +- aiida_fleur/workflows/create_magnetic_film.py | 2 +- aiida_fleur/workflows/dmi.py | 2 +- aiida_fleur/workflows/dos.py | 2 +- aiida_fleur/workflows/eos.py | 2 +- aiida_fleur/workflows/initial_cls.py | 2 +- aiida_fleur/workflows/mae.py | 2 +- aiida_fleur/workflows/mae_conv.py | 2 +- aiida_fleur/workflows/optimize_para.py | 4 ++-- aiida_fleur/workflows/relax.py | 2 +- aiida_fleur/workflows/scf.py | 2 +- aiida_fleur/workflows/ssdisp.py | 2 +- aiida_fleur/workflows/ssdisp_conv.py | 2 +- setup.json | 2 +- .../JUDFT_WARN_ONLY | 2 +- tests/conftest.py | 5 ++--- 23 files changed, 36 insertions(+), 36 deletions(-) diff --git a/aiida_fleur/calculation/fleur.py b/aiida_fleur/calculation/fleur.py index 1de9a4d76..344407326 100644 --- a/aiida_fleur/calculation/fleur.py +++ b/aiida_fleur/calculation/fleur.py @@ -193,7 +193,7 @@ class FleurCalculation(CalcJob): @classmethod def define(cls, spec): - super(FleurCalculation, cls).define(spec) + super().define(spec) # spec.input('metadata.options.input_filename', valid_type=six.string_types, # default=cls._INPXML_FILE_NAME) diff --git a/aiida_fleur/calculation/fleurinputgen.py b/aiida_fleur/calculation/fleurinputgen.py index a54741eb9..6d1a123c2 100644 --- a/aiida_fleur/calculation/fleurinputgen.py +++ b/aiida_fleur/calculation/fleurinputgen.py @@ -100,7 +100,7 @@ class FleurinputgenCalculation(CalcJob): @classmethod def define(cls, spec): - super(FleurinputgenCalculation, cls).define(spec) + super().define(spec) spec.input('metadata.options.input_filename', valid_type=six.string_types, default=cls._INPUT_FILE) spec.input('metadata.options.output_filename', valid_type=six.string_types, default=cls._INPXML_FILE_NAME) @@ -568,8 +568,8 @@ def get_input_data_text(key, val, value_only, mapping=None): for elemk, itemval in six.iteritems(val): try: idx = mapping[elemk] - except KeyError: - raise ValueError("Unable to find the key '{}' in the mapping " 'dictionary'.format(elemk)) + except KeyError as exc: + raise ValueError("Unable to find the key '{}' in the mapping " 'dictionary'.format(elemk)) from exc list_of_strings.append((idx, ' {0}({2})={1} '.format(key, conv_to_fortran(itemval), idx))) # changed {0}({2}) = {1}\n".format diff --git a/aiida_fleur/data/fleurinp.py b/aiida_fleur/data/fleurinp.py index 31b10839a..7e4083bd0 100644 --- a/aiida_fleur/data/fleurinp.py +++ b/aiida_fleur/data/fleurinp.py @@ -82,7 +82,7 @@ def __init__(self, **kwargs): files.append(filename) break node = kwargs.pop('node', None) - super(FleurinpData, self).__init__(**kwargs) + super().__init__(**kwargs) search_paths = [] ifolders = get_internal_search_paths() @@ -433,10 +433,10 @@ def _set_inp_dict(self): with self.open(path='inp.xml', mode='r') as inpxmlfile: try: tree_x = etree.parse(inpxmlfile, parser) - except etree.XMLSyntaxError: + except etree.XMLSyntaxError 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) + 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) @@ -468,7 +468,8 @@ def _set_inp_dict(self): 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)) + raise InputValidationError( + 'Input file does not validate against the schema: {}'.format(message)) from msg raise InputValidationError('Input file is not validated against the schema.' 'Reason is unknown') @@ -495,7 +496,7 @@ def _validate(self): """ #from aiida.common.exceptions import ValidationError - super(FleurinpData, self)._validate() + super()._validate() if 'inp.xml' in self.files: # has_inpxml = True # does nothing so far @@ -973,10 +974,10 @@ def get_tag(self, xpath): try: return_value = root.xpath(xpath) - except etree.XPathEvalError: + 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)) + ''.format(xpath)) from exc if len(return_value) == 1: return return_value diff --git a/aiida_fleur/parsers/fleur_inputgen.py b/aiida_fleur/parsers/fleur_inputgen.py index 10068518d..8cf2adb01 100644 --- a/aiida_fleur/parsers/fleur_inputgen.py +++ b/aiida_fleur/parsers/fleur_inputgen.py @@ -38,7 +38,7 @@ def __init__(self, node): """ Initialize the instance of Fleur_inputgenParser """ - super(Fleur_inputgenParser, self).__init__(node) + super().__init__(node) # these files should be at least present after success of inpgen self._default_files = {FleurinputgenCalculation._OUTPUT_FILE_NAME, FleurinputgenCalculation._INPXML_FILE_NAME} diff --git a/aiida_fleur/workflows/banddos.py b/aiida_fleur/workflows/banddos.py index aea1751be..2ed2f6349 100644 --- a/aiida_fleur/workflows/banddos.py +++ b/aiida_fleur/workflows/banddos.py @@ -71,7 +71,7 @@ class FleurBandDosWorkChain(WorkChain): @classmethod def define(cls, spec): - super(FleurBandDosWorkChain, cls).define(spec) + super().define(spec) # spec.expose_inputs(FleurScfWorkChain, namespace='scf') spec.input('wf_parameters', valid_type=Dict, required=False) spec.input('fleur', valid_type=Code, required=True) diff --git a/aiida_fleur/workflows/base_fleur.py b/aiida_fleur/workflows/base_fleur.py index 2c428e556..daadc01df 100644 --- a/aiida_fleur/workflows/base_fleur.py +++ b/aiida_fleur/workflows/base_fleur.py @@ -39,7 +39,7 @@ class FleurBaseWorkChain(BaseRestartWorkChain): @classmethod def define(cls, spec): - super(FleurBaseWorkChain, cls).define(spec) + super().define(spec) spec.input('code', valid_type=orm.Code, help='The FLEUR code.') spec.input('parent_folder', valid_type=orm.RemoteData, @@ -174,8 +174,8 @@ def check_kpts(self): self.ctx.suggest_mpi_omp_ratio, fleurinp, only_even_MPI=self.inputs.only_even_MPI) - except ValueError: - raise Warning('Not optimal computational resources, load less than 60%') + except ValueError as exc: + raise Warning('Not optimal computational resources, load less than 60%') from exc self.report(message) diff --git a/aiida_fleur/workflows/base_relax.py b/aiida_fleur/workflows/base_relax.py index 11df482e5..deb41dd5f 100644 --- a/aiida_fleur/workflows/base_relax.py +++ b/aiida_fleur/workflows/base_relax.py @@ -41,7 +41,7 @@ class FleurBaseRelaxWorkChain(BaseRestartWorkChain): @classmethod def define(cls, spec): - super(FleurBaseRelaxWorkChain, cls).define(spec) + super().define(spec) spec.expose_inputs(RelaxProcess) spec.input('description', valid_type=six.string_types, diff --git a/aiida_fleur/workflows/corehole.py b/aiida_fleur/workflows/corehole.py index ced0a21b6..243cb3bb1 100644 --- a/aiida_fleur/workflows/corehole.py +++ b/aiida_fleur/workflows/corehole.py @@ -147,7 +147,7 @@ class fleur_corehole_wc(WorkChain): @classmethod def define(cls, spec): - super(fleur_corehole_wc, cls).define(spec) + super().define(spec) spec.input('wf_parameters', valid_type=Dict, required=False, default=lambda: Dict(dict=cls._default_wf_para)) spec.input('fleurinp', valid_type=FleurinpData, required=False) spec.input('fleur', valid_type=Code, required=True) diff --git a/aiida_fleur/workflows/create_magnetic_film.py b/aiida_fleur/workflows/create_magnetic_film.py index ea4719dba..b5fcb54ac 100644 --- a/aiida_fleur/workflows/create_magnetic_film.py +++ b/aiida_fleur/workflows/create_magnetic_film.py @@ -53,7 +53,7 @@ class FleurCreateMagneticWorkChain(WorkChain): @classmethod def define(cls, spec): - super(FleurCreateMagneticWorkChain, cls).define(spec) + super().define(spec) spec.expose_inputs(FleurEosWorkChain, namespace_options={ 'required': False, diff --git a/aiida_fleur/workflows/dmi.py b/aiida_fleur/workflows/dmi.py index f4be09837..9a98361a1 100644 --- a/aiida_fleur/workflows/dmi.py +++ b/aiida_fleur/workflows/dmi.py @@ -75,7 +75,7 @@ class FleurDMIWorkChain(WorkChain): @classmethod def define(cls, spec): - super(FleurDMIWorkChain, cls).define(spec) + super().define(spec) spec.expose_inputs(FleurScfWorkChain, namespace='scf') spec.input('wf_parameters', valid_type=Dict, required=False) spec.input('fleur', valid_type=Code, required=True) diff --git a/aiida_fleur/workflows/dos.py b/aiida_fleur/workflows/dos.py index 30d7186ea..ce6ee4229 100644 --- a/aiida_fleur/workflows/dos.py +++ b/aiida_fleur/workflows/dos.py @@ -58,7 +58,7 @@ class fleur_dos_wc(WorkChain): @classmethod def define(cls, spec): - super(fleur_dos_wc, cls).define(spec) + super().define(spec) spec.input('wf_parameters', valid_type=Dict, required=False, default=lambda: Dict(dict=cls._default_wf_para)) spec.input('calc_parameters', valid_type=Dict, required=False) spec.input('settings', valid_type=Dict, required=False) diff --git a/aiida_fleur/workflows/eos.py b/aiida_fleur/workflows/eos.py index a7476361a..0a47eba02 100644 --- a/aiida_fleur/workflows/eos.py +++ b/aiida_fleur/workflows/eos.py @@ -60,7 +60,7 @@ class FleurEosWorkChain(WorkChain): @classmethod def define(cls, spec): - super(FleurEosWorkChain, cls).define(spec) + super().define(spec) spec.expose_inputs(FleurScfWorkChain, namespace='scf', exclude=( 'structure', 'remote_data', diff --git a/aiida_fleur/workflows/initial_cls.py b/aiida_fleur/workflows/initial_cls.py index 69d942076..b6c373da5 100644 --- a/aiida_fleur/workflows/initial_cls.py +++ b/aiida_fleur/workflows/initial_cls.py @@ -107,7 +107,7 @@ class fleur_initial_cls_wc(WorkChain): @classmethod def define(cls, spec): - super(fleur_initial_cls_wc, cls).define(spec) + super().define(spec) spec.input('wf_parameters', valid_type=Dict, required=False, default=lambda: Dict(dict=cls._default_wf_para)) spec.input('fleurinp', valid_type=FleurinpData, required=False) spec.input('fleur', valid_type=Code, required=True) diff --git a/aiida_fleur/workflows/mae.py b/aiida_fleur/workflows/mae.py index 18cccfa4e..befb8ba6f 100644 --- a/aiida_fleur/workflows/mae.py +++ b/aiida_fleur/workflows/mae.py @@ -68,7 +68,7 @@ class FleurMaeWorkChain(WorkChain): @classmethod def define(cls, spec): - super(FleurMaeWorkChain, cls).define(spec) + super().define(spec) spec.expose_inputs(FleurScfWorkChain, namespace='scf') spec.input('wf_parameters', valid_type=Dict, required=False) spec.input('fleur', valid_type=Code, required=True) diff --git a/aiida_fleur/workflows/mae_conv.py b/aiida_fleur/workflows/mae_conv.py index 6965996e9..220a6bb75 100644 --- a/aiida_fleur/workflows/mae_conv.py +++ b/aiida_fleur/workflows/mae_conv.py @@ -49,7 +49,7 @@ class FleurMaeConvWorkChain(WorkChain): @classmethod def define(cls, spec): - super(FleurMaeConvWorkChain, cls).define(spec) + super().define(spec) spec.expose_inputs(FleurScfWorkChain, namespace='scf') spec.input('wf_parameters', valid_type=Dict, required=False) diff --git a/aiida_fleur/workflows/optimize_para.py b/aiida_fleur/workflows/optimize_para.py index 6c1a9ad2d..c6c5a2032 100644 --- a/aiida_fleur/workflows/optimize_para.py +++ b/aiida_fleur/workflows/optimize_para.py @@ -53,11 +53,11 @@ class fleur_optimize_parameters_wc(WorkChain): _default_wf_para = {} def __init__(self, *args, **kwargs): - super(fleur_optimize_parameters_wc, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) @classmethod def define(cls, spec): - super(fleur_optimize_parameters_wc, cls).define(spec) + super().define(spec) spec.input( 'wf_parameters', valid_type=Dict, diff --git a/aiida_fleur/workflows/relax.py b/aiida_fleur/workflows/relax.py index 610375d23..1cdd36bb6 100644 --- a/aiida_fleur/workflows/relax.py +++ b/aiida_fleur/workflows/relax.py @@ -54,7 +54,7 @@ class FleurRelaxWorkChain(WorkChain): @classmethod def define(cls, spec): - super(FleurRelaxWorkChain, cls).define(spec) + super().define(spec) spec.expose_inputs(FleurScfWorkChain, namespace='scf') spec.expose_inputs(FleurScfWorkChain, namespace='final_scf', diff --git a/aiida_fleur/workflows/scf.py b/aiida_fleur/workflows/scf.py index b8810f45b..f48eebd09 100644 --- a/aiida_fleur/workflows/scf.py +++ b/aiida_fleur/workflows/scf.py @@ -97,7 +97,7 @@ class FleurScfWorkChain(WorkChain): @classmethod def define(cls, spec): - super(FleurScfWorkChain, cls).define(spec) + super().define(spec) spec.input('fleur', valid_type=Code, required=True) spec.input('inpgen', valid_type=Code, required=False) spec.input('wf_parameters', valid_type=Dict, required=False) diff --git a/aiida_fleur/workflows/ssdisp.py b/aiida_fleur/workflows/ssdisp.py index 09d9dd74e..9decd146e 100644 --- a/aiida_fleur/workflows/ssdisp.py +++ b/aiida_fleur/workflows/ssdisp.py @@ -70,7 +70,7 @@ class FleurSSDispWorkChain(WorkChain): @classmethod def define(cls, spec): - super(FleurSSDispWorkChain, cls).define(spec) + super().define(spec) spec.expose_inputs(FleurScfWorkChain, namespace='scf') spec.input('wf_parameters', valid_type=Dict, required=False) spec.input('fleur', valid_type=Code, required=True) diff --git a/aiida_fleur/workflows/ssdisp_conv.py b/aiida_fleur/workflows/ssdisp_conv.py index cd37fade3..889ea388f 100644 --- a/aiida_fleur/workflows/ssdisp_conv.py +++ b/aiida_fleur/workflows/ssdisp_conv.py @@ -46,7 +46,7 @@ class FleurSSDispConvWorkChain(WorkChain): @classmethod def define(cls, spec): - super(FleurSSDispConvWorkChain, cls).define(spec) + super().define(spec) spec.expose_inputs(FleurScfWorkChain, namespace='scf') spec.input('wf_parameters', valid_type=Dict, required=False) diff --git a/setup.json b/setup.json index 6921007bf..166e27385 100644 --- a/setup.json +++ b/setup.json @@ -51,7 +51,7 @@ "pre-commit": [ "pre-commit>=2.6.0", "yapf~=0.30", - "pylint~=2.5" + "pylint>=2.5" ], "testing" : [ "pytest>=2.9", diff --git a/tests/calculation/data_dir/mock-fleur-88edcca7024f2f2c75ac80044aa80644/JUDFT_WARN_ONLY b/tests/calculation/data_dir/mock-fleur-88edcca7024f2f2c75ac80044aa80644/JUDFT_WARN_ONLY index 7a019b2b4..65c71eb10 100644 --- a/tests/calculation/data_dir/mock-fleur-88edcca7024f2f2c75ac80044aa80644/JUDFT_WARN_ONLY +++ b/tests/calculation/data_dir/mock-fleur-88edcca7024f2f2c75ac80044aa80644/JUDFT_WARN_ONLY @@ -1 +1 @@ -/n \ No newline at end of file +/n diff --git a/tests/conftest.py b/tests/conftest.py index 70437ad91..74ec02f82 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -8,7 +8,6 @@ import os import collections import pytest -import six import sys from aiida.orm import Node, Code, Dict, RemoteData, CalcJobNode @@ -90,7 +89,7 @@ def generate_calc_job_node(fixture_localhost): def flatten_inputs(inputs, prefix=''): """Flatten inputs recursively like :meth:`aiida.engine.processes.process::Process._flatten_inputs`.""" flat_inputs = [] - for key, value in six.iteritems(inputs): + for key, value in inputs.items(): if isinstance(value, collections.Mapping): flat_inputs.extend(flatten_inputs(value, prefix=prefix + key + '__')) else: @@ -273,7 +272,7 @@ def generate_work_chain_node(): def flatten_inputs(inputs, prefix=''): """Flatten inputs recursively like :meth:`aiida.engine.processes.process::Process._flatten_inputs`.""" flat_inputs = [] - for key, value in six.iteritems(inputs): + for key, value in inputs.items(): if isinstance(value, collections.Mapping): flat_inputs.extend(flatten_inputs(value, prefix=prefix + key + '__')) else: From f6716ac48338d7d07cf8a13853fda777cdd1085f Mon Sep 17 00:00:00 2001 From: broeder-j Date: Tue, 15 Dec 2020 11:12:50 +0100 Subject: [PATCH 53/53] Add on to previous commit for raise from --- aiida_fleur/common/mapping.py | 4 +-- aiida_fleur/common/workchain/base/restart.py | 13 ++++---- aiida_fleur/data/fleurinpmodifier.py | 25 ++++++++------- aiida_fleur/tools/common_fleur_wf.py | 6 ++-- aiida_fleur/tools/xml_util.py | 32 +++++++++++--------- 5 files changed, 42 insertions(+), 38 deletions(-) diff --git a/aiida_fleur/common/mapping.py b/aiida_fleur/common/mapping.py index cb01e3015..59118a4b6 100644 --- a/aiida_fleur/common/mapping.py +++ b/aiida_fleur/common/mapping.py @@ -57,8 +57,8 @@ def prepare_process_inputs(process, inputs): try: process_spec = process.spec() - except AttributeError: - raise ValueError('process {} does not have a spec') + except AttributeError as exc: + raise ValueError('Process {} does not have a spec') from exc for key, value in six.iteritems(inputs): diff --git a/aiida_fleur/common/workchain/base/restart.py b/aiida_fleur/common/workchain/base/restart.py index 9841b7c7f..0114e4ddc 100644 --- a/aiida_fleur/common/workchain/base/restart.py +++ b/aiida_fleur/common/workchain/base/restart.py @@ -56,7 +56,7 @@ class BaseRestartWorkChain(WorkChain): _error_handler_entry_point = None def __init__(self, *args, **kwargs): - super(BaseRestartWorkChain, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) if self._calculation_class is None or not issubclass(self._calculation_class, (CalcJob, WorkChain)): raise ValueError('no valid CalcJob or WorkChain class defined for `_calculation_class` attribute') @@ -65,7 +65,7 @@ def __init__(self, *args, **kwargs): @override def load_instance_state(self, saved_state, load_context): - super(BaseRestartWorkChain, self).load_instance_state(saved_state, load_context) + super().load_instance_state(saved_state, load_context) self._load_error_handlers() def _load_error_handlers(self): @@ -86,8 +86,7 @@ def _load_error_handlers(self): @classmethod def define(cls, spec): # yapf: disable - # pylint: disable=bad-continuation - super(BaseRestartWorkChain, cls).define(spec) + super().define(spec) spec.input('max_iterations', valid_type=orm.Int, default=lambda: orm.Int(3), help='Maximum number of iterations the work chain will restart the calculation to finish successfully.') spec.input('clean_workdir', valid_type=orm.Bool, default=lambda: orm.Bool(False), @@ -121,8 +120,8 @@ def run_calculation(self): try: unwrapped_inputs = self.ctx.inputs - except AttributeError: - raise AttributeError('no calculation input dictionary was defined in `self.ctx.inputs`') + except AttributeError as exc: + raise AttributeError('no calculation input dictionary was defined in `self.ctx.inputs`') from exc inputs = prepare_process_inputs(self._calculation_class, unwrapped_inputs) calculation = self.submit(self._calculation_class, **inputs) @@ -201,7 +200,7 @@ def results(self): def on_terminated(self): """Clean the working directories of all child calculations if `clean_workdir=True` in the inputs.""" - super(BaseRestartWorkChain, self).on_terminated() + super().on_terminated() if self.inputs.clean_workdir.value is False: self.report('remote folders will not be cleaned') diff --git a/aiida_fleur/data/fleurinpmodifier.py b/aiida_fleur/data/fleurinpmodifier.py index e24c6be27..d95f4c3af 100644 --- a/aiida_fleur/data/fleurinpmodifier.py +++ b/aiida_fleur/data/fleurinpmodifier.py @@ -199,8 +199,8 @@ def set_nmmpmat1(fleurinp_tree_copy, nmmp_lines_copy, species_name, orbital,\ for task in modification_tasks: try: action = actions[task[0]] - except KeyError: - raise ValueError('Unknown task {}'.format(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:]) @@ -210,14 +210,16 @@ def set_nmmpmat1(fleurinp_tree_copy, nmmp_lines_copy, species_name, orbital,\ if schema_tree: try: xmlschema.assertValid(clear_xml(workingtree)) - except etree.DocumentInvalid: - print('changes were not valid: {}'.format(modification_tasks)) - raise + 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: - print('changes were not valid (n_mmp_mat file is not compatible): {}'.format(modification_tasks)) - raise + 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 @@ -640,9 +642,10 @@ def modify_fleurinpdata(original, modifications, **kwargs): try: xmlschema.assertValid(clear_xml(tree)) - except etree.DocumentInvalid: - print('Input file is not validated against the schema') - raise + except etree.DocumentInvalid as exc: + msg = 'Input file is not validated against the schema' + print(msg) + raise etree.DocumentInvalid(msg) from exc try: with new_fleurinp.open(path='n_mmp_mat', mode='r') as n_mmp_file: diff --git a/aiida_fleur/tools/common_fleur_wf.py b/aiida_fleur/tools/common_fleur_wf.py index 5b874b1d3..05096931a 100644 --- a/aiida_fleur/tools/common_fleur_wf.py +++ b/aiida_fleur/tools/common_fleur_wf.py @@ -218,7 +218,7 @@ def test_and_get_codenode(codenode, expected_code_type, use_exceptions=False): code = codenode if code.get_input_plugin_name() != expected_code_type: raise ValueError - except ValueError: + except ValueError as exc: from aiida.orm.querybuilder import QueryBuilder qb = QueryBuilder() qb.append(Code, filters={'attributes.input_plugin': {'==': expected_code_type}}, project='*') @@ -231,7 +231,7 @@ def test_and_get_codenode(codenode, expected_code_type, use_exceptions=False): msg += '\n'.join('* {}'.format(l) for l in valid_code_labels) if use_exceptions: - raise ValueError(msg) + raise ValueError(msg) from exc else: print(msg) # , file=sys.stderr) sys.exit(1) @@ -240,7 +240,7 @@ def test_and_get_codenode(codenode, expected_code_type, use_exceptions=False): 'Configure at least one first using\n' ' verdi code setup'.format(expected_code_type)) if use_exceptions: - raise ValueError(msg) + raise ValueError(msg) from exc else: print(msg) # , file=sys.stderr) sys.exit(1) diff --git a/aiida_fleur/tools/xml_util.py b/aiida_fleur/tools/xml_util.py index 7325303fc..1c54d8ed5 100644 --- a/aiida_fleur/tools/xml_util.py +++ b/aiida_fleur/tools/xml_util.py @@ -416,7 +416,8 @@ def create_tag(xmlnode, xpath, newelement, create=False, place_index=None, tag_o 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)) + 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: @@ -426,8 +427,8 @@ def create_tag(xmlnode, xpath, newelement, create=False, place_index=None, tag_o # behind what shall I place it try: place_index = tag_order.index(newelement_name) - except: - raise ValueError('Did not find element name in the tag_order list') + 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 = [] @@ -438,8 +439,9 @@ def create_tag(xmlnode, xpath, newelement, create=False, place_index=None, tag_o for name in tags: try: current = tag_order.index(name) - except ValueError: - raise ValueError('Did not find existing tag name in the tag_order list' ': {}'.format(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: @@ -452,10 +454,10 @@ def create_tag(xmlnode, xpath, newelement, create=False, place_index=None, tag_o tag_index = node_1.index(child) try: node_1.insert(tag_index + 1, element_to_write) - except ValueError as v: + except ValueError as exc: raise ValueError('{}. If this is a species, are' 'you sure this species exists in your inp.xml?' - ''.format(v)) + ''.format(exc)) from exc was_set = True break if was_set: @@ -463,23 +465,23 @@ def create_tag(xmlnode, xpath, newelement, create=False, place_index=None, tag_o if not was_set: # just append try: node_1.insert(0, element_to_write) - except ValueError as v: + except ValueError as exc: raise ValueError('{}. If this is a species, are you' ' sure this species exists in your inp.xml?' - ''.format(v)) + ''.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 v: + except ValueError as exc: raise ValueError('{}. If this is a species, are you sure this species ' - 'exists in your inp.xml?'.format(v)) + 'exists in your inp.xml?'.format(exc)) from exc else: try: node_1.append(element_to_write) - except ValueError as v: + except ValueError as exc: raise ValueError('{}. If this is a species, are you sure this species exists' - 'in your inp.xml?'.format(v)) + 'in your inp.xml?'.format(exc)) from exc return xmlnode @@ -1478,11 +1480,11 @@ def eval_xpath3(node, xpath, create=False, place_index=None, tag_order=None): """ try: return_value = node.xpath(xpath) - except etree.XPathEvalError: + 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) + raise etree.XPathEvalError(message) from exc if return_value == []: if create: