-
Notifications
You must be signed in to change notification settings - Fork 29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
aiida-cp2k cli #105
Open
dev-zero
wants to merge
5
commits into
aiidateam:main
Choose a base branch
from
dev-zero:feature/cli
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
aiida-cp2k cli #105
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
8418c64
initial commit of the aiida-cp2k cli
dev-zero 7027f1e
initial commit of the workflow command
dev-zero 195cc45
docker: install cli deps for testing
dev-zero d7791ab
cli/workchains: add more options, parse referenced structure file
dev-zero c5313ce
setup: register structure import command with aiida.cmdline.data.stru…
dev-zero File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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,22 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
# pylint: disable=wrong-import-position,wildcard-import | ||
"""Base command line interface module to wire up subcommands and loading the profile.""" | ||
|
||
import click | ||
import click_completion | ||
|
||
from aiida.cmdline.params import options, types | ||
|
||
# Activate the completion of parameter types provided by the click_completion package | ||
click_completion.init() | ||
|
||
|
||
@click.group('aiida-cp2k', context_settings={'help_option_names': ['-h', '--help']}) | ||
@options.PROFILE(type=types.ProfileParamType(load_profile=True)) | ||
def cmd_root(profile): # pylint: disable=unused-argument | ||
"""CLI for the `aiida-cp2k` plugin.""" | ||
|
||
|
||
from .data import cmd_structure | ||
from .workflows import cmd_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,15 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
# pylint: disable=cyclic-import,unused-import,wrong-import-position | ||
"""Module with CLI commands for various data types.""" | ||
|
||
from .. import cmd_root | ||
|
||
|
||
@cmd_root.group('data') | ||
def cmd_data(): | ||
"""Commands to create and inspect data nodes.""" | ||
|
||
|
||
# Import the sub commands to register them with the CLI | ||
from .structure import cmd_structure |
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,36 @@ | ||
# -*- coding: utf-8 -*- | ||
"""Command line utilities to create and inspect `StructureData` nodes from CP2K input files.""" | ||
|
||
import click | ||
|
||
from aiida.cmdline.params import options | ||
from aiida.cmdline.utils import decorators, echo | ||
|
||
from . import cmd_data | ||
from ..utils.structure import structure_from_cp2k_inp | ||
|
||
|
||
@cmd_data.group('structure') | ||
def cmd_structure(): | ||
"""Commands to create and inspect `StructureData` nodes from CP2K input.""" | ||
|
||
|
||
@cmd_structure.command('import') | ||
@click.argument('filename', type=click.File('r')) | ||
@options.DRY_RUN() | ||
@decorators.with_dbenv() | ||
def cmd_import(filename, dry_run): | ||
"""Import a `StructureData` from a CP2K input file.""" | ||
|
||
try: | ||
structure = structure_from_cp2k_inp(filename) | ||
except ValueError as exc: | ||
echo.echo_critical(str(exc)) | ||
|
||
formula = structure.get_formula() | ||
|
||
if dry_run: | ||
echo.echo_success('parsed structure with formula {}'.format(formula)) | ||
else: | ||
structure.store() | ||
echo.echo_success('parsed and stored StructureData<{}> with formula {}'.format(structure.pk, formula)) |
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,64 @@ | ||
"""Some helpers for writing CLI functions""" | ||
|
||
# pylint: disable=import-outside-toplevel | ||
|
||
import os | ||
|
||
import click | ||
|
||
# the following 2 methods originated from aiiida-quantumespresso | ||
|
||
|
||
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.cmdline.utils.common import get_node_info | ||
|
||
class_name = node.process_class.__name__ | ||
|
||
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("-> Files created in folder '{}'".format(rel_path)) | ||
click.echo("-> Submission script filename: '{}'".format(node.dry_run_info['script_filename'])) | ||
return | ||
|
||
if node.is_finished and node.exit_message: | ||
state = '{} [{}] `{}`'.format(node.process_state.value, node.exit_status, node.exit_message) | ||
elif node.is_finished: | ||
state = '{} [{}]'.format(node.process_state.value, node.exit_status) | ||
else: | ||
state = node.process_state.value | ||
|
||
click.echo("{}<{}> terminated with state: {}]\n".format(class_name, node.pk, state)) | ||
click.echo(get_node_info(node)) | ||
|
||
|
||
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 launch, Process, ProcessBuilder | ||
|
||
if isinstance(process, ProcessBuilder): | ||
process_name = process.process_class.__name__ | ||
elif issubclass(process, Process): | ||
process_name = process.__name__ | ||
else: | ||
raise TypeError('invalid type for process: {}'.format(process)) | ||
|
||
if daemon: | ||
node = launch.submit(process, **inputs) | ||
click.echo('Submitted {}<{}> to the daemon'.format(process_name, node.pk)) | ||
else: | ||
if inputs.get('metadata', {}).get('dry_run', False): | ||
click.echo('Running a dry run for {}...'.format(process_name)) | ||
else: | ||
click.echo('Running a {}...'.format(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,31 @@ | ||
# -*- coding: utf-8 -*- | ||
"""Helpers to setup command line options""" | ||
|
||
from aiida.cmdline.params import types | ||
from aiida.cmdline.params.options import OverridableOption | ||
|
||
DAEMON = OverridableOption("-d", | ||
"--daemon", | ||
is_flag=True, | ||
default=False, | ||
show_default=True, | ||
help="Submit the process to the daemon instead of running it directly.") | ||
|
||
STRUCTURE = OverridableOption("-s", | ||
"--structure", | ||
type=types.DataParamType(sub_classes=("aiida.data:structure",)), | ||
help="StructureData node.") | ||
|
||
MAX_NUM_MACHINES = OverridableOption('-m', | ||
'--max-num-machines', | ||
type=int, | ||
default=1, | ||
show_default=True, | ||
help='The maximum number of machines (nodes) to use for the calculations.') | ||
|
||
MAX_WALLCLOCK_SECONDS = OverridableOption('-w', | ||
'--max-wallclock-seconds', | ||
type=int, | ||
default=1800, | ||
show_default=True, | ||
help='the maximum wallclock time in seconds to set for the calculations.') |
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,94 @@ | ||
# -*- coding: utf-8 -*- | ||
"""Helper functions for building CLI functions dealing with structures""" | ||
|
||
import re | ||
|
||
import numpy as np | ||
|
||
|
||
def structure_from_cp2k_inp(filename): | ||
"""Create an AiiDA StructureData from a structure inside a CP2K input file""" | ||
# pylint: disable=import-outside-toplevel,invalid-name,too-many-locals,too-many-statements,too-many-branches | ||
|
||
from cp2k_input_tools.parser import CP2KInputParser | ||
from aiida.orm.nodes.data.structure import StructureData, Kind, Site, symop_fract_from_ortho | ||
from ase.geometry.cell import cell_to_cellpar, cellpar_to_cell | ||
import ase.io | ||
|
||
# the following was taken from aiida-quantumespresso | ||
VALID_ELEMENTS_REGEX = re.compile( | ||
r""" | ||
^( | ||
H | He | | ||
Li | Be | B | C | N | O | F | Ne | | ||
Na | Mg | Al | Si | P | S | Cl | Ar | | ||
K | Ca | Sc | Ti | V | Cr | Mn | Fe | Co | Ni | Cu | Zn | Ga | Ge | As | Se | Br | Kr | | ||
Rb | Sr | Y | Zr | Nb | Mo | Tc | Ru | Rh | Pd | Ag | Cd | In | Sn | Sb | Te | I | Xe | | ||
Cs | Ba | Hf | Ta | W | Re | Os | Ir | Pt | Au | Hg | Tl | Pb | Bi | Po | At | Rn | | ||
Fr | Ra | Rf | Db | Sg | Bh | Hs | Mt | | ||
La | Ce | Pr | Nd | Pm | Sm | Eu | Gd | Tb | Dy | Ho | Er | Tm | Yb | Lu | # Lanthanides | ||
Ac | Th | Pa | U | Np | Pu | Am | Cm | Bk | Cf | Es | Fm | Md | No | Lr | # Actinides | ||
) | ||
""", re.VERBOSE | re.IGNORECASE) | ||
|
||
parser = CP2KInputParser() | ||
tree = parser.parse(filename) | ||
force_eval_no = -1 | ||
force_eval = None | ||
|
||
for force_eval_no, force_eval in enumerate(tree['+force_eval']): | ||
try: | ||
cell = force_eval['+subsys']['+cell'] | ||
kinds = force_eval['+subsys']['+kind'] | ||
break # for now grab the first &COORD found | ||
except KeyError: | ||
continue | ||
else: | ||
raise ValueError('no CELL, or KIND found in the given input file') | ||
|
||
# CP2K can get its cell information in two ways: | ||
# - A, B, C: cell vectors | ||
# - ABC: scaling of cell vectors, ALPHA_BETA_GAMMA: angles between the cell vectors (optional) | ||
|
||
if 'a' in cell: | ||
unit_cell = np.array([cell['a'], cell['b'], cell['c']]) # unit vectors given | ||
cellpar = cell_to_cellpar(unit_cell) | ||
elif 'abc' in cell: | ||
cellpar = cell['abc'] + cell.get('alpha_beta_gamma', [90., 90., 90.]) | ||
unit_cell = cellpar_to_cell(cellpar) | ||
else: | ||
raise ValueError('incomplete &CELL section') | ||
|
||
pbc = [c in cell.get('periodic', 'XYZ') for c in 'XYZ'] | ||
|
||
structure = StructureData(cell=unit_cell, pbc=pbc) | ||
|
||
if force_eval['+subsys'].get('+coord', {}).get('scaled', False): | ||
tmat = symop_fract_from_ortho(cellpar) | ||
else: | ||
tmat = np.eye(3) | ||
|
||
for kind in kinds: | ||
name = kind['_'] | ||
|
||
try: | ||
# prefer the ELEMENT keyword, fallback to extracting from name | ||
element = kind.get('element', VALID_ELEMENTS_REGEX.search(name)[0]) | ||
except TypeError: | ||
raise ValueError('ELEMENT not set and unable to extract from {name}'.format(name=name)) | ||
|
||
structure.append_kind(Kind(name=name, symbols=element)) | ||
|
||
try: | ||
structfn = force_eval["+subsys"]["+topology"]["coord_file_name"] | ||
atoms = ase.io.read(structfn) | ||
|
||
for name, position in zip(atoms.symbols, atoms.positions): | ||
structure.append_site(Site(kind_name=name, position=tmat @ np.array(position))) | ||
|
||
except KeyError: | ||
for name, position, _ in parser.coords(force_eval_no): | ||
# positions can be scaled, apply transformation matrix | ||
structure.append_site(Site(kind_name=name, position=tmat @ np.array(position))) | ||
|
||
return structure |
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,18 @@ | ||
# -*- coding: utf-8 -*- | ||
# pylint: disable=cyclic-import,unused-import,wrong-import-position | ||
"""Base workflow commands and sub-commands""" | ||
|
||
from .. import cmd_root | ||
|
||
|
||
@cmd_root.group("workflow") | ||
def cmd_workflow(): | ||
"""Commands to launch and interact with workflows.""" | ||
|
||
|
||
@cmd_workflow.group("launch") | ||
def cmd_launch(): | ||
"""Launch workflow.""" | ||
|
||
|
||
from .base import cmd_launch_workflow |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add the number of CPUs per node parameter.