From de95642a996e64494c8bf32e4acb11c27bc71c17 Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Tue, 29 Sep 2020 12:34:33 +0100 Subject: [PATCH] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Upgrade=20to=20aiida-core?= =?UTF-8?q?=20v1.4.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - drop support for python<3.5 - remove six dependency - fix deprecation warnings - impore pre-commit code style - add tox.ini and docker-compose.yml for improved test infrastructure --- .flake8 | 3 - .gitignore | 1 + .pre-commit-config.yaml | 47 ++++---- .pre-commit/check_version.py | 71 +++++++++++ .travis-data/check_version.py | 33 ------ aiida_lammps/calculations/dynaphopy.py | 8 +- aiida_lammps/calculations/lammps/__init__.py | 21 ++-- aiida_lammps/calculations/lammps/combinate.py | 18 +-- aiida_lammps/calculations/lammps/force.py | 8 +- aiida_lammps/calculations/lammps/md.py | 14 ++- aiida_lammps/calculations/lammps/md_multi.py | 6 +- aiida_lammps/calculations/lammps/optimize.py | 6 +- aiida_lammps/common/parse_trajectory.py | 4 +- aiida_lammps/common/reaxff_convert.py | 1 - aiida_lammps/data/pot_plugins/base_plugin.py | 5 +- aiida_lammps/data/pot_plugins/reaxff.py | 5 +- aiida_lammps/data/potential.py | 10 +- aiida_lammps/data/trajectory.py | 21 ++-- aiida_lammps/parsers/dynaphopy.py | 4 +- aiida_lammps/parsers/lammps/base.py | 2 +- aiida_lammps/parsers/lammps/force.py | 11 +- aiida_lammps/parsers/lammps/md.py | 5 +- aiida_lammps/parsers/lammps/md_multi.py | 5 +- aiida_lammps/parsers/lammps/optimize.py | 2 +- aiida_lammps/tests/test_calculations.py | 5 +- .../test_input_creation_reaxff_lammps_md_.txt | 1 - ...input_creation_reaxff_lammps_md_multi_.txt | 1 - aiida_lammps/tests/test_generate_structure.py | 4 +- aiida_lammps/tests/test_parsers.py | 103 ++++++++-------- aiida_lammps/tests/test_potential_data.py | 8 +- .../test_potential_data/test_init_reaxff_.yml | 2 +- .../test_input_lines_reaxff_.txt | 1 - aiida_lammps/tests/test_reaxff_parse.py | 7 +- aiida_lammps/tests/test_trajectory.py | 4 +- aiida_lammps/tests/utils.py | 112 +++--------------- aiida_lammps/validation/utils.py | 3 +- conftest.py | 28 ++--- docker-compose.yml | 29 +++++ examples/launch_dynaphopy_si.py | 6 +- examples/launch_lammps_combinate.py | 7 +- examples/launch_lammps_force_gan.py | 8 +- examples/launch_lammps_md_si.py | 9 +- examples/launch_lammps_optimization_fe.py | 9 +- examples/launch_lammps_optimization_gan.py | 8 +- examples/launch_lammps_optimization_lj.py | 8 +- pyproject.toml | 13 ++ pytest.ini | 7 -- setup.json | 25 ++-- setup.py | 4 +- tox.ini | 48 ++++++++ 50 files changed, 388 insertions(+), 383 deletions(-) delete mode 100644 .flake8 create mode 100644 .pre-commit/check_version.py delete mode 100644 .travis-data/check_version.py create mode 100644 docker-compose.yml delete mode 100644 pytest.ini create mode 100644 tox.ini diff --git a/.flake8 b/.flake8 deleted file mode 100644 index b7bcc15..0000000 --- a/.flake8 +++ /dev/null @@ -1,3 +0,0 @@ -[flake8] -max-line-length = 88 -ignore = E203,E501,W503 diff --git a/.gitignore b/.gitignore index 553e826..45b0680 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ dist/ *.egg* .DS_Store .idea/ +.tox/ .pytest_cache/ .idea/vcs.xml postgres*.log diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5827c59..0b1649d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,49 +10,50 @@ exclude: > repos: - repo: git://github.com/pre-commit/pre-commit-hooks - rev: v2.2.3 + rev: v3.2.0 hooks: - id: check-json - id: check-yaml - id: end-of-file-fixer - id: trailing-whitespace + + - repo: https://github.com/timothycrosley/isort + rev: 5.5.3 + hooks: + - id: isort + + - repo: https://github.com/psf/black + rev: 20.8b1 + hooks: + - id: black + + - repo: https://gitlab.com/pycqa/flake8 + rev: 3.8.3 + hooks: - id: flake8 + # additional_dependencies: + # - flake8-bugbear==20.1.4 + # - flake8-builtins==1.5.3 + # - flake8-comprehensions==3.2.3 exclude: > (?x)^( aiida_lammps/calculations/dynaphopy.py )$ - - repo: https://github.com/psf/black - rev: stable - hooks: - - id: black - - repo: local hooks: - - id: version-number name: Check version numbers - entry: python ./.travis-data/check_version.py - language: system + entry: python ./.pre-commit/check_version.py version + language: python files: >- (?x)^( setup.json| - .travis-data/check_version.py| + .pre-commit/check_version.py| aiida_lammps/__init__.py )$ pass_filenames: false - - # - id: travis-linter - # name: Travis Lint - # entry: travis lint - # files: .travis.yml - # language: ruby - # additional_dependencies: ['travis'] - - # - id: doc8 - # entry: doc8 - # language: system - # types: [rst] - # name: doc8 - Lint the documentation. + additional_dependencies: + - click # TODO could also add check-manifest diff --git a/.pre-commit/check_version.py b/.pre-commit/check_version.py new file mode 100644 index 0000000..797d78d --- /dev/null +++ b/.pre-commit/check_version.py @@ -0,0 +1,71 @@ +"""Validate consistency of versions and dependencies. + +Validates consistency of setup.json and + + * environment.yml + * version in aiida_lammps/__init__.py +""" +import json +import os +import sys + +import click + +FILENAME_SETUP_JSON = "setup.json" +SCRIPT_PATH = os.path.split(os.path.realpath(__file__))[0] +ROOT_DIR = os.path.join(SCRIPT_PATH, os.pardir) +FILEPATH_SETUP_JSON = os.path.join(ROOT_DIR, FILENAME_SETUP_JSON) + + +def get_setup_json(): + """Return the `setup.json` as a python dictionary.""" + with open(FILEPATH_SETUP_JSON, "r") as handle: + setup_json = json.load(handle) # , object_pairs_hook=OrderedDict) + + return setup_json + + +@click.group() +def cli(): + """Command line interface for pre-commit checks.""" + pass + + +@cli.command("version") +def validate_version(): + """Check that version numbers match. + + Check version number in setup.json and aiida_lammos/__init__.py and make sure + they match. + """ + # Get version from python package + sys.path.insert(0, ROOT_DIR) + import aiida_lammps # pylint: disable=wrong-import-position + + version = aiida_lammps.__version__ + + setup_content = get_setup_json() + if version != setup_content["version"]: + click.echo("Version number mismatch detected:") + click.echo( + "Version number in '{}': {}".format( + FILENAME_SETUP_JSON, setup_content["version"] + ) + ) + click.echo( + "Version number in '{}/__init__.py': {}".format("aiida_lammps", version) + ) + click.echo( + "Updating version in '{}' to: {}".format(FILENAME_SETUP_JSON, version) + ) + + setup_content["version"] = version + with open(FILEPATH_SETUP_JSON, "w") as fil: + # Write with indentation of two spaces and explicitly define separators to not have spaces at end of lines + json.dump(setup_content, fil, indent=2, separators=(",", ": ")) + + sys.exit(1) + + +if __name__ == "__main__": + cli() # pylint: disable=no-value-for-parameter diff --git a/.travis-data/check_version.py b/.travis-data/check_version.py deleted file mode 100644 index d05dc63..0000000 --- a/.travis-data/check_version.py +++ /dev/null @@ -1,33 +0,0 @@ -"""Check that version numbers match. - -Check version number in setup.json and aiida_lammps/__init__.py and make sure -they match. -""" -import os -import json -import sys - -this_path = os.path.split(os.path.realpath(__file__))[0] - -# Get content of setup.json -setup_fname = "setup.json" -setup_path = os.path.join(this_path, os.pardir, setup_fname) -with open(setup_path) as f: - setup_content = json.load(f) - -# Get version from python package -sys.path.insert(0, os.path.join(this_path, os.pardir)) -import aiida_lammps # noqa - -version = aiida_lammps.__version__ - -if version != setup_content["version"]: - print("Version number mismatch detected:") - print("Version number in '{}': {}".format(setup_fname, setup_content["version"])) - print("Version number in '{}/__init__.py': {}".format("aiida_lammps", version)) - sys.exit(1) - -# Overwrite version in setup.json -# setup_content['version'] = version -# with open(setup_path, 'w') as f: -# json.dump(setup_content, f, indent=4, sort_keys=True) diff --git a/aiida_lammps/calculations/dynaphopy.py b/aiida_lammps/calculations/dynaphopy.py index 8e9eadd..5571c76 100644 --- a/aiida_lammps/calculations/dynaphopy.py +++ b/aiida_lammps/calculations/dynaphopy.py @@ -1,10 +1,10 @@ -from aiida.engine import CalcJob -from aiida.orm import ArrayData, StructureData, TrajectoryData -from aiida.common.exceptions import InputValidationError from aiida.common.datastructures import CalcInfo, CodeInfo +from aiida.common.exceptions import InputValidationError from aiida.common.utils import classproperty - +from aiida.engine import CalcJob +from aiida.orm import ArrayData, StructureData, TrajectoryData from aiida_phonopy.common.raw_parsers import get_force_constants, get_poscar_txt + from aiida_lammps.common.generate_input_files import ( get_trajectory_txt, parameters_to_input_file, diff --git a/aiida_lammps/calculations/lammps/__init__.py b/aiida_lammps/calculations/lammps/__init__.py index 9783a16..ca015a6 100644 --- a/aiida_lammps/calculations/lammps/__init__.py +++ b/aiida_lammps/calculations/lammps/__init__.py @@ -1,13 +1,12 @@ -from aiida.engine import CalcJob -from aiida.common.exceptions import ValidationError from aiida.common import CalcInfo, CodeInfo -from aiida.orm import StructureData, Dict +from aiida.common.exceptions import ValidationError +from aiida.engine import CalcJob +from aiida.orm import Dict, StructureData from aiida.plugins import DataFactory +import numpy as np + from aiida_lammps.common.generate_structure import generate_lammps_structure from aiida_lammps.data.potential import EmpiricalPotential -import six - -import numpy as np def get_supercell(structure, supercell_shape): @@ -111,27 +110,27 @@ def define(cls, spec): spec.input("parameters", valid_type=Dict, help="the parameters", required=False) spec.input( "metadata.options.cell_transform_filename", - valid_type=six.string_types, + valid_type=str, default="cell_transform.npy", ) spec.input( "metadata.options.output_filename", - valid_type=six.string_types, + valid_type=str, default=cls._DEFAULT_OUTPUT_FILE_NAME, ) spec.input( "metadata.options.trajectory_suffix", - valid_type=six.string_types, + valid_type=str, default=cls._DEFAULT_TRAJECTORY_FILE_NAME, ) spec.input( "metadata.options.system_suffix", - valid_type=six.string_types, + valid_type=str, default=cls._DEFAULT_SYSTEM_FILE_NAME, ) spec.input( "metadata.options.restart_filename", - valid_type=six.string_types, + valid_type=str, default=cls._DEFAULT_RESTART_FILE_NAME, ) diff --git a/aiida_lammps/calculations/lammps/combinate.py b/aiida_lammps/calculations/lammps/combinate.py index a752c85..089cd22 100644 --- a/aiida_lammps/calculations/lammps/combinate.py +++ b/aiida_lammps/calculations/lammps/combinate.py @@ -1,15 +1,15 @@ # Not working with Aiida 1.0 from aiida.common.exceptions import InputValidationError +from aiida.orm import ArrayData, Dict from aiida_phonopy.common.raw_parsers import ( get_force_constants, - get_poscar_txt, get_FORCE_SETS_txt, + get_poscar_txt, ) -from aiida_lammps.calculations.lammps import BaseLammpsCalculation -from aiida.orm import Dict, ArrayData import numpy as np -import six + +from aiida_lammps.calculations.lammps import BaseLammpsCalculation def generate_dynaphopy_input( @@ -59,7 +59,7 @@ def define(cls, spec): super(CombinateCalculation, cls).define(spec) spec.input( "metadata.options.parser_name", - valid_type=six.string_types, + valid_type=str, default="dynaphopy", ) spec.input("parameters_dynaphopy", valid_type=Dict, help="dynaphopy parameters") @@ -68,7 +68,7 @@ def define(cls, spec): ) spec.input("force_sets", valid_type=ArrayData, help="phonopy force sets") - # spec.input('settings', valid_type=six.string_types, default='lammps.optimize') + # spec.input('settings', valid_type=str, default='lammps.optimize') @staticmethod def create_main_input_content( @@ -94,8 +94,10 @@ def create_main_input_content( lammps_input_file += "neighbor 0.3 bin\n" lammps_input_file += "neigh_modify every 1 delay 0 check no\n" - lammps_input_file += "velocity all create {0} {1} dist gaussian mom yes\n".format( - parameter_data.dict.temperature, random_number + lammps_input_file += ( + "velocity all create {0} {1} dist gaussian mom yes\n".format( + parameter_data.dict.temperature, random_number + ) ) lammps_input_file += "velocity all scale {}\n".format( parameter_data.dict.temperature diff --git a/aiida_lammps/calculations/lammps/force.py b/aiida_lammps/calculations/lammps/force.py index 2359e97..e7879c2 100644 --- a/aiida_lammps/calculations/lammps/force.py +++ b/aiida_lammps/calculations/lammps/force.py @@ -1,8 +1,8 @@ from aiida.plugins import DataFactory + from aiida_lammps.calculations.lammps import BaseLammpsCalculation -from aiida_lammps.validation import validate_against_schema from aiida_lammps.common.utils import convert_date_string -import six +from aiida_lammps.validation import validate_against_schema class ForceCalculation(BaseLammpsCalculation): @@ -12,11 +12,11 @@ def define(cls, spec): spec.input( "metadata.options.parser_name", - valid_type=six.string_types, + valid_type=str, default="lammps.force", ) - # spec.input('settings', valid_type=six.string_types, default='lammps.optimize') + # spec.input('settings', valid_type=str, default='lammps.optimize') spec.output( "arrays", diff --git a/aiida_lammps/calculations/lammps/md.py b/aiida_lammps/calculations/lammps/md.py index 33d1f04..3c18ef4 100644 --- a/aiida_lammps/calculations/lammps/md.py +++ b/aiida_lammps/calculations/lammps/md.py @@ -1,10 +1,10 @@ -import numpy as np from aiida.common.exceptions import InputValidationError from aiida.plugins import DataFactory +import numpy as np + from aiida_lammps.calculations.lammps import BaseLammpsCalculation -from aiida_lammps.common.utils import convert_date_string, join_keywords, get_path +from aiida_lammps.common.utils import convert_date_string, get_path, join_keywords from aiida_lammps.validation import validate_against_schema -import six class MdCalculation(BaseLammpsCalculation): @@ -14,7 +14,7 @@ def define(cls, spec): spec.input( "metadata.options.parser_name", - valid_type=six.string_types, + valid_type=str, default="lammps.md", ) spec.default_output_port = "results" @@ -96,8 +96,10 @@ def create_main_input_content( raise_error=False, ) if initial_temp is not None: - lammps_input_file += "velocity all create {0} {1} dist gaussian mom yes\n".format( - initial_temp, pdict.get("rand_seed", np.random.randint(10000000)) + lammps_input_file += ( + "velocity all create {0} {1} dist gaussian mom yes\n".format( + initial_temp, pdict.get("rand_seed", np.random.randint(10000000)) + ) ) lammps_input_file += "velocity all scale {}\n".format(initial_temp) diff --git a/aiida_lammps/calculations/lammps/md_multi.py b/aiida_lammps/calculations/lammps/md_multi.py index 3e53c17..1c7dcef 100644 --- a/aiida_lammps/calculations/lammps/md_multi.py +++ b/aiida_lammps/calculations/lammps/md_multi.py @@ -1,9 +1,9 @@ from aiida.common.exceptions import InputValidationError from aiida.plugins import DataFactory + from aiida_lammps.calculations.lammps import BaseLammpsCalculation -from aiida_lammps.common.utils import convert_date_string, join_keywords, get_path +from aiida_lammps.common.utils import convert_date_string, get_path, join_keywords from aiida_lammps.validation import validate_against_schema -import six class MdMultiCalculation(BaseLammpsCalculation): @@ -15,7 +15,7 @@ def define(cls, spec): spec.input( "metadata.options.parser_name", - valid_type=six.string_types, + valid_type=str, default="lammps.md.multi", ) spec.default_output_port = "results" diff --git a/aiida_lammps/calculations/lammps/optimize.py b/aiida_lammps/calculations/lammps/optimize.py index 65f8e27..fd780de 100644 --- a/aiida_lammps/calculations/lammps/optimize.py +++ b/aiida_lammps/calculations/lammps/optimize.py @@ -1,9 +1,9 @@ from aiida.common.exceptions import InputValidationError +from aiida.plugins import DataFactory + from aiida_lammps.calculations.lammps import BaseLammpsCalculation from aiida_lammps.common.utils import convert_date_string, join_keywords from aiida_lammps.validation import validate_against_schema -from aiida.plugins import DataFactory -import six class OptimizeCalculation(BaseLammpsCalculation): @@ -13,7 +13,7 @@ def define(cls, spec): spec.input( "metadata.options.parser_name", - valid_type=six.string_types, + valid_type=str, default="lammps.optimize", ) diff --git a/aiida_lammps/common/parse_trajectory.py b/aiida_lammps/common/parse_trajectory.py index 6590f4c..a6098ed 100644 --- a/aiida_lammps/common/parse_trajectory.py +++ b/aiida_lammps/common/parse_trajectory.py @@ -1,9 +1,7 @@ from collections import namedtuple -import numpy as np - from aiida.orm import StructureData - +import numpy as np TRAJ_BLOCK = namedtuple( "TRAJ_BLOCK", ["lines", "timestep", "natoms", "cell", "pbc", "atom_fields"] diff --git a/aiida_lammps/common/reaxff_convert.py b/aiida_lammps/common/reaxff_convert.py index b73a6fa..9b59434 100644 --- a/aiida_lammps/common/reaxff_convert.py +++ b/aiida_lammps/common/reaxff_convert.py @@ -7,7 +7,6 @@ from aiida_lammps.validation import validate_against_schema - INDEX_SEP = "-" KEYS_GLOBAL = ( diff --git a/aiida_lammps/data/pot_plugins/base_plugin.py b/aiida_lammps/data/pot_plugins/base_plugin.py index f81e742..38bdc1b 100644 --- a/aiida_lammps/data/pot_plugins/base_plugin.py +++ b/aiida_lammps/data/pot_plugins/base_plugin.py @@ -1,10 +1,7 @@ import abc -import six - -@six.add_metaclass(abc.ABCMeta) -class PotentialAbstract: +class PotentialAbstract(abc.ABC): """Abstract class for potential plugins.""" def __init__(self, data): diff --git a/aiida_lammps/data/pot_plugins/reaxff.py b/aiida_lammps/data/pot_plugins/reaxff.py index 2de53e7..8b96f73 100644 --- a/aiida_lammps/data/pot_plugins/reaxff.py +++ b/aiida_lammps/data/pot_plugins/reaxff.py @@ -112,11 +112,12 @@ def get_input_potential_lines(self): ) lammps_input_text += "fix qeq all qeq/reax 1 0.0 10.0 1e-6 reax/c\n" if control.get("fix_modify_qeq", True): - # TODO in conda-forge/osx-64::lammps-2019.06.05-py36_openmpi_5, + # TODO #15 in conda-forge/osx-64::lammps-2019.06.05-py36_openmpi_5, # an error is raised: ERROR: Illegal fix_modify command (src/fix.cpp:147) # posted question to lammps-users@lists.sourceforge.net # 'Using qeq/reax fix_modify energy in recent versions of LAMMPS' - lammps_input_text += "fix_modify qeq energy yes\n" + # lammps_input_text += "fix_modify qeq energy yes\n" + pass lammps_input_text += "compute reax all pair reax/c\n" return lammps_input_text diff --git a/aiida_lammps/data/potential.py b/aiida_lammps/data/potential.py index fe6c1a0..ba93375 100644 --- a/aiida_lammps/data/potential.py +++ b/aiida_lammps/data/potential.py @@ -1,8 +1,8 @@ from hashlib import md5 -import six +from io import StringIO from aiida.orm import Data -from aiida.plugins.entry_point import load_entry_point, get_entry_point_names +from aiida.plugins.entry_point import get_entry_point_names, load_entry_point class EmpiricalPotential(Data): @@ -66,8 +66,7 @@ def set_data(self, potential_type, data=None): self.set_attribute( "md5|input_lines", md5(pot_lines.encode("utf-8")).hexdigest() ) - with self.open(self.pot_lines_fname, mode="w") as handle: - handle.write(six.ensure_text(pot_lines)) + self.put_object_from_filelike(StringIO(pot_lines), self.pot_lines_fname) # store external files required by the potential external_files = [] @@ -76,8 +75,7 @@ def set_data(self, potential_type, data=None): "md5|{}".format(fname.replace(".", "_")), md5(content.encode("utf-8")).hexdigest(), ) - with self.open(fname, mode="w") as handle: - handle.write(six.ensure_text(content)) + self.put_object_from_filelike(StringIO(content), fname) external_files.append(fname) self.set_attribute("external_files", sorted(external_files)) diff --git a/aiida_lammps/data/trajectory.py b/aiida_lammps/data/trajectory.py index 6b311d6..468957b 100644 --- a/aiida_lammps/data/trajectory.py +++ b/aiida_lammps/data/trajectory.py @@ -1,15 +1,13 @@ import io import tempfile -from zipfile import ZipFile, ZIP_DEFLATED - -import six +from zipfile import ZIP_DEFLATED, ZipFile from aiida.orm import Data from aiida_lammps.common.parse_trajectory import ( + create_structure, iter_trajectories, parse_step, - create_structure, ) @@ -41,7 +39,7 @@ def __init__(self, fileobj=None, aliases=None, **kwargs): super(LammpsTrajectory, self).__init__(**kwargs) if fileobj is not None: - if isinstance(fileobj, six.string_types): + if isinstance(fileobj, str): with io.open(fileobj) as handle: self.set_from_fileobj(handle, aliases) else: @@ -119,8 +117,9 @@ def set_from_fileobj(self, fileobj, aliases=None): temp_handle, self._traj_filename, mode="wb", encoding=None ) - with self.open(self._timestep_filename, "w") as handle: - handle.write(six.ensure_text(" ".join([str(t) for t in time_steps]))) + self.put_object_from_filelike( + io.StringIO(" ".join([str(t) for t in time_steps])), self._timestep_filename + ) self.set_attribute("number_steps", len(time_steps)) self.set_attribute("number_atoms", number_atoms) @@ -162,9 +161,9 @@ def get_step_string(self, step_idx): with ZipFile( handle, "r", self.get_attribute("compression_method") ) as zip_file: - with zip_file.open(zip_name) as step_file: + with zip_file.open(zip_name, "r") as step_file: content = step_file.read() - return six.ensure_text(content) + return content.decode("utf8") def get_step_data(self, step_idx): """Return parsed data, for a specific trajectory step.""" @@ -180,7 +179,7 @@ def iter_step_strings(self): zip_name = "{}{}".format(self.get_attribute("zip_prefix"), step_idx) with zip_file.open(zip_name) as step_file: content = step_file.read() - yield six.ensure_text(content) + yield content def get_step_structure( self, @@ -218,4 +217,4 @@ def write_as_lammps(self, handle): """Write out the lammps trajectory to file.""" for string in self.iter_step_strings(): handle.write(string) - handle.write(six.ensure_text("\n")) + handle.write("\n") diff --git a/aiida_lammps/parsers/dynaphopy.py b/aiida_lammps/parsers/dynaphopy.py index 786f9e7..798aafa 100644 --- a/aiida_lammps/parsers/dynaphopy.py +++ b/aiida_lammps/parsers/dynaphopy.py @@ -1,13 +1,13 @@ # Not working with Aiida 1.0 -from aiida.parsers.parser import Parser from aiida.orm.data.parameter import ParameterData +from aiida.parsers.parser import Parser +from aiida_phonopy.common.raw_parsers import parse_FORCE_CONSTANTS from aiida_lammps.common.raw_parsers import ( parse_dynaphopy_output, parse_quasiparticle_data, ) -from aiida_phonopy.common.raw_parsers import parse_FORCE_CONSTANTS class DynaphopyParser(Parser): diff --git a/aiida_lammps/parsers/lammps/base.py b/aiida_lammps/parsers/lammps/base.py index 21af8e2..92e409a 100644 --- a/aiida_lammps/parsers/lammps/base.py +++ b/aiida_lammps/parsers/lammps/base.py @@ -3,8 +3,8 @@ import os import traceback -from aiida.parsers.parser import Parser from aiida.common import exceptions +from aiida.parsers.parser import Parser from aiida_lammps import __version__ as aiida_lammps_version from aiida_lammps.common.raw_parsers import read_log_file diff --git a/aiida_lammps/parsers/lammps/force.py b/aiida_lammps/parsers/lammps/force.py index 9f93da0..d7443f6 100644 --- a/aiida_lammps/parsers/lammps/force.py +++ b/aiida_lammps/parsers/lammps/force.py @@ -1,13 +1,10 @@ +from aiida.orm import ArrayData, Dict import numpy as np -from aiida.orm import Dict, ArrayData - -from aiida_lammps.parsers.lammps.base import LAMMPSBaseParser +from aiida_lammps.common.parse_trajectory import TRAJ_BLOCK # noqa: F401 +from aiida_lammps.common.parse_trajectory import iter_trajectories from aiida_lammps.common.raw_parsers import get_units_dict -from aiida_lammps.common.parse_trajectory import ( - iter_trajectories, - TRAJ_BLOCK, # noqa: F401 -) +from aiida_lammps.parsers.lammps.base import LAMMPSBaseParser class ForceParser(LAMMPSBaseParser): diff --git a/aiida_lammps/parsers/lammps/md.py b/aiida_lammps/parsers/lammps/md.py index 96201bc..b4f3653 100644 --- a/aiida_lammps/parsers/lammps/md.py +++ b/aiida_lammps/parsers/lammps/md.py @@ -1,11 +1,10 @@ import traceback +from aiida.orm import ArrayData, Dict import numpy as np -from aiida.orm import Dict, ArrayData - -from aiida_lammps.data.trajectory import LammpsTrajectory from aiida_lammps.common.raw_parsers import convert_units, get_units_dict +from aiida_lammps.data.trajectory import LammpsTrajectory from aiida_lammps.parsers.lammps.base import LAMMPSBaseParser diff --git a/aiida_lammps/parsers/lammps/md_multi.py b/aiida_lammps/parsers/lammps/md_multi.py index be6efc3..de2206b 100644 --- a/aiida_lammps/parsers/lammps/md_multi.py +++ b/aiida_lammps/parsers/lammps/md_multi.py @@ -3,12 +3,11 @@ import re import traceback +from aiida.orm import ArrayData, Dict import numpy as np -from aiida.orm import Dict, ArrayData - -from aiida_lammps.data.trajectory import LammpsTrajectory from aiida_lammps.common.raw_parsers import convert_units, get_units_dict +from aiida_lammps.data.trajectory import LammpsTrajectory from aiida_lammps.parsers.lammps.base import LAMMPSBaseParser diff --git a/aiida_lammps/parsers/lammps/optimize.py b/aiida_lammps/parsers/lammps/optimize.py index 74643fb..7c99d8f 100644 --- a/aiida_lammps/parsers/lammps/optimize.py +++ b/aiida_lammps/parsers/lammps/optimize.py @@ -2,8 +2,8 @@ from aiida.orm import Dict -from aiida_lammps.data.trajectory import LammpsTrajectory from aiida_lammps.common.raw_parsers import get_units_dict +from aiida_lammps.data.trajectory import LammpsTrajectory from aiida_lammps.parsers.lammps.base import LAMMPSBaseParser diff --git a/aiida_lammps/tests/test_calculations.py b/aiida_lammps/tests/test_calculations.py index 531234e..97631ec 100644 --- a/aiida_lammps/tests/test_calculations.py +++ b/aiida_lammps/tests/test_calculations.py @@ -1,9 +1,8 @@ -import pytest - -from aiida.engine import run_get_node from aiida.cmdline.utils.common import get_calcjob_report +from aiida.engine import run_get_node from aiida.orm import Dict from aiida.plugins import CalculationFactory, DataFactory +import pytest import aiida_lammps.tests.utils as tests diff --git a/aiida_lammps/tests/test_calculations/test_input_creation_reaxff_lammps_md_.txt b/aiida_lammps/tests/test_calculations/test_input_creation_reaxff_lammps_md_.txt index 3908b84..442cbeb 100644 --- a/aiida_lammps/tests/test_calculations/test_input_creation_reaxff_lammps_md_.txt +++ b/aiida_lammps/tests/test_calculations/test_input_creation_reaxff_lammps_md_.txt @@ -6,7 +6,6 @@ read_data input.data pair_style reax/c potential.control safezone 1.6 pair_coeff * * potential.pot A B fix qeq all qeq/reax 1 0.0 10.0 1e-6 reax/c -fix_modify qeq energy yes compute reax all pair reax/c neighbor 0.3 bin neigh_modify every 1 delay 0 check no diff --git a/aiida_lammps/tests/test_calculations/test_input_creation_reaxff_lammps_md_multi_.txt b/aiida_lammps/tests/test_calculations/test_input_creation_reaxff_lammps_md_multi_.txt index 1376529..c21ab3f 100644 --- a/aiida_lammps/tests/test_calculations/test_input_creation_reaxff_lammps_md_multi_.txt +++ b/aiida_lammps/tests/test_calculations/test_input_creation_reaxff_lammps_md_multi_.txt @@ -11,7 +11,6 @@ read_data input.data pair_style reax/c potential.control safezone 1.6 pair_coeff * * potential.pot A B fix qeq all qeq/reax 1 0.0 10.0 1e-6 reax/c -fix_modify qeq energy yes compute reax all pair reax/c # General Setup diff --git a/aiida_lammps/tests/test_generate_structure.py b/aiida_lammps/tests/test_generate_structure.py index c5bdc43..ac56978 100644 --- a/aiida_lammps/tests/test_generate_structure.py +++ b/aiida_lammps/tests/test_generate_structure.py @@ -1,5 +1,5 @@ import pytest -import six + from aiida_lammps.common.generate_structure import generate_lammps_structure @@ -9,4 +9,4 @@ def test_generate(db_test_app, get_structure_data, structure, file_regression): structure = get_structure_data(structure) text, transform = generate_lammps_structure(structure, round_dp=8) - file_regression.check(six.ensure_text(text)) + file_regression.check(text) diff --git a/aiida_lammps/tests/test_parsers.py b/aiida_lammps/tests/test_parsers.py index 0d89d96..a0394f6 100644 --- a/aiida_lammps/tests/test_parsers.py +++ b/aiida_lammps/tests/test_parsers.py @@ -1,27 +1,26 @@ +from io import StringIO from textwrap import dedent -import pytest -import six -from aiida.orm import FolderData + from aiida.cmdline.utils.common import get_calcjob_report +from aiida.orm import FolderData +from aiida.plugins import ParserFactory +import pytest def get_log(): - return six.ensure_text( - dedent( - """\ + return dedent( + """\ units metal final_energy: 2.0 final_cell: 0 1 0 0 1 0 0 1 0 final_stress: 0 0 0 0 0 0 """ - ) ) def get_traj_force(): - return six.ensure_text( - dedent( - """\ + return dedent( + """\ ITEM: TIMESTEP 0 ITEM: NUMBER OF ATOMS @@ -38,7 +37,6 @@ def get_traj_force(): S 25.5468278966 20.6615772179 -0.0000000000 S 25.5468278966 -20.6615772179 0.0000000000 """ - ) ) @@ -50,9 +48,10 @@ def test_missing_log(db_test_app, plugin_name): retrieved = FolderData() calc_node = db_test_app.generate_calcjob_node(plugin_name, retrieved) + parser = ParserFactory(plugin_name) with db_test_app.sandbox_folder() as temp_path: - results, calcfunction = db_test_app.parse_from_node( - plugin_name, calc_node, temp_path.abspath + results, calcfunction = parser.parse_from_node( + calc_node, retrieved_temporary_folder=temp_path.abspath ) assert calcfunction.is_finished, calcfunction.exception @@ -69,17 +68,15 @@ def test_missing_log(db_test_app, plugin_name): def test_missing_traj(db_test_app, plugin_name): retrieved = FolderData() - with retrieved.open("log.lammps", "w") as handle: - handle.write(get_log()) - with retrieved.open("_scheduler-stdout.txt", "w"): - pass - with retrieved.open("_scheduler-stderr.txt", "w"): - pass + retrieved.put_object_from_filelike(StringIO(get_log()), "log.lammps") + retrieved.put_object_from_filelike(StringIO(""), "_scheduler-stdout.txt") + retrieved.put_object_from_filelike(StringIO(""), "_scheduler-stderr.txt") calc_node = db_test_app.generate_calcjob_node(plugin_name, retrieved) + parser = ParserFactory(plugin_name) with db_test_app.sandbox_folder() as temp_path: - results, calcfunction = db_test_app.parse_from_node( - plugin_name, calc_node, temp_path.abspath + results, calcfunction = parser.parse_from_node( + calc_node, retrieved_temporary_folder=temp_path.abspath ) assert calcfunction.is_finished, calcfunction.exception @@ -96,21 +93,22 @@ def test_missing_traj(db_test_app, plugin_name): def test_empty_log(db_test_app, plugin_name): retrieved = FolderData() - with retrieved.open("log.lammps", "w"): - pass - with retrieved.open("trajectory.lammpstrj", "w"): - pass - with retrieved.open("_scheduler-stdout.txt", "w"): - pass - with retrieved.open("_scheduler-stderr.txt", "w"): - pass + for filename in [ + "log.lammps", + "trajectory.lammpstrj", + "_scheduler-stdout.txt", + "_scheduler-stderr.txt", + ]: + retrieved.put_object_from_filelike(StringIO(""), filename) calc_node = db_test_app.generate_calcjob_node(plugin_name, retrieved) + parser = ParserFactory(plugin_name) + with db_test_app.sandbox_folder() as temp_path: with temp_path.open("x-trajectory.lammpstrj", "w"): pass - results, calcfunction = db_test_app.parse_from_node( - plugin_name, calc_node, temp_path.abspath + results, calcfunction = parser.parse_from_node( + calc_node, retrieved_temporary_folder=temp_path.abspath ) assert calcfunction.is_finished, calcfunction.exception @@ -127,21 +125,21 @@ def test_empty_log(db_test_app, plugin_name): def test_empty_traj(db_test_app, plugin_name): retrieved = FolderData() - with retrieved.open("log.lammps", "w") as handle: - handle.write(get_log()) - with retrieved.open("trajectory.lammpstrj", "w") as handle: - pass - with retrieved.open("_scheduler-stdout.txt", "w"): - pass - with retrieved.open("_scheduler-stderr.txt", "w"): - pass + retrieved.put_object_from_filelike(StringIO(get_log()), "log.lammps") + for filename in [ + "trajectory.lammpstrj", + "_scheduler-stdout.txt", + "_scheduler-stderr.txt", + ]: + retrieved.put_object_from_filelike(StringIO(""), filename) calc_node = db_test_app.generate_calcjob_node(plugin_name, retrieved) + parser = ParserFactory(plugin_name) with db_test_app.sandbox_folder() as temp_path: - with temp_path.open("x-trajectory.lammpstrj", "w") as handle: + with temp_path.open("x-trajectory.lammpstrj", "w"): pass - results, calcfunction = db_test_app.parse_from_node( - plugin_name, calc_node, temp_path.abspath + results, calcfunction = parser.parse_from_node( + calc_node, retrieved_temporary_folder=temp_path.abspath ) assert calcfunction.is_finished, calcfunction.exception @@ -158,22 +156,23 @@ def test_empty_traj(db_test_app, plugin_name): def test_run_error(db_test_app, plugin_name): retrieved = FolderData() - with retrieved.open("log.lammps", "w") as handle: - handle.write(get_log()) - with retrieved.open("x-trajectory.lammpstrj", "w") as handle: - handle.write(get_traj_force()) - with retrieved.open("_scheduler-stdout.txt", "w") as handle: - handle.write(six.ensure_text("ERROR description")) - with retrieved.open("_scheduler-stderr.txt", "w"): - pass + retrieved.put_object_from_filelike(StringIO(get_log()), "log.lammps") + retrieved.put_object_from_filelike( + StringIO(get_traj_force()), "x-trajectory.lammpstrj" + ) + retrieved.put_object_from_filelike( + StringIO("ERROR description"), "_scheduler-stdout.txt" + ) + retrieved.put_object_from_filelike(StringIO(""), "_scheduler-stderr.txt") calc_node = db_test_app.generate_calcjob_node(plugin_name, retrieved) + parser = ParserFactory(plugin_name) with db_test_app.sandbox_folder() as temp_path: with temp_path.open("x-trajectory.lammpstrj", "w") as handle: handle.write(get_traj_force()) - results, calcfunction = db_test_app.parse_from_node( - plugin_name, calc_node, temp_path.abspath + results, calcfunction = parser.parse_from_node( + calc_node, retrieved_temporary_folder=temp_path.abspath ) print(get_calcjob_report(calc_node)) diff --git a/aiida_lammps/tests/test_potential_data.py b/aiida_lammps/tests/test_potential_data.py index ffd5262..a6d6fb4 100644 --- a/aiida_lammps/tests/test_potential_data.py +++ b/aiida_lammps/tests/test_potential_data.py @@ -1,5 +1,5 @@ import pytest -import six + from aiida_lammps.data.potential import EmpiricalPotential @@ -28,9 +28,7 @@ def test_potential_files( ): potential = get_potential_data(potential_type) node = EmpiricalPotential(type=potential.type, data=potential.data) - file_regression.check( - six.ensure_text(node.get_object_content("potential.pot", "r")) - ) + file_regression.check(node.get_object_content("potential.pot", "r")) @pytest.mark.parametrize( @@ -39,4 +37,4 @@ def test_potential_files( def test_input_lines(db_test_app, get_potential_data, potential_type, file_regression): potential = get_potential_data(potential_type) node = EmpiricalPotential(type=potential.type, data=potential.data) - file_regression.check(six.ensure_text(node.get_input_lines())) + file_regression.check(node.get_input_lines()) diff --git a/aiida_lammps/tests/test_potential_data/test_init_reaxff_.yml b/aiida_lammps/tests/test_potential_data/test_init_reaxff_.yml index 8ef459e..2debdef 100644 --- a/aiida_lammps/tests/test_potential_data/test_init_reaxff_.yml +++ b/aiida_lammps/tests/test_potential_data/test_init_reaxff_.yml @@ -6,7 +6,7 @@ default_units: real external_files: - potential.control - potential.pot -md5|input_lines: 9a3f3e85cf7086b0d0b262b87c2d3019 +md5|input_lines: 924b38b68db0a9ed5779c054db4d62ac md5|potential_control: ae19ae9eb3dd37edf8aecbd147e48aa0 md5|potential_pot: eec691f028ac0fc05aa17501b7cca734 potential_type: reaxff diff --git a/aiida_lammps/tests/test_potential_data/test_input_lines_reaxff_.txt b/aiida_lammps/tests/test_potential_data/test_input_lines_reaxff_.txt index 0ae30bc..93f2b63 100644 --- a/aiida_lammps/tests/test_potential_data/test_input_lines_reaxff_.txt +++ b/aiida_lammps/tests/test_potential_data/test_input_lines_reaxff_.txt @@ -1,5 +1,4 @@ pair_style reax/c potential.control safezone 1.6 pair_coeff * * potential.pot {kind_symbols} fix qeq all qeq/reax 1 0.0 10.0 1e-6 reax/c -fix_modify qeq energy yes compute reax all pair reax/c diff --git a/aiida_lammps/tests/test_reaxff_parse.py b/aiida_lammps/tests/test_reaxff_parse.py index 5d54b05..14e3011 100644 --- a/aiida_lammps/tests/test_reaxff_parse.py +++ b/aiida_lammps/tests/test_reaxff_parse.py @@ -1,10 +1,7 @@ from textwrap import dedent -import six - from aiida_lammps.common.reaxff_convert import read_lammps_format, write_lammps_format - lammps_file1 = dedent( """\ Reactive MD-force field: Cr/O/Fe/S/C/H force field 2014 @@ -439,7 +436,7 @@ def test_read_lammps_format(data_regression): def test_round_trip_lammps_format(file_regression): data = read_lammps_format(lammps_file1.splitlines()) output = write_lammps_format(data) - file_regression.check(six.ensure_text(output)) + file_regression.check(output) def test_read_lammps_format2(data_regression): @@ -450,4 +447,4 @@ def test_read_lammps_format2(data_regression): def test_round_trip_lammps_format2(file_regression): data = read_lammps_format(lammps_file2.splitlines()) output = write_lammps_format(data) - file_regression.check(six.ensure_text(output)) + file_regression.check(output) diff --git a/aiida_lammps/tests/test_trajectory.py b/aiida_lammps/tests/test_trajectory.py index c34daa6..82d7f60 100644 --- a/aiida_lammps/tests/test_trajectory.py +++ b/aiida_lammps/tests/test_trajectory.py @@ -1,9 +1,9 @@ import io import os -from aiida_lammps.tests.utils import TEST_DIR, recursive_round -from aiida_lammps.common.parse_trajectory import iter_trajectories, create_structure +from aiida_lammps.common.parse_trajectory import create_structure, iter_trajectories from aiida_lammps.data.trajectory import LammpsTrajectory +from aiida_lammps.tests.utils import TEST_DIR, recursive_round def test_iter_trajectories(data_regression): diff --git a/aiida_lammps/tests/utils.py b/aiida_lammps/tests/utils.py index b6006d6..aed28eb 100644 --- a/aiida_lammps/tests/utils.py +++ b/aiida_lammps/tests/utils.py @@ -1,4 +1,4 @@ -from collections import Mapping +from collections.abc import Mapping from contextlib import contextmanager import distutils.spawn import os @@ -6,8 +6,6 @@ import subprocess import sys -import six - TEST_DIR = os.path.dirname(os.path.realpath(__file__)) @@ -17,9 +15,7 @@ def lammps_version(executable="lammps"): we assume `lammps -h` returns e.g. 'LAMMPS (10 Feb 2015)' or 'Large-scale Atomic/Molecular Massively Parallel Simulator - 5 Jun 2019' """ - p = subprocess.Popen([executable, "-h"], stdout=subprocess.PIPE) - stdout, stderr = p.communicate() - out_text = six.ensure_str(stdout) + out_text = subprocess.check_output([executable, "-h"], text=True) match = re.search(r"LAMMPS \((.*)\)", out_text) if match: return match.group(1) @@ -35,7 +31,7 @@ def lammps_version(executable="lammps"): def get_path_to_executable(executable): - """ Get path to local executable. + """Get path to local executable. :param executable: Name of executable in the $PATH variable :type executable: str @@ -74,14 +70,14 @@ def get_or_create_local_computer(work_directory, name="localhost"): aiida.orm.computers.Computer """ - from aiida.orm import Computer from aiida.common import NotExistent + from aiida.orm import Computer try: - computer = Computer.objects.get(name=name) + computer = Computer.objects.get(label=name) except NotExistent: computer = Computer( - name=name, + label=name, hostname="localhost", description=("localhost computer, " "set up by aiida_lammps tests"), transport_type="local", @@ -96,15 +92,15 @@ def get_or_create_local_computer(work_directory, name="localhost"): def get_or_create_code(entry_point, computer, executable, exec_path=None): """Setup code on localhost computer""" - from aiida.orm import Code, Computer from aiida.common import NotExistent + from aiida.orm import Code, Computer - if isinstance(computer, six.string_types): - computer = Computer.objects.get(name=computer) + if isinstance(computer, str): + computer = Computer.objects.get(label=computer) try: code = Code.objects.get( - label="{}-{}@{}".format(entry_point, executable, computer.name) + label="{}-{}-{}".format(entry_point, executable, computer.label) ) except NotExistent: if exec_path is None: @@ -112,7 +108,7 @@ def get_or_create_code(entry_point, computer, executable, exec_path=None): code = Code( input_plugin_name=entry_point, remote_computer_exec=[computer, exec_path] ) - code.label = "{}-{}@{}".format(entry_point, executable, computer.name) + code.label = "{}-{}-{}".format(entry_point, executable, computer.label) code.store() return code @@ -159,66 +155,6 @@ def recursive_round(ob, dp, apply_lists=False): return ob -# TODO this can be removed once aiidateam/aiida-core#3061 is implemented -def parse_from_node(cls, node, store_provenance=True, retrieved_temporary_folder=None): - """Parse the outputs directly from the `CalcJobNode`. - - If `store_provenance` is set to False, a `CalcFunctionNode` will still be generated, but it will not be stored. - It's storing method will also be disabled, making it impossible to store, because storing it afterwards would - not have the expected effect, as the outputs it produced will not be stored with it. - - This method is useful to test parsing in unit tests where a `CalcJobNode` can be mocked without actually having - to run a `CalcJob`. It can also be useful to actually re-perform the parsing of a completed `CalcJob` with a - different parser. - - :param node: a `CalcJobNode` instance - :param store_provenance: bool, if True will store the parsing as a `CalcFunctionNode` in the provenance - :return: a tuple of the parsed results and the `CalcFunctionNode` representing the process of parsing - """ - from aiida.engine import calcfunction, Process - from aiida.orm import Str - - parser = cls(node=node) - - @calcfunction - def parse_calcfunction(**kwargs): - """A wrapper function that will turn calling the `Parser.parse` method into a `CalcFunctionNode`. - - .. warning:: This implementation of a `calcfunction` uses the `Process.current` to circumvent the limitation - of not being able to return both output nodes as well as an exit code. However, since this calculation - function is supposed to emulate the parsing of a `CalcJob`, which *does* have that capability, I have - to use this method. This method should however not be used in process functions, in other words: - - Do not try this at home! - - :param kwargs: keyword arguments that are passed to `Parser.parse` after it has been constructed - """ - if "retrieved_temporary_folder" in kwargs: - string = kwargs.pop("retrieved_temporary_folder").value - kwargs["retrieved_temporary_folder"] = string - - exit_code = parser.parse(**kwargs) - outputs = parser.outputs - - if exit_code and exit_code.status: - # In the case that an exit code was returned, still attach all the registered outputs on the current - # process as well, which should represent this `CalcFunctionNode`. Otherwise the caller of the - # `parse_from_node` method will get an empty dictionary as a result, despite the `Parser.parse` method - # having registered outputs. - process = Process.current() - process.out_many(outputs) - return exit_code - - return dict(outputs) - - inputs = {"metadata": {"store_provenance": store_provenance}} - inputs.update(parser.get_outputs_for_parsing()) - if retrieved_temporary_folder is not None: - inputs["retrieved_temporary_folder"] = Str(retrieved_temporary_folder) - - return parse_calcfunction.run_get_node(**inputs) - - class AiidaTestApp(object): def __init__(self, work_directory, executable_map, environment=None): """a class providing methods for testing purposes @@ -354,10 +290,12 @@ def generate_calcjob_node( entry_point = format_entry_point_string("aiida.calculations", entry_point_name) node = CalcJobNode(computer=computer, process_type=entry_point) - spec_options = process.spec().inputs["metadata"]["options"] - # TODO post v1.0.0b2, this can be replaced with process.spec_options node.set_options( - {k: v.default for k, v in spec_options.items() if v.has_default()} + { + k: v.default() if callable(v.default) else v.default + for k, v in process.spec_options.items() + if v.has_default() + } ) node.set_option("resources", {"num_machines": 1, "num_mpiprocs_per_machine": 1}) node.set_option("max_wallclock_seconds", 1800) @@ -414,21 +352,3 @@ def generate_calcinfo(entry_point_name, folder, inputs=None): calc_info = process.prepare_for_submission(folder) return calc_info - - @staticmethod - def parse_from_node(entry_point_name, node, retrieved_temporary_folder=None): - """Parse the outputs directly from the `CalcJobNode`. - - Parameters - ---------- - entry_point_name : str - entry point name of the parser class - - """ - from aiida.plugins import ParserFactory - - return parse_from_node( - ParserFactory(entry_point_name), - node, - retrieved_temporary_folder=retrieved_temporary_folder, - ) diff --git a/aiida_lammps/validation/utils.py b/aiida_lammps/validation/utils.py index 6fbecb0..db3c19c 100644 --- a/aiida_lammps/validation/utils.py +++ b/aiida_lammps/validation/utils.py @@ -7,7 +7,6 @@ import importlib_resources import jsonschema -import six from aiida_lammps.validation import schemas @@ -50,7 +49,7 @@ def load_validator(schema): the validator to use """ - if isinstance(schema, six.string_types): + if isinstance(schema, str): schema = load_schema(schema) validator_cls = jsonschema.validators.validator_for(schema) diff --git a/conftest.py b/conftest.py index 850154d..d20dbfe 100644 --- a/conftest.py +++ b/conftest.py @@ -7,12 +7,13 @@ import shutil import tempfile -from aiida.manage.fixtures import fixture_manager from aiida.plugins import DataFactory import numpy as np import pytest -from aiida_lammps.tests.utils import AiidaTestApp, TEST_DIR +from aiida_lammps.tests.utils import TEST_DIR, AiidaTestApp + +pytest_plugins = ["aiida.manage.tests.pytest_fixtures"] def pytest_addoption(parser): @@ -54,18 +55,8 @@ def pytest_report_header(config): ] -@pytest.fixture(scope="session") -def aiida_environment(): - """Setup a test profile for the duration of the tests.""" - # TODO this is required locally for click - # (see https://click.palletsprojects.com/en/7.x/python3/) - os.environ["LC_ALL"] = "en_US.UTF-8" - with fixture_manager() as fixture_mgr: - yield fixture_mgr - - @pytest.fixture(scope="function") -def db_test_app(aiida_environment, pytestconfig): +def db_test_app(aiida_profile, pytestconfig): """Clear the database after each test.""" exec_name = pytestconfig.getoption("lammps_exec") or "lammps" executables = { @@ -82,8 +73,8 @@ def db_test_app(aiida_environment, pytestconfig): else: work_directory = tempfile.mkdtemp() - yield AiidaTestApp(work_directory, executables, environment=aiida_environment) - aiida_environment.reset_db() + yield AiidaTestApp(work_directory, executables, environment=aiida_profile) + aiida_profile.reset_db() if not test_workdir: shutil.rmtree(work_directory) @@ -92,8 +83,7 @@ def db_test_app(aiida_environment, pytestconfig): @pytest.fixture(scope="function") def get_structure_data(): def _get_structure_data(pkey): - """ return test structure data - """ + """return test structure data""" if pkey == "Fe": cell = [ @@ -232,7 +222,7 @@ def _get_structure_data(pkey): @pytest.fixture(scope="function") def get_potential_data(get_structure_data): def _get_potential_data(pkey): - """ return data to create a potential, + """return data to create a potential, and accompanying structure data and expected output data to test it with """ if pkey == "eam": @@ -287,8 +277,8 @@ def _get_potential_data(pkey): elif pkey == "reaxff": from aiida_lammps.common.reaxff_convert import ( - read_lammps_format, filter_by_species, + read_lammps_format, ) pair_style = "reaxff" diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..6ae5ef5 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,29 @@ +# A small configuration for use in local CI testing, +# if you wish to control the rabbitmq used. + +# Simply install docker, then run: +# $ docker-compose -f docker-compose.yml up -d + +# and to power down, after testing: +# $ docker-compose -f docker-compose.yml down + +# you can monitor rabbitmq use at: http://localhost:15672 + +version: '3.4' + +services: + + rabbit: + image: rabbitmq:3.8.3-management + container_name: aiida-rmq + environment: + RABBITMQ_DEFAULT_USER: guest + RABBITMQ_DEFAULT_PASS: guest + ports: + - '5672:5672' + - '15672:15672' + healthcheck: + test: rabbitmq-diagnostics -q ping + interval: 30s + timeout: 30s + retries: 5 diff --git a/examples/launch_dynaphopy_si.py b/examples/launch_dynaphopy_si.py index afe05d0..eb0bab8 100644 --- a/examples/launch_dynaphopy_si.py +++ b/examples/launch_dynaphopy_si.py @@ -1,7 +1,7 @@ -from aiida.plugins import CalculationFactory -from aiida.orm import Code, Dict, StructureData -from aiida.engine import run_get_node from aiida.common.extendeddicts import AttributeDict +from aiida.engine import run_get_node +from aiida.orm import Code, Dict, StructureData +from aiida.plugins import CalculationFactory import numpy as np if __name__ == "__main__": diff --git a/examples/launch_lammps_combinate.py b/examples/launch_lammps_combinate.py index 87de882..c29e99d 100644 --- a/examples/launch_lammps_combinate.py +++ b/examples/launch_lammps_combinate.py @@ -1,12 +1,11 @@ # This calculation requires also phonopy plugin to work -from aiida.plugins import CalculationFactory -from aiida.orm import Code, Dict, StructureData -from aiida.engine import run_get_node from aiida.common.extendeddicts import AttributeDict +from aiida.engine import run_get_node +from aiida.orm import Code, Dict, StructureData +from aiida.plugins import CalculationFactory import numpy as np - if __name__ == "__main__": from aiida import load_profile # noqa: F401 diff --git a/examples/launch_lammps_force_gan.py b/examples/launch_lammps_force_gan.py index 7c0f0e9..544b9b3 100644 --- a/examples/launch_lammps_force_gan.py +++ b/examples/launch_lammps_force_gan.py @@ -1,10 +1,10 @@ -from aiida.plugins import CalculationFactory -from aiida.orm import Code, StructureData -from aiida.engine import run_get_node from aiida.common.extendeddicts import AttributeDict -from aiida_lammps.data.potential import EmpiricalPotential +from aiida.engine import run_get_node +from aiida.orm import Code, StructureData +from aiida.plugins import CalculationFactory import numpy as np +from aiida_lammps.data.potential import EmpiricalPotential if __name__ == "__main__": diff --git a/examples/launch_lammps_md_si.py b/examples/launch_lammps_md_si.py index cc3c80a..d301fdf 100644 --- a/examples/launch_lammps_md_si.py +++ b/examples/launch_lammps_md_si.py @@ -1,11 +1,12 @@ from aiida import load_profile -from aiida.plugins import CalculationFactory -from aiida.orm import Code, Dict, StructureData -from aiida.engine import run_get_node from aiida.common.extendeddicts import AttributeDict -from aiida_lammps.data.potential import EmpiricalPotential +from aiida.engine import run_get_node +from aiida.orm import Code, Dict, StructureData +from aiida.plugins import CalculationFactory import numpy as np +from aiida_lammps.data.potential import EmpiricalPotential + if __name__ == "__main__": load_profile() diff --git a/examples/launch_lammps_optimization_fe.py b/examples/launch_lammps_optimization_fe.py index fca3168..b9c8ee5 100644 --- a/examples/launch_lammps_optimization_fe.py +++ b/examples/launch_lammps_optimization_fe.py @@ -1,11 +1,10 @@ -from aiida.plugins import CalculationFactory -from aiida.orm import Code, Dict, StructureData -from aiida.engine import run_get_node from aiida.common.extendeddicts import AttributeDict -from aiida_lammps.data.potential import EmpiricalPotential - +from aiida.engine import run_get_node +from aiida.orm import Code, Dict, StructureData +from aiida.plugins import CalculationFactory import numpy as np +from aiida_lammps.data.potential import EmpiricalPotential if __name__ == "__main__": diff --git a/examples/launch_lammps_optimization_gan.py b/examples/launch_lammps_optimization_gan.py index 29d9ff7..51233c0 100644 --- a/examples/launch_lammps_optimization_gan.py +++ b/examples/launch_lammps_optimization_gan.py @@ -1,10 +1,10 @@ -from aiida.plugins import CalculationFactory -from aiida.orm import Code, Dict, StructureData -from aiida.engine import run_get_node from aiida.common.extendeddicts import AttributeDict -from aiida_lammps.data.potential import EmpiricalPotential +from aiida.engine import run_get_node +from aiida.orm import Code, Dict, StructureData +from aiida.plugins import CalculationFactory import numpy as np +from aiida_lammps.data.potential import EmpiricalPotential if __name__ == "__main__": diff --git a/examples/launch_lammps_optimization_lj.py b/examples/launch_lammps_optimization_lj.py index faa70e8..a421e91 100644 --- a/examples/launch_lammps_optimization_lj.py +++ b/examples/launch_lammps_optimization_lj.py @@ -1,10 +1,10 @@ -from aiida.plugins import CalculationFactory -from aiida.orm import Code, Dict, StructureData -from aiida.engine import run_get_node from aiida.common.extendeddicts import AttributeDict -from aiida_lammps.data.potential import EmpiricalPotential +from aiida.engine import run_get_node +from aiida.orm import Code, Dict, StructureData +from aiida.plugins import CalculationFactory import numpy as np +from aiida_lammps.data.potential import EmpiricalPotential if __name__ == "__main__": diff --git a/pyproject.toml b/pyproject.toml index 91fef15..653cf83 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,16 @@ [build-system] # Minimum requirements for the build system to execute. requires = ["setuptools", "wheel", "reentry>=1.3.0"] + +[tool.isort] +skip = ["venv"] +# Force imports to be sorted by module, independent of import type +force_sort_within_sections = true +# Group first party and local folder imports together +no_lines_before = ["LOCALFOLDER"] + +# Configure isort to work without access to site-packages +known_first_party = ["aiida_lammos"] + +# Settings for Black compatibility +profile = "black" diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index 84b3e64..0000000 --- a/pytest.ini +++ /dev/null @@ -1,7 +0,0 @@ -[pytest] -adopts = --ignore=setup.py -timeout = 180 -filterwarnings = - ignore::DeprecationWarning:inspect.* - ignore::DeprecationWarning:django.* - ignore::django.utils.deprecation.RemovedInDjango20Warning diff --git a/setup.json b/setup.json index fcfb2c4..12437e1 100644 --- a/setup.json +++ b/setup.json @@ -2,20 +2,19 @@ "name": "aiida-lammps", "version": "0.7.0b5", "description": "AiiDA plugin for LAMMPS", - "url": "https://github.com/abelcarreras/aiida-lammps", + "url": "https://github.com/aiidaplugins/aiida-lammps", "author": "Abel Carreras, Chris Sewell", "author_email": "abelcarreras83@gmail.com", "license": "MIT license", + "python_requires": ">=3.6", "install_requires": [ - "aiida-core==1.0.0b5", - "ase>=3.12.0,<3.18.0; python_version < '3'", - "ase>=3.12.0,<4.0.0; python_version >= '3'", + "aiida-core>=1.4.0,<2.0.0", + "ase>=3.12.0,<4.0.0", "importlib_resources", "jsonschema", "numpy", "packaging", - "python-dateutil", - "six" + "python-dateutil" ], "reentry_register": true, "include_package_data": true, @@ -49,19 +48,15 @@ "extras_require": { "testing": [ "attrs>=17.4.0", - "mock==2.0.0", - "pgtest==1.2.0", - "sqlalchemy-diff==0.1.3", - "pytest==3.6.3", + "pgtest", + "pytest", "pytest-cov", + "coverage", "pytest-timeout", - "pytest-regressions", - "wheel>=0.31" + "pytest-regressions" ], "code_style": [ - "flake8<3.8.0,>=3.7.0", - "black", - "pre-commit==1.17.0" + "pre-commit~=2.6" ], "phonopy": [ "dynaphopy" diff --git a/setup.py b/setup.py index cdc22db..a607dbb 100644 --- a/setup.py +++ b/setup.py @@ -1,9 +1,11 @@ #!/usr/bin/env python from __future__ import absolute_import -from setuptools import setup, find_packages + import json +from setuptools import find_packages, setup + if __name__ == "__main__": with open("setup.json", "r") as info: kwargs = json.load(info) diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..cc00f07 --- /dev/null +++ b/tox.ini @@ -0,0 +1,48 @@ +[tox] +envlist = py{36,37,38} + +[testenv] +use_develop = true + +[testenv:py{36,37,38}] +extras = testing +deps = + black + flake8 +commands_pre = reentry scan +commands = pytest --lammps-exec lmp_serial {posargs} + +[testenv:py{36,37,38}-docs-{clean,update}] +extras = docs +commands_pre = reentry scan +setenv = + update: RUN_APIDOC = False +changedir = docs +whitelist_externals = make +commands = + clean: make clean + make debug + +[testenv:py{36,37,38}-{sync,syncrm}] +description=sync notebooks with jupytext +skip_install = true +deps = jupytext >=1.6 +commands = jupytext --sync {posargs:docs/source/notebooks/*.ipynb} +; run docs/source/notebooks/*.md before docs builds + +[pytest] +addopts = --ignore=setup.py +timeout = 180 +filterwarnings = + ignore::DeprecationWarning:frozendict.* + ignore::DeprecationWarning:sqlalchemy_utils.* + ignore::DeprecationWarning:reentry.* +markers = + lammps_call: calls the lammps executable + + +[flake8] +max-line-length = 88 +; max-complexity = 10 +# These checks violate PEP8 so let's ignore them +extend-ignore = E203 # ,E501,W503