Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ For example, to install a configuration of the `SSSP`_, just run:
$ aiida-pseudo install sssp

The version, functional, and protocol can be controlled with various options; use ``aiida-pseudo install sssp --help`` to see their description.
To install all SSSP configurations (PBE and PBEsol, with both efficiency and precision) for the latest version, use the ``--all`` flag:

.. code-block:: console

$ aiida-pseudo install sssp --all

If you are experiencing problems with this automated install method, see the :ref:`Troubleshooting section <troubleshooting>` for help.
Installed pseudopotential families can be listed using:

Expand Down
271 changes: 167 additions & 104 deletions src/aiida_pseudo/cli/install.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""Command to install a pseudo potential family."""
import json
import pathlib
import sys
import tarfile
import tempfile
import typing as t
Expand Down Expand Up @@ -202,8 +201,18 @@ def install_sssp(
@options.PROTOCOL(type=click.Choice(['efficiency', 'precision']), default='efficiency', show_default=True)
@options.DOWNLOAD_ONLY()
@options.FROM_DOWNLOAD()
@click.option(
'--all',
'install_all',
is_flag=True,
default=False,
help=(
'Install all SSSP functionals and protocols (PBE and PBEsol for the efficiency and '
'precision protocols) for the specified version.'
),
)
@options.TRACEBACK()
def cmd_install_sssp(version, functional, protocol, download_only, from_download, traceback):
def cmd_install_sssp(version, functional, protocol, download_only, from_download, install_all, traceback):
"""Install an SSSP configuration.

The SSSP configuration will be automatically downloaded from the Materials Cloud Archive entry to create a new
Expand All @@ -223,79 +232,99 @@ def cmd_install_sssp(version, functional, protocol, download_only, from_download
'cannot specify both `--download-only` and `--from-download`.',
param_hint="'--download-only' / '--from-download'",
)
if install_all and from_download:
raise click.BadParameter(
'cannot specify both `--all` and `--from-download`.',
param_hint="'--all' / '--from-download'",
)

configuration = SsspConfiguration(version, functional, protocol)
label = SsspFamily.format_configuration_label(configuration)
configurations = [(version, functional, protocol)]
if install_all:
configurations = [
(version, 'PBE', 'efficiency'),
(version, 'PBE', 'precision'),
(version, 'PBEsol', 'efficiency'),
(version, 'PBEsol', 'precision'),
]

if configuration not in SsspFamily.valid_configurations:
echo.echo_critical(f'{version} {functional} {protocol} is not a valid SSSP configuration')
for config_idx, config in enumerate(configurations):
if config_idx > 0:
echo.echo('')
configuration = SsspConfiguration(*config)
label = SsspFamily.format_configuration_label(configuration)

with tempfile.TemporaryDirectory() as tmppath:
dirpath = pathlib.Path(tmppath)
if configuration not in SsspFamily.valid_configurations:
echo.echo_critical(f'{version} {functional} {protocol} is not a valid SSSP configuration')

filepath_archive = dirpath / 'archive.tar.gz'
filepath_metadata = dirpath / 'metadata.json'
filepath_configuration = dirpath / 'configuration.json'
echo.echo_report(f'Trying to install SSSP configuration: {label}')

if download_only:
tarball_path = pathlib.Path.cwd() / f'{label}.aiida_pseudo'.replace('/', '_')
with tempfile.TemporaryDirectory() as tmppath:
dirpath = pathlib.Path(tmppath)

if tarball_path.exists():
echo.echo_critical(f'the file `{tarball_path}` already exists.')
filepath_archive = dirpath / 'archive.tar.gz'
filepath_metadata = dirpath / 'metadata.json'
filepath_configuration = dirpath / 'configuration.json'

download_sssp(configuration, filepath_archive, filepath_metadata, traceback)
if download_only:
tarball_path = pathlib.Path.cwd() / f'{label}.aiida_pseudo'.replace('/', '_')

with filepath_configuration.open('w') as handle:
handle.write(json.dumps(configuration._asdict()))
if tarball_path.exists():
echo.echo_critical(f'the file `{tarball_path}` already exists.')

