From 3499c8dfe20b27be1b9dee6d9f6ec151ed94cc9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edward=20Linsc-Bot=20=F0=9F=A4=96?= <310205674+elinsc-bot@users.noreply.github.com> Date: Thu, 6 Aug 2026 14:57:14 +0200 Subject: [PATCH 1/2] Say what a rejected pseudo_library label means A label naming no installed family was reported as a format error, though a family installed by hand is accepted under any label at all. - The message now says that no installed family carries the label and that koopmans cannot download one, then gives both ways forward: install the pseudopotentials yourself under that label, or name one of the three formats koopmans can fetch. - The `pseudo_library` help text pointed readers at `koopmans pseudos list`, which is not a command; it now describes both routes. - The installation page gains a Pseudopotentials section, which previously said nothing about them at all. - Tests pin that a family installed under an arbitrary label is used as it stands, and that an unusable label reports both routes. Co-Authored-By: Claude Opus 5 (1M context) --- docs/source/installation.rst | 28 ++++++++++++++++++++++ src/koopmans/aiida/setup/pseudos.py | 29 +++++++++++++++------- src/koopmans/input_file/workflow.py | 2 +- tests/conftest.py | 1 + tests/fixtures.py | 6 +++++ tests/test_pseudos.py | 37 +++++++++++++++++++++++++++++ 6 files changed, 93 insertions(+), 10 deletions(-) create mode 100644 tests/test_pseudos.py diff --git a/docs/source/installation.rst b/docs/source/installation.rst index fd5024d8..6fb07454 100644 --- a/docs/source/installation.rst +++ b/docs/source/installation.rst @@ -117,6 +117,34 @@ for any other reason, start it again with and stop it with ``koopmans backend daemon stop``. ``koopmans backend uninstall`` removes the whole setup, database included. +******************* + Pseudopotentials +******************* + +You do not have to install pseudopotentials up front. The ``pseudo_library`` keyword of +your input file names a family, and ``koopmans`` downloads that family the first time it +is needed. It can fetch `PseudoDojo `_, `SSSP +`_ and `SG15 +`_ families, named like + +- ``PseudoDojo/0.4/LDA/SR/standard/upf`` +- ``SSSP/1.3/PBEsol/efficiency`` +- ``SG15/1.2/PBE/SR`` + +A family that you install yourself works just as well, under whatever label you give it: +``koopmans`` downloads a family only when no installed one carries the label you asked +for. This is the route for pseudopotentials it cannot fetch — the full-relativistic LDA +sets that spin-orbit calculations need, for instance. Point ``aiida-pseudo`` at a +directory holding one file per element: + +.. code-block:: console + + $ aiida-pseudo install family my-lda-fr -F pseudo.family.cutoffs + $ aiida-pseudo family cutoffs set my-lda-fr + +Both commands are needed: the calculations take ``ecutwfc`` and ``ecutrho`` from the +family's recommended cutoffs. Then set ``pseudo_library`` to ``my-lda-fr``. + **************************** Installing for development **************************** diff --git a/src/koopmans/aiida/setup/pseudos.py b/src/koopmans/aiida/setup/pseudos.py index 2b57f90e..dc615cdf 100644 --- a/src/koopmans/aiida/setup/pseudos.py +++ b/src/koopmans/aiida/setup/pseudos.py @@ -14,17 +14,16 @@ def ensure_pseudo_family_installed(pseudo_family: str) -> None: """Ensure a pseudopotential family is installed, installing it if necessary. - Supports PseudoDojo families with labels like: + Any already-installed family is used as it stands, whatever its label. A + label that names no installed family is downloaded, which koopmans can do + for three families: 'PseudoDojo/0.4/LDA/SR/standard/upf' - - SSSP families with labels like: 'SSSP/1.3/PBEsol/efficiency' - - And SG15 ONCV families with labels like: 'SG15/1.2/PBE/SR' Raises: - ValueError: If the family format is not recognized or installation fails. + ValueError: If no family carries the label and koopmans cannot + download it, or if the download fails. """ from aiida.common.exceptions import NotExistent from aiida_pseudo.groups.family import PseudoPotentialFamily @@ -42,7 +41,10 @@ def ensure_pseudo_family_installed(pseudo_family: str) -> None: def install_pseudo_family(pseudo_family: str) -> None: - """Install a pseudopotential family. Parse the label and dispatch.""" + """Download and install a pseudopotential family. Parse the label and dispatch. + + No family may already carry the label. + """ parts = pseudo_family.split("/") if parts[0] == "PseudoDojo" and len(parts) == 6: @@ -53,8 +55,17 @@ def install_pseudo_family(pseudo_family: str) -> None: _install_sg15_family(pseudo_family, parts) else: raise ValueError( - f"Unrecognized pseudo family format: '{pseudo_family}'. " - "Expected 'PseudoDojo/version/functional/relativistic/protocol/format', " + f"No installed pseudopotential family has the label '{pseudo_family}', and " + "koopmans cannot download one under that label.\n" + "Install the pseudopotentials yourself, from a directory holding one file " + "per element:\n" + f" aiida-pseudo install family {pseudo_family} " + "-F pseudo.family.cutoffs\n" + f" aiida-pseudo family cutoffs set {pseudo_family} \n" + "Both steps are needed: the calculations take ecutwfc and ecutrho from the " + "family's recommended cutoffs.\n" + "Alternatively, name a family koopmans can download for you, as " + "'PseudoDojo/version/functional/relativistic/protocol/format', " "'SSSP/version/functional/protocol', " "or 'SG15/version/functional/relativistic'." ) diff --git a/src/koopmans/input_file/workflow.py b/src/koopmans/input_file/workflow.py index 513d42e1..1898245b 100644 --- a/src/koopmans/input_file/workflow.py +++ b/src/koopmans/input_file/workflow.py @@ -64,7 +64,7 @@ class WorkflowConfig(BaseModel): default=True, description="whether or not to calculate the screening parameters ab-initio" ) pseudo_library: str = Field( - description="the pseudopotential library to use (for valid options, run `koopmans pseudos list`)" + description="the label of the pseudopotential family to use. Any family you have installed yourself is used as it stands, whatever its label; a label naming no installed family is downloaded, which koopmans can do for 'PseudoDojo/version/functional/relativistic/protocol/format', 'SSSP/version/functional/protocol' and 'SG15/version/functional/relativistic'" ) screening_method: CalculateScreeningMethod = Field( default=CalculateScreeningMethod.DSCF, diff --git a/tests/conftest.py b/tests/conftest.py index b29ebdb4..445832bf 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -18,6 +18,7 @@ clear_database_after_test, code_without_mpi_flag, compiled_binaries, + fake_custom_label_family, fake_pseudodojo_lda_family, fake_sg15_cutoffs_family, fake_sg15_fr_cutoffs_family, diff --git a/tests/fixtures.py b/tests/fixtures.py index 768882e1..1263a732 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -576,6 +576,12 @@ def fake_sg15_fr_cutoffs_family(aiida_profile: Any) -> Any: return _install_fake_family("SG15/1.0/PBE/FR", {"O": 6.0, "Si": 4.0}, cutoffs=True, has_so=True) +@pytest.fixture +def fake_custom_label_family(aiida_profile: Any) -> Any: + """Install a fake family under a label matching none of the download formats.""" + return _install_fake_family("my-gaas-fr", {"Ga": 13.0, "As": 5.0}, cutoffs=True, has_so=True) + + @pytest.fixture def fake_pseudodojo_lda_family(aiida_profile: Any) -> Any: """Install a minimal fake ``PseudoDojo/0.4/LDA/SR/standard/upf`` family. diff --git a/tests/test_pseudos.py b/tests/test_pseudos.py new file mode 100644 index 00000000..5fbb8305 --- /dev/null +++ b/tests/test_pseudos.py @@ -0,0 +1,37 @@ +"""Tests for resolving the ``pseudo_library`` label to a pseudopotential family.""" + +from __future__ import annotations + +from typing import Any + +import pytest + + +class TestEnsurePseudoFamilyInstalled: + """``ensure_pseudo_family_installed`` downloads only what is missing.""" + + def test_installed_family_under_arbitrary_label_is_used_as_is( + self, fake_custom_label_family: Any, monkeypatch: pytest.MonkeyPatch + ) -> None: + """A label matching no download format resolves to the installed family.""" + from koopmans.aiida.setup import pseudos + + def _fail(label: str) -> None: + raise AssertionError(f"attempted to download '{label}'") + + monkeypatch.setattr(pseudos, "install_pseudo_family", _fail) + + pseudos.ensure_pseudo_family_installed(fake_custom_label_family.label) + + def test_uninstallable_label_reports_both_routes(self, aiida_profile_clean: Any) -> None: + """An unknown label names the install command and the download formats.""" + from koopmans.aiida.setup import pseudos + + with pytest.raises(ValueError) as excinfo: + pseudos.ensure_pseudo_family_installed("my-gaas-fr") + + message = str(excinfo.value) + assert "No installed pseudopotential family has the label 'my-gaas-fr'" in message + assert "aiida-pseudo install family my-gaas-fr" in message + assert "aiida-pseudo family cutoffs set my-gaas-fr" in message + assert "PseudoDojo/version/functional/relativistic/protocol/format" in message From c2d1ba9df5671a3f6cb574322853c744c31cfe8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edward=20Linsc-Bot=20=F0=9F=A4=96?= <310205674+elinsc-bot@users.noreply.github.com> Date: Fri, 7 Aug 2026 19:13:19 +0200 Subject: [PATCH 2/2] Point the self-install advice at a plain family The message and the installation docs told a user to build a cutoffs family and load a cutoffs file into it, which is a family koopmans now refuses to run against. - Offer `aiida-pseudo install family