-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
33 changed files
with
895 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# -*- coding: utf-8 -*- | ||
# pylint: disable=wrong-import-position | ||
"""Module for the command line interface.""" | ||
from aiida.cmdline.groups import VerdiCommandGroup | ||
from aiida.cmdline.params import options, types | ||
import click | ||
|
||
|
||
@click.group('aiida-vibroscopy', cls=VerdiCommandGroup, context_settings={'help_option_names': ['-h', '--help']}) | ||
@options.PROFILE(type=types.ProfileParamType(load_profile=True), expose_value=False) | ||
def cmd_root(): | ||
"""CLI for the `aiida-vibroscopy` plugin.""" | ||
|
||
|
||
from .workflows import cmd_launch |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# -*- coding: utf-8 -*- | ||
"""Utilities for the command line interface.""" | ||
# pylint: disable=cyclic-import,unused-import,wrong-import-position,import-error |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# -*- coding: utf-8 -*- | ||
"""Module with utitlies for the CLI to generate default values.""" | ||
|
||
|
||
def get_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 aiida.orm import QueryBuilder, StructureData | ||
from ase.spacegroup import crystal | ||
|
||
# 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) | ||
structure = builder.first(flat=True) | ||
|
||
if not structure: | ||
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() | ||
|
||
return structure.uuid |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
# -*- coding: utf-8 -*- | ||
"""Module with display utitlies for the CLI.""" | ||
import os | ||
|
||
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.base.links.get_outgoing(link_type=(LinkType.CREATE, LinkType.RETURN)).all() | ||
|
||
if hasattr(node, 'dry_run_info'): | ||
# It is a dry-run: get the information and print it | ||
rel_path = os.path.relpath(node.dry_run_info['folder']) | ||
click.echo(f"-> Files created in folder '{rel_path}'") | ||
click.echo(f"-> Submission script filename: '{node.dry_run_info['script_filename']}'") | ||
return | ||
|
||
if node.is_finished and node.exit_message: | ||
state = f'{node.process_state.value} [{node.exit_status}] `{node.exit_message}`' | ||
elif node.is_finished: | ||
state = f'{node.process_state.value} [{node.exit_status}]' | ||
else: | ||
state = node.process_state.value | ||
|
||
click.echo(f'{class_name}<{node.pk}> terminated with state: {state}') | ||
|
||
if not outputs: | ||
click.echo(f'{class_name}<{node.pk}> registered no outputs') | ||
return | ||
|
||
click.echo(f"\n{'Output link':25s} Node pk and type") | ||
click.echo(f"{'-' * 60}") | ||
|
||
for triple in sorted(outputs, key=lambda triple: triple.link_label): | ||
click.echo(f'{triple.link_label:25s} {triple.node.__class__.__name__}<{triple.node.pk}> ') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
# -*- coding: utf-8 -*- | ||
"""Module with launch utitlies for the CLI.""" | ||
import click | ||
|
||
from .display import echo_process_results | ||
|
||
|
||
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 | ||
:param daemon: boolean, if True will submit to the daemon instead of running in current interpreter | ||
:param inputs: inputs for the process | ||
""" | ||
from aiida.engine import Process, ProcessBuilder, launch | ||
|
||
if isinstance(process, ProcessBuilder): | ||
process_name = process.process_class.__name__ | ||
elif issubclass(process, Process): | ||
process_name = process.__name__ | ||
else: | ||
raise TypeError(f'invalid type for process: {process}') | ||
|
||
if daemon: | ||
node = launch.submit(process, **inputs) | ||
click.echo( | ||
f""" | ||
Submitted {process_name} to the daemon. | ||
Information of the launched process: | ||
* PK :\t{node.pk}> | ||
* UUID:\t{node.uuid}> | ||
Record this information for later usage, or put this node in a Group. | ||
""" | ||
) | ||
else: | ||
if inputs.get('metadata', {}).get('dry_run', False): | ||
click.echo(f'Running a dry run for {process_name}...') | ||
else: | ||
click.echo(f'Running a {process_name}...') | ||
_, node = launch.run_get_node(process, **inputs) | ||
echo_process_results(node) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
# -*- coding: utf-8 -*- | ||
"""Pre-defined overridable options for commonly used command line interface parameters.""" | ||
# pylint: disable=too-few-public-methods,import-error | ||
from aiida.cmdline.params import types | ||
from aiida.cmdline.params.options import OverridableOption | ||
from aiida.cmdline.utils import decorators | ||
from aiida.common import exceptions | ||
import click | ||
|
||
from . import validate | ||
|
||
|
||
class PseudoFamilyType(types.GroupParamType): | ||
"""Subclass of `GroupParamType` in order to be able to print warning with instructions.""" | ||
|
||
def __init__(self, pseudo_types=None, **kwargs): | ||
"""Construct a new instance.""" | ||
super().__init__(**kwargs) | ||
self._pseudo_types = pseudo_types | ||
|
||
@decorators.with_dbenv() | ||
def convert(self, value, param, ctx): | ||
"""Convert the value to actual pseudo family instance.""" | ||
try: | ||
group = super().convert(value, param, ctx) | ||
except click.BadParameter: | ||
try: | ||
from aiida.orm import load_group | ||
load_group(value) | ||
except exceptions.NotExistent: # pylint: disable=try-except-raise | ||
raise | ||
|
||
raise click.BadParameter( # pylint: disable=raise-missing-from | ||
f'`{value}` is not of a supported pseudopotential family type.\nTo install a supported ' | ||
'pseudofamily, use the `aiida-pseudo` plugin. See the following link for detailed instructions:\n\n' | ||
' https://github.com/aiidateam/aiida-quantumespresso#pseudopotentials' | ||
) | ||
|
||
if self._pseudo_types is not None and group.pseudo_type not in self._pseudo_types: | ||
pseudo_types = ', '.join(self._pseudo_types) | ||
raise click.BadParameter( | ||
f'family `{group.label}` contains pseudopotentials of the wrong type `{group.pseudo_type}`.\nOnly the ' | ||
f'following types are supported: {pseudo_types}' | ||
) | ||
|
||
return group | ||
|
||
|
||
PW_CODE = OverridableOption( | ||
'--pw', | ||
'pw_code', | ||
type=types.CodeParamType(entry_point='quantumespresso.pw'), | ||
required=True, | ||
help='The code to use for the pw.x executable.' | ||
) | ||
|
||
PHONOPY_CODE = OverridableOption( | ||
'--phonopy', | ||
'phonopy_code', | ||
type=types.CodeParamType(entry_point='phonopy.phonopy'), | ||
required=True, | ||
help='The code to use for the phonopy executable.' | ||
) | ||
|
||
PSEUDO_FAMILY = OverridableOption( | ||
'-F', | ||
'--pseudo-family', | ||
type=PseudoFamilyType(sub_classes=('aiida.groups:pseudo.family',), pseudo_types=('pseudo.upf',)), | ||
required=False, | ||
help='Select a pseudopotential family, identified by its label.' | ||
) | ||
|
||
STRUCTURE = OverridableOption( | ||
'-S', | ||
'--structure', | ||
type=types.DataParamType(sub_classes=('aiida.data:core.structure',)), | ||
help='A StructureData node identified by its ID or UUID.' | ||
) | ||
|
||
KPOINTS_MESH = OverridableOption( | ||
'-k', | ||
'--kpoints-mesh', | ||
'kpoints_mesh', | ||
nargs=6, | ||
type=click.Tuple([int, int, int, float, float, float]), | ||
show_default=True, | ||
callback=validate.validate_kpoints_mesh, | ||
help='The number of points in the kpoint mesh along each basis vector and the offset. ' | ||
'Example: `-k 2 2 2 0 0 0`. Specify `0.5 0.5 0.5` for the offset if you want to result ' | ||
'in the equivalent Quantum ESPRESSO pw.x `1 1 1` shift.' | ||
) | ||
|
||
PARENT_FOLDER = OverridableOption( | ||
'-P', | ||
'--parent-folder', | ||
'parent_folder', | ||
type=types.DataParamType(sub_classes=('aiida.data:core.remote',)), | ||
show_default=True, | ||
required=False, | ||
help='A parent remote folder node identified by its ID or UUID.' | ||
) | ||
|
||
DAEMON = OverridableOption( | ||
'-D', | ||
'--daemon', | ||
is_flag=True, | ||
default=True, | ||
show_default=True, | ||
help='Submit the process to the daemon instead of running it and waiting for it to finish.' | ||
) | ||
|
||
OVERRIDES = OverridableOption( | ||
'-o', | ||
'--overrides', | ||
type=click.File('r'), | ||
required=False, | ||
help='The filename or filepath containing the overrides, in YAML format.' | ||
) | ||
|
||
PROTOCOL = OverridableOption( | ||
'-p', | ||
'--protocol', | ||
type=click.STRING, | ||
required=False, | ||
help='Select the protocol that defines the accuracy of the calculation.' | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# -*- coding: utf-8 -*- | ||
"""Utility functions for validation of command line interface parameter inputs.""" | ||
from aiida.cmdline.utils import decorators | ||
import click | ||
|
||
|
||
@decorators.with_dbenv() | ||
def validate_kpoints_mesh(ctx, param, value): | ||
"""Command line option validator for a kpoints mesh tuple. | ||
The value should be a tuple of three positive integers out of which a KpointsData object will be created with a mesh | ||
equal to the tuple. | ||
:param ctx: internal context of the click.command | ||
:param param: the click Parameter, i.e. either the Option or Argument to which the validator is hooked up | ||
:param value: a tuple of three positive integers | ||
:returns: a KpointsData instance | ||
""" | ||
# pylint: disable=unused-argument | ||
from aiida.orm import KpointsData | ||
|
||
if not value: | ||
return None | ||
|
||
if any(not isinstance(integer, int) for integer in value[:3]) or any(int(i) <= 0 for i in value[:3]): | ||
raise click.BadParameter('all values of the tuple should be positive greater than zero integers') | ||
|
||
try: | ||
kpoints = KpointsData() | ||
kpoints.set_kpoints_mesh(value[:3], value[3:]) | ||
except ValueError as exception: | ||
raise click.BadParameter(f'failed to create a KpointsData mesh out of {value}\n{exception}') | ||
|
||
return kpoints |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# -*- coding: utf-8 -*- | ||
# pylint: disable=cyclic-import,reimported,unused-import,wrong-import-position,import-error | ||
"""Module with CLI commands for the various work chain implementations.""" | ||
from .. import cmd_root | ||
|
||
|
||
@cmd_root.group('launch') | ||
def cmd_launch(): | ||
"""Launch workflows.""" | ||
|
||
|
||
from .dielectric.base import launch_workflow | ||
from .phonons.base import launch_workflow | ||
from .phonons.harmonic import launch_workflow | ||
# Import the sub commands to register them with the CLI | ||
from .spectra.iraman import launch_workflow |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# -*- coding: utf-8 -*- | ||
# pylint: disable=cyclic-import,unused-import,wrong-import-position,import-error | ||
"""Module with CLI commands for various dielectric workflows.""" |
Oops, something went wrong.