with tarfile.open(tarball_path, 'w') as tarball_file:
for filepath in [filepath_configuration, filepath_metadata, filepath_archive]:
tarball_file.add(filepath, filepath.name)
download_sssp(configuration, filepath_archive, filepath_metadata, traceback)

echo.echo_success(f'Pseudopotential archive written to: {tarball_path.name}')
return
with filepath_configuration.open('w') as handle:
handle.write(json.dumps(configuration._asdict()))

if from_download is not None:
tarball_path = pathlib.Path(from_download).absolute()
with tarfile.open(tarball_path, 'r') as handle:
handle.extractall(dirpath)
with tarfile.open(tarball_path, 'w') as tarball_file:
for filepath in [filepath_configuration, filepath_metadata, filepath_archive]:
tarball_file.add(filepath, filepath.name)

for filepath in (filepath_archive, filepath_metadata, filepath_configuration):
if not filepath.exists():
echo.echo_critical(
f'archive `{from_download}` did not contain required file `{filepath_archive.name}`'
)
echo.echo_success(f'Pseudopotential archive written to: {tarball_path.name}')
continue

with filepath_configuration.open('r') as handle:
configuration = SsspConfiguration(**json.load(handle))
if from_download is not None:
tarball_path = pathlib.Path(from_download).absolute()
with tarfile.open(tarball_path, 'r') as handle:
handle.extractall(dirpath)

label = SsspFamily.format_configuration_label(configuration)
for filepath in (filepath_archive, filepath_metadata, filepath_configuration):
if not filepath.exists():
echo.echo_critical(
f'archive `{from_download}` did not contain required file `{filepath_archive.name}`'
)

if configuration not in SsspFamily.valid_configurations:
echo.echo_critical(f'{version} {functional} {protocol} is not a valid SSSP configuration')
with filepath_configuration.open('r') as handle:
configuration = SsspConfiguration(**json.load(handle))

try:
load_profile()
except ConfigurationError:
echo.echo_critical(
'Could not load a valid AiiDA profile. Check `verdi profile list` to make sure you have one defined.'
)
label = SsspFamily.format_configuration_label(configuration)

if configuration not in SsspFamily.valid_configurations:
echo.echo_critical(f'{version} {functional} {protocol} is not a valid SSSP configuration')

if QueryBuilder().append(SsspFamily, filters={'label': label}).first():
echo.echo_report(f'{SsspFamily.__name__}<{label}> is already installed')
sys.exit(1)
try:
load_profile()
except ConfigurationError:
echo.echo_critical(
'Could not load a valid AiiDA profile. Check `verdi profile list` to make sure '
'you have one defined.'
)

if not from_download:
download_sssp(configuration, filepath_archive, filepath_metadata, traceback)
if QueryBuilder().append(SsspFamily, filters={'label': label}).first():
echo.echo_report(f'{SsspFamily.__name__}<{label}> is already installed')
continue

description = (
f'SSSP v{configuration.version} {configuration.functional} {configuration.protocol} '
f'installed with aiida-pseudo v{__version__}'
)
description += f'\nArchive pseudos md5: {md5_file(filepath_archive)}'
description += f'\nPseudo metadata md5: {md5_file(filepath_metadata)}'
if not from_download:
download_sssp(configuration, filepath_archive, filepath_metadata, traceback)

install_sssp(filepath_archive, filepath_metadata, label, description, traceback)
description = (
f'SSSP v{configuration.version} {configuration.functional} {configuration.protocol} '
f'installed with aiida-pseudo v{__version__}'
)
description += f'\nArchive pseudos md5: {md5_file(filepath_archive)}'
description += f'\nPseudo metadata md5: {md5_file(filepath_metadata)}'

install_sssp(filepath_archive, filepath_metadata, label, description, traceback)


def download_pseudo_dojo(
Expand Down Expand Up @@ -420,6 +449,16 @@ def install_pseudo_dojo(
@options.DEFAULT_STRINGENCY(type=click.Choice(['low', 'normal', 'high']), default='normal', show_default=True)
@options.DOWNLOAD_ONLY()
@options.FROM_DOWNLOAD()
@click.option(
'--all',
'install_all',
is_flag=True,
default=False,
help=(
'Install all PseudoDojo functionals and protocols (PBE, PBEsol and LDA for the standard and '
'stringent protocol) for the specified version and relativistic type.'
),
)
@options.TRACEBACK()
def cmd_install_pseudo_dojo(
version,
Expand All @@ -430,6 +469,7 @@ def cmd_install_pseudo_dojo(
default_stringency,
download_only,
from_download,
install_all,
traceback,
):
"""Install a PseudoDojo configuration.
Expand All @@ -452,6 +492,11 @@ def cmd_install_pseudo_dojo(
'cannot specify both `--download-only` and `--from-download`.',
param_hint="'--download-only' / '--from-download'",
)
if install_all and from_download:
raise click.BadParameter(
'cannot specify both `--all` and `--from-download`.',
param_hint="'--all' / '--from-download'",
)

pseudo_type_mapping = {
'jthxml': JthXmlData,
Expand All @@ -461,70 +506,88 @@ def cmd_install_pseudo_dojo(
}
pseudo_type = pseudo_type_mapping[pseudo_format]

configuration = PseudoDojoConfiguration(version, functional, relativistic, protocol, pseudo_format)
label = PseudoDojoFamily.format_configuration_label(configuration)
configurations = [(version, functional, relativistic, protocol, pseudo_format)]
if install_all:
configurations = [
(version, 'PBE', relativistic, 'standard', pseudo_format),
(version, 'PBE', relativistic, 'stringent', pseudo_format),
(version, 'PBEsol', relativistic, 'standard', pseudo_format),
(version, 'PBEsol', relativistic, 'stringent', pseudo_format),
(version, 'LDA', relativistic, 'standard', pseudo_format),
(version, 'LDA', relativistic, 'stringent', pseudo_format),
]

if configuration not in PseudoDojoFamily.valid_configurations:
echo.echo_critical(f'{configuration} is not a valid configuration')
for config_idx, config in enumerate(configurations):
configuration = PseudoDojoConfiguration(*config)
label = PseudoDojoFamily.format_configuration_label(configuration)

with tempfile.TemporaryDirectory() as tmppath:
dirpath = pathlib.Path(tmppath)
if config_idx > 0:
echo.echo('')

filepath_archive = dirpath / 'archive.tgz'
filepath_metadata = dirpath / 'metadata.tgz'
filepath_configuration = dirpath / 'configuration.json'
if configuration not in PseudoDojoFamily.valid_configurations:
echo.echo_critical(f'{configuration} is not a valid configuration')

if download_only:
tarball_path = pathlib.Path.cwd() / f'{label}.aiida_pseudo'.replace('/', '_')
echo.echo_report(f'Trying to install PseudoDojo configuration: {label}')

if tarball_path.exists():
echo.echo_critical(f'the file `{tarball_path}` already exists.')
with tempfile.TemporaryDirectory() as tmppath:
dirpath = pathlib.Path(tmppath)

download_pseudo_dojo(configuration, filepath_archive, filepath_metadata, traceback)
filepath_archive = dirpath / 'archive.tgz'
filepath_metadata = dirpath / 'metadata.tgz'
filepath_configuration = dirpath / 'configuration.json'

with filepath_configuration.open('w') as handle:
handle.write(json.dumps(configuration._asdict()))
if download_only:
tarball_path = pathlib.Path.cwd() / f'{label}.aiida_pseudo'.replace('/', '_')

with tarfile.open(tarball_path, 'w') as tarball_file:
for filepath in [filepath_configuration, filepath_metadata, filepath_archive]:
tarball_file.add(filepath, filepath.name)
if tarball_path.exists():
echo.echo_critical(f'the file `{tarball_path}` already exists.')

echo.echo_success(f'Pseudopotential archive written to: {tarball_path.name}')
return
download_pseudo_dojo(configuration, filepath_archive, filepath_metadata, traceback)

if from_download is not None:
tarball_path = pathlib.Path(from_download).absolute()
with tarfile.open(tarball_path, 'r') as handle:
handle.extractall(dirpath)
with filepath_configuration.open('w') as handle:
handle.write(json.dumps(configuration._asdict()))

with filepath_configuration.open('r') as handle:
configuration = PseudoDojoConfiguration(**json.load(handle))
pseudo_type = pseudo_type_mapping[configuration.pseudo_format]
with tarfile.open(tarball_path, 'w') as tarball_file:
for filepath in [filepath_configuration, filepath_metadata, filepath_archive]:
tarball_file.add(filepath, filepath.name)

label = PseudoDojoFamily.format_configuration_label(configuration)
echo.echo_success(f'Pseudopotential archive written to: {tarball_path.name}')
continue

if configuration not in PseudoDojoFamily.valid_configurations:
echo.echo_critical(f'{configuration} is not a valid configuration')
if from_download is not None:
tarball_path = pathlib.Path(from_download).absolute()
with tarfile.open(tarball_path, 'r') as handle:
handle.extractall(dirpath)

try:
load_profile()
except ConfigurationError:
echo.echo_critical(
'Could not load a valid AiiDA profile. Check `verdi profile list` to make sure you have one defined.'
)
with filepath_configuration.open('r') as handle:
configuration = PseudoDojoConfiguration(**json.load(handle))
pseudo_type = pseudo_type_mapping[configuration.pseudo_format]

label = PseudoDojoFamily.format_configuration_label(configuration)

if QueryBuilder().append(PseudoDojoFamily, filters={'label': label}).first():
echo.echo_report(f'{PseudoDojoFamily.__name__}<{label}> is already installed')
sys.exit(1)
if configuration not in PseudoDojoFamily.valid_configurations:
echo.echo_critical(f'{configuration} is not a valid configuration')

if not from_download:
download_pseudo_dojo(configuration, filepath_archive, filepath_metadata, traceback)
try:
load_profile()
except ConfigurationError:
echo.echo_critical(
'Could not load a valid AiiDA profile. Check `verdi profile list` to make sure '
'you have one defined.'
)

description = f'{configuration} installed with aiida-pseudo v{__version__}'
description += f'\nArchive pseudos md5: {md5_file(filepath_archive)}'
description += f'\nPseudo metadata md5: {md5_file(filepath_metadata)}'
if QueryBuilder().append(PseudoDojoFamily, filters={'label': label}).first():
echo.echo_report(f'{PseudoDojoFamily.__name__}<{label}> is already installed')
continue

family = install_pseudo_dojo(
configuration, filepath_archive, filepath_metadata, pseudo_type, label, description, traceback
)
family.set_default_stringency(default_stringency)
if not from_download:
download_pseudo_dojo(configuration, filepath_archive, filepath_metadata, traceback)

description = f'{configuration} installed with aiida-pseudo v{__version__}'
description += f'\nArchive pseudos md5: {md5_file(filepath_archive)}'
description += f'\nPseudo metadata md5: {md5_file(filepath_metadata)}'

family = install_pseudo_dojo(
configuration, filepath_archive, filepath_metadata, pseudo_type, label, description, traceback
)
family.set_default_stringency(default_stringency)
4 changes: 2 additions & 2 deletions tests/cli/test_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ def test_install_sssp(run_cli_command):
assert 'Archive pseudos md5: ' in family.description
assert 'Pseudo metadata md5: ' in family.description

result = run_cli_command(cmd_install_sssp, raises=SystemExit)
result = run_cli_command(cmd_install_sssp)
assert 'is already installed' in result.output


Expand All @@ -254,7 +254,7 @@ def test_install_pseudo_dojo(run_cli_command):
assert 'Archive pseudos md5: a43737369e8a0a4417ccf364397298b3' in family.description
assert 'Pseudo metadata md5: d0c0057f16cb905bb2d43382146ffad2' in family.description

result = run_cli_command(cmd_install_pseudo_dojo, raises=SystemExit)
result = run_cli_command(cmd_install_pseudo_dojo)
assert 'is already installed' in result.output


Expand Down