Skip to content

Commit

Permalink
Some changes for CLI
Browse files Browse the repository at this point in the history
  • Loading branch information
d-wortmann committed May 28, 2024
1 parent 7627aff commit 05e1a2d
Show file tree
Hide file tree
Showing 6 changed files with 218 additions and 12 deletions.
3 changes: 3 additions & 0 deletions aiida_fleur/cmdline/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from .workflows import cmd_workflow
from .visualization import cmd_plot
from .util import options as options_af
from .util import cmd_defaults

# Activate the completion of parameter types provided by the click_completion package
# for bash: eval "$(_AIIDA_FLEUR_COMPLETE=source aiida-fleur)"
Expand Down Expand Up @@ -59,3 +60,5 @@ def cmd_root(profile): # pylint: disable=unused-argument
cmd_root.add_command(cmd_data)
cmd_root.add_command(cmd_workflow)
cmd_root.add_command(cmd_plot)

cmd_root.add_command(cmd_defaults)
19 changes: 17 additions & 2 deletions aiida_fleur/cmdline/launch/launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,8 @@ def launch_scf(structure, inpgen, calc_parameters, fleurinp, fleur, wf_parameter
from aiida.orm import load_node
wf=load_node(pk)
scf_output=wf.outputs.output_scf_wc_para.get_dict()
scf_output["SCF-uuid"]=wf.uuid

#json with dict
import json
with open("scf.json","w") as file:
Expand Down Expand Up @@ -319,7 +321,7 @@ def launch_eos(structure, inpgen, calc_parameters, fleur, wf_parameters, scf_par


@click.command('banddos')
@options.FLEURINP()
@options.FLEURINP(default='inp.xml')
@options.FLEUR()
@options.WF_PARAMETERS()
@options.REMOTE()
Expand All @@ -330,6 +332,7 @@ def launch_banddos(fleurinp, fleur, wf_parameters, parent_folder, daemon, settin
"""
Launch a banddos workchain
"""

workchain_class = WorkflowFactory('fleur.banddos')
inputs = {
'wf_parameters': wf_parameters,
Expand All @@ -341,7 +344,19 @@ def launch_banddos(fleurinp, fleur, wf_parameters, parent_folder, daemon, settin
inputs = clean_nones(inputs)
builder = workchain_class.get_builder()
builder.update(inputs)
utils.launch_process(builder, daemon)
pk=utils.launch_process(builder, daemon)

#Now create output files
from aiida.orm import load_node
wf=load_node(pk)
banddos_output=wf.outputs.output_banddos_wc_para.get_dict()
#json with dict
import json
with open("banddos.json","w") as file:
json.dump(banddos_output,file,indent=2)
#plot
from aiida_fleur.tools.plot.fleur import plot_fleur
plot_fleur(wf,save=True,show=False)


@click.command('init_cls')
Expand Down
28 changes: 28 additions & 0 deletions aiida_fleur/cmdline/util/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,31 @@
'''
AiiDA-FLEUR
'''
import click
import json

from .defaults import get_code_interactive,get_default_dict
from aiida.cmdline.utils import decorators


# general further commands for fleur workchains
@click.command('config')
@decorators.with_dbenv()
def cmd_defaults():
"""Interactively create/modify the default settings for aiida-fleur CLI."""

dict=get_default_dict()

#default codes
dict["fleur"]=get_code_interactive("fleur.fleur",dict["fleur"])
dict["inpgen"]=get_code_interactive("fleur.inpgen",dict["inpgen"])

try:
os.mkdir(f"{HOME}/.aiida-fleur")
except:
pass #dir might exist already
with open(f"{HOME}/.aiida-fleur/cli.json","w") as f:
json.dump(dict,f)



71 changes: 66 additions & 5 deletions aiida_fleur/cmdline/util/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,24 @@
Here we specify some defaults for cli commands
"""

# Structures
def get_default_dict():
import os
HOME=os.getenv("HOME")
#first see if we have already a setting file
try:
with open(f"{HOME}/.aiida-fleur/cli.json","r") as f:
dict=json.load(f)
except:
dict={"fleur":None,
"inpgen":None,
"copyback":False,
"resources":None
}
return dict



# Structures
def get_si_bulk_structure():
"""Return a `StructureData` representing bulk silicon.
Expand Down Expand Up @@ -84,15 +99,20 @@ def get_fept_film_structure():
# Codes
def get_inpgen():
"""Return a `Code` node of the latest added inpgen executable in the database."""

return get_last_code('fleur.inpgen')
try:
return get_default_dict()["inpgen"]
except:
return get_last_code('fleur.inpgen')


def get_fleur():
"""Return a `Code` node of the latest added inpgen executable in the database."""
try:
return get_default_dict()["fleur"]
except:
return get_last_code('fleur.fleur')

return get_last_code('fleur.fleur')



def get_last_code(entry_point_name):
"""Return a `Code` node of the latest code executable of the given entry_point_name in the database.
Expand All @@ -117,3 +137,44 @@ def get_last_code(entry_point_name):
if not results:
raise NotExistent(f'ERROR: Could not find any Code in the database with entry point: {entry_point_name}!')
return results[0].uuid


def get_code_interactive(entry_point_name,default_uuid=None):
"""Return a `Code` node of the given entry_point_name in the database.
The database will be queried for the existence the possible codes, they will be listed and
one can be choosen.
If this is not exists and NotExistent error is raised.
:param entry_point_name: string
:return: the uuid of a inpgen `Code` node
:raise: aiida.common.exceptions.NotExistent
"""
import click
from aiida.orm import QueryBuilder, Code
from aiida.common.exceptions import NotExistent

filters = {'attributes.input_plugin': {'==': entry_point_name}}

builder = QueryBuilder().append(Code, filters=filters)

if not builder.all():
raise NotExistent(f'ERROR: Could not find any Code in the database with entry point: {entry_point_name}!')

print(f"Selection for {entry_point_name}:")
i=0
default_i=0
for code in builder.all():
if code[0].uuid==default_uuid:
default_i=i
print(f"{i}:{code[0].full_label}")
i=i+1
i=click.prompt("Please enter your choice",type=int,default=default_i)
try:
result=builder.all()[i]
except:
return default_uuid


return result[0].uuid
8 changes: 4 additions & 4 deletions aiida_fleur/cmdline/util/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from aiida.cmdline.params import types
from aiida.cmdline.params.options import OverridableOption
from .defaults import get_inpgen, get_fleur, get_si_bulk_structure
from .types import StructureNodeOrFileParamType
from .types import StructureNodeOrFileParamType,WFParameterType,RemoteType,FleurinpType

STRUCTURE_OR_FILE = OverridableOption(
'-s',
Expand Down Expand Up @@ -55,7 +55,7 @@

FLEURINP = OverridableOption('-inp',
'--fleurinp',
type=types.DataParamType(sub_classes=('aiida.data:fleur.fleurinp',)),
type=FleurinpType(),
help='FleurinpData node for the fleur calculation.')

CALC_PARAMETERS = OverridableOption(
Expand All @@ -71,7 +71,7 @@

WF_PARAMETERS = OverridableOption('-wf',
'--wf-parameters',
type=types.DataParamType(sub_classes=('aiida.data:core.dict',)),
type=WFParameterType(),
help='Dict containing parameters given to the workchain.')

SCF_PARAMETERS = OverridableOption('-scf',
Expand Down Expand Up @@ -132,7 +132,7 @@
REMOTE = OverridableOption('-P',
'--parent-folder',
'parent_folder',
type=types.DataParamType(sub_classes=('aiida.data:core.remote',)),
type=RemoteType(),
show_default=True,
required=False,
help='The PK of a parent remote folder (for restarts).')
Expand Down
101 changes: 100 additions & 1 deletion aiida_fleur/cmdline/util/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,106 @@
from aiida.plugins import DataFactory
from aiida.cmdline.utils.decorators import with_dbenv

wf_template_files={"eos":'{"points": 9,\n'
'"step": 0.002,\n'
'"guess": 1.00}',
"scf":'{"fleur_runmax": 4,\n'
'"density_converged": 0.00002,\n'
'"energy_converged": 0.002,\n'
'"mode": "density",\n'
'"itmax_per_run": 30}',
"banddos":'{"mode": "band",\n'
'"kpath": "auto",\n'
'"klistname": "path-3",\n'
'"kpoints_number": None,\n'
'"kpoints_distance": None,\n'
'"kpoints_explicit": None,\n'
'"sigma": 0.005,\n'
'"emin": -0.50,\n'
'"emax": 0.90\n}'
}


class FleurinpType(click.ParamType):
"""
Type to either load a fleurinp node or read an inp.xml file
"""
name = "FleurInp data/inp.xml file"
def convert(self,value,param,ctx):
try:
return types.DataParamType(sub_classes=('aiida.data:fleur.fleurinp',)).convert(value, param, ctx)
except:
pass #Ok this failed, so we try to read the file

if value in ["inp.xml",".",'./']:
from aiida_fleur.data.fleurinp import FleurinpData
inp_files=["inp.xml"]
#check if there are included files in this dir
for file in ["kpts.xml","sym.xml","relax.xml"]:
import os.path
if os.path.isfile(file):
inp_files.append(file)
finp = FleurinpData(files=inp_files)
return finp.store()
return None

class RemoteType(click.ParamType):
"""
Type for remote data. Might be specified by a uuid or a file in which this uuid is found
"""
name = "Remote folder"
def convert(self,value,param,ctx):
#Try to interpret value as uuid
try:
return types.DataParamType(sub_classes=('aiida.data:core.remote',)).convert(value, param, ctx)
except:
pass #Ok this failed, so we try to read the file

try:
from aiida.orm import load_node
with open(value,"r") as f:
import json
dict_from_file=json.load(f)

scf_wf=load_node(dict_from_file["SCF-uuid"])
print(scf_wf)
return scf_wf.outputs.last_calc.remote_folder
except:
return None

class WFParameterType(click.ParamType):
"""
ParamType for giving workflow parameters
"""
name = "Workflow parameters"
def convert(self,value,param,ctx):
import os

if value=="template.json":
if ctx.command.name in wf_template_files:
with open(f"wf_{ctx.command.name}.json","w") as f:
f.write(wf_template_files[ctx.command.name])
quit()

if (os.path.isfile(value)):
# a file was given. Create a dict from the file and use it
try:
with open(value,"r") as f:
import json
wf_param=json.load(f)
except:
print(f"{value} could not be converted into a dict")
os.abort()
aiida_dict=DataFactory("dict")
wf_dict=aiida_dict(wf_param)

return wf_dict.store()

#Now load from aiida
wf_dict = types.DataParamType(sub_classes=('aiida.data:core.dict',)).convert(value, param, ctx)

return wf_dict

class StructureNodeOrFileParamType(click.ParamType):
"""
The ParamType for identifying a structure by node or to extract it from a given file
Expand Down Expand Up @@ -51,7 +150,7 @@ def convert(self, value, param, ctx):
try:
structure = types.DataParamType(sub_classes=('aiida.data:core.structure',)).convert(value, param, ctx)
except (NotExistent, click.exceptions.BadParameter) as er:
echo.echo(f'Tried to load node, could not fine one for {value}. '
echo.echo(f'Tried to load node, could not find one for {value}. '
'I will further check if it is a filepath.')
is_path = True

Expand Down

0 comments on commit 05e1a2d

Please sign in to comment.