diff --git a/.flake8 b/.flake8 index c04df776..50b1c1d5 100644 --- a/.flake8 +++ b/.flake8 @@ -3,8 +3,8 @@ # rather than using the flake8 default of 79: max-line-length = 88 extend-ignore = - E501 # Line length handled by black. - W503 # Line break before binary operator, preferred formatting for black. - E203 # Whitespace before ':', preferred formatting for black. + E501 + W503 + E203 exclude = docs/source/conf.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2ba5d1ce..0ba821c0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,6 +49,7 @@ jobs: export DOCKERID=`docker run -d aiida_cp2k_test` docker exec --tty $DOCKERID wait-for-services docker logs $DOCKERID + docker exec --tty --user aiida $DOCKERID /bin/bash -l -c 'aiida-pseudo install sssp -v 1.3 -x PBE -p efficiency' docker exec --tty --user aiida $DOCKERID /bin/bash -l -c 'cd /opt/aiida-cp2k/ && py.test --cov aiida_cp2k --cov-append .' diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d9d66788..d08d6934 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,7 +1,7 @@ --- repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.2.0 + rev: v4.4.0 hooks: - id: check-json @@ -16,32 +16,32 @@ repos: exclude: *exclude_pre_commit_hooks - repo: https://github.com/psf/black - rev: 22.3.0 + rev: 23.7.0 hooks: - id: black language_version: python3 # Should be a command that runs python3.6+ exclude: aiida_cp2k/workchains/base.py - repo: https://github.com/PyCQA/flake8 - rev: 4.0.1 + rev: 6.1.0 hooks: - id: flake8 args: [--count, --show-source, --statistics] - repo: https://github.com/pycqa/isort - rev: 5.10.1 + rev: 5.12.0 hooks: - id: isort args: [--profile, black, --filter-files] - repo: https://github.com/asottile/pyupgrade - rev: v2.32.1 + rev: v3.10.1 hooks: - id: pyupgrade args: [--py37-plus] - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.961 + rev: v1.5.1 hooks: - id: mypy additional_dependencies: diff --git a/aiida_cp2k/calculations/__init__.py b/aiida_cp2k/calculations/__init__.py index b79fb7ad..9eadca6d 100644 --- a/aiida_cp2k/calculations/__init__.py +++ b/aiida_cp2k/calculations/__init__.py @@ -6,12 +6,14 @@ ############################################################################### """AiiDA-CP2K input plugin.""" +import json from operator import add from aiida.common import CalcInfo, CodeInfo, InputValidationError from aiida.engine import CalcJob from aiida.orm import Dict, RemoteData, SinglefileData from aiida.plugins import DataFactory +from upf_to_json import upf_to_json from ..utils import Cp2kInput from ..utils.datatype_helpers import ( @@ -26,6 +28,7 @@ BandsData = DataFactory("array.bands") # pylint: disable=invalid-name StructureData = DataFactory("structure") # pylint: disable=invalid-name KpointsData = DataFactory("array.kpoints") # pylint: disable=invalid-name +UpfData = DataFactory("pseudo.upf") # pylint: disable=invalid-name class Cp2kCalculation(CalcJob): @@ -105,6 +108,14 @@ def define(cls, spec): ), ) + spec.input_namespace( + "pseudos_upf", + valid_type=UpfData, + dynamic=True, + required=True, + help="A mapping of `UpfData` nodes onto the kind name to which they should apply.", + ) + # Specify default parser. spec.input( "metadata.options.parser_name", @@ -262,6 +273,12 @@ def prepare_for_submission(self, folder): ) write_pseudos(inp, self.inputs.pseudos, folder) + if "pseudos_upf" in self.inputs: + for atom_kind, pseudo in self.inputs.pseudos_upf.items(): + pseudo_dict = upf_to_json(pseudo.get_content(), atom_kind) + with folder.open(atom_kind + ".json", "w") as fobj: + fobj.write(json.dumps(pseudo_dict, indent=2)) + # Kpoints. if "kpoints" in self.inputs: try: diff --git a/examples/files/si.xyz b/examples/files/si.xyz new file mode 100644 index 00000000..f181bb8c --- /dev/null +++ b/examples/files/si.xyz @@ -0,0 +1,10 @@ +8 +Lattice="5.43 0.0 0.0 0.0 5.43 0.0 0.0 0.0 5.43" Properties=species:S:1:pos:R:3:spacegroup_kinds:I:1 spacegroup="F d -3 m" unit_cell=conventional pbc="T T T" +Si 0.00000000 0.00000000 0.00000000 0 +Si 0.00000000 2.71500000 2.71500000 0 +Si 2.71500000 2.71500000 0.00000000 0 +Si 2.71500000 0.00000000 2.71500000 0 +Si 4.07250000 1.35750000 4.07250000 0 +Si 1.35750000 1.35750000 1.35750000 0 +Si 1.35750000 4.07250000 4.07250000 0 +Si 4.07250000 4.07250000 1.35750000 0 diff --git a/examples/single_calculations/example_sirius.py b/examples/single_calculations/example_sirius.py new file mode 100644 index 00000000..3fb3af99 --- /dev/null +++ b/examples/single_calculations/example_sirius.py @@ -0,0 +1,126 @@ +############################################################################### +# Copyright (c), The AiiDA-CP2K authors. # +# SPDX-License-Identifier: MIT # +# AiiDA-CP2K is hosted on GitHub at https://github.com/aiidateam/aiida-cp2k # +# For further information on the license, see the LICENSE.txt file. # +############################################################################### +"""Run simple DFT with calculation with SIRIUS.""" + +import os +import sys + +import ase.io +import click +from aiida import common, engine, orm, plugins + +StructureData = plugins.DataFactory("core.structure") + + +def example_sirius(cp2k_code): + """Run simple DFT calculation.""" + + print("Testing CP2K SIRIUS ENERGY on Si (DFT)...") + + thisdir = os.path.dirname(os.path.realpath(__file__)) + + # Structure. + structure = StructureData( + ase=ase.io.read(os.path.join(thisdir, "..", "files", "si.xyz")) + ) + + # Parameters. + parameters = orm.Dict( + { + "FORCE_EVAL": { + "METHOD": "SIRIUS", + "STRESS_TENSOR": "ANALYTICAL", + "PRINT": { + "FORCES": {"FILENAME": "requested-forces", "ADD_LAST": "SYMBOLIC"} + }, + "PW_DFT": { + "CONTROL": {"VERBOSITY": 2}, + "PARAMETERS": { + "ELECTRONIC_STRUCTURE_METHOD": "pseudopotential", + "USE_SYMMETRY": True, + "GK_CUTOFF": 10, + "PW_CUTOFF": 55, + "ENERGY_TOL": 1e-15, + "NUM_DFT_ITER": 400, + "SMEARING": "FERMI_DIRAC", + "SMEARING_WIDTH": 0.00225, + }, + "ITERATIVE_SOLVER": { + "ENERGY_TOLERANCE": 0.001, + "NUM_STEPS": 20, + "SUBSPACE_SIZE": 4, + "CONVERGE_BY_ENERGY": 1, + }, + }, + "DFT": { + "XC": { + "XC_FUNCTIONAL": { + "GGA_X_PBE": {"_": ""}, + "GGA_C_PBE": {"_": ""}, + } + }, + "PRINT": { + "MO": { + "_": "OFF", + "ADD_LAST": "SYMBOLIC", + "EIGENVALUES": True, + "OCCUPATION_NUMBERS": True, + "NDIGITS": 12, + "EACH": {"CELL_OPT": 0, "GEO_OPT": 0, "MD": 0, "QS_SCF": 0}, + }, + "MULLIKEN": { + "_": "ON", + "ADD_LAST": "SYMBOLIC", + "EACH": {"CELL_OPT": 0, "GEO_OPT": 0, "MD": 0}, + }, + "LOWDIN": {"_": "OFF"}, + "HIRSHFELD": {"_": "OFF"}, + }, + }, + "SUBSYS": { + "KIND": [ + { + "_": "Si", + "POTENTIAL": "UPF Si.json", + }, + ], + }, + }, + } + ) + + # Construct process builder. + builder = cp2k_code.get_builder() + builder.structure = structure + builder.parameters = parameters + builder.code = cp2k_code + pseudo_family = orm.load_group("SSSP/1.3/PBE/efficiency") + builder.pseudos_upf = pseudo_family.get_pseudos(structure=structure) + builder.metadata.options.resources = { + "num_machines": 1, + "num_mpiprocs_per_machine": 1, + } + builder.metadata.options.max_wallclock_seconds = 1 * 3 * 60 + + print("Submitted calculation...") + engine.run(builder) + + +@click.command("cli") +@click.argument("codelabel") +def cli(codelabel): + """Click interface.""" + try: + code = orm.load_code(codelabel) + except common.NotExistent: + print(f"The code '{codelabel}' does not exist.") + sys.exit(1) + example_sirius(code) + + +if __name__ == "__main__": + cli() diff --git a/pyproject.toml b/pyproject.toml index 52ffb55c..97413c34 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,6 +25,8 @@ dependencies = [ "ase", "ruamel.yaml>=0.16.5", "cp2k-output-tools", + "aiida-pseudo~=1.2", + "upf-to-json>=0.9", ] [[project.authors]]