From c15d1c34fa459b594be13d1284e2cbb70571743c Mon Sep 17 00:00:00 2001 From: Daniel Wortmann Date: Wed, 12 Jun 2024 15:07:21 +0200 Subject: [PATCH] Reworked CLI --- aiida_fleur/cmdline/launch/__init__.py | 6 +- aiida_fleur/cmdline/launch/launch.py | 85 +++++++++++++++++++++++--- aiida_fleur/cmdline/util/types.py | 49 ++++++++++----- aiida_fleur/parsers/fleur.py | 2 +- aiida_fleur/tools/plot/fleur.py | 40 ++++++++++-- 5 files changed, 151 insertions(+), 31 deletions(-) diff --git a/aiida_fleur/cmdline/launch/__init__.py b/aiida_fleur/cmdline/launch/__init__.py index d5480e73e..69a6b00fc 100644 --- a/aiida_fleur/cmdline/launch/__init__.py +++ b/aiida_fleur/cmdline/launch/__init__.py @@ -15,7 +15,8 @@ from .launch import launch_inpgen from .launch import launch_fleur from .launch import launch_scf -from .launch import launch_banddos +from .launch import launch_dos +from .launch import launch_band from .launch import launch_relax from .launch import launch_eos from .launch import launch_corehole @@ -36,7 +37,8 @@ def cmd_launch(): cmd_launch.add_command(launch_inpgen) cmd_launch.add_command(launch_fleur) cmd_launch.add_command(launch_scf) -cmd_launch.add_command(launch_banddos) +cmd_launch.add_command(launch_dos) +cmd_launch.add_command(launch_band) cmd_launch.add_command(launch_relax) cmd_launch.add_command(launch_eos) cmd_launch.add_command(launch_corehole) diff --git a/aiida_fleur/cmdline/launch/launch.py b/aiida_fleur/cmdline/launch/launch.py index d5991169e..b905fbe40 100755 --- a/aiida_fleur/cmdline/launch/launch.py +++ b/aiida_fleur/cmdline/launch/launch.py @@ -218,7 +218,7 @@ def launch_scf(structure, inpgen, calc_parameters, fleurinp, fleur, wf_parameter f.write(wf.outputs.last_calc.retrieved.get_object_content(file,"rb")) @click.command('relax') -@options.STRUCTURE_OR_FILE(default=defaults.get_si_bulk_structure, show_default=True) +@options.STRUCTURE_OR_FILE(default="inp.xml", show_default=True) @options.INPGEN() @options.CALC_PARAMETERS() @options.FLEUR() @@ -234,9 +234,24 @@ def launch_relax(structure, inpgen, calc_parameters, fleur, wf_parameters, scf_p # TODO final scf input """ - workchain_class = WorkflowFactory('fleur.base_relax') + from aiida_fleur.workflows.relax import FleurRelaxWorkChain + + if isinstance(structure, FleurinpData): + fleurinp=structure + structure=None + inpgen=None + + # we need a scf_paramters dict to change the forcemix if required later + if scf_parameters==None: + scf_parameters=Dict(dict= { + 'force_dict': {'forcemix': 'BFGS'}, + 'inpxml_changes': [] + }) + + #workchain_class = WorkflowFactory('fleur.base_relax') inputs = { 'scf': { + 'fleurinp': fleurinp, 'wf_parameters': scf_parameters, 'structure': structure, 'calc_parameters': calc_parameters, @@ -246,11 +261,35 @@ def launch_relax(structure, inpgen, calc_parameters, fleur, wf_parameters, scf_p }, 'wf_parameters': wf_parameters } + + inputs = clean_nones(inputs) - builder = workchain_class.get_builder() + builder = FleurRelaxWorkChain.get_builder() builder.update(inputs) - utils.launch_process(builder, daemon) + pk=utils.launch_process(builder, daemon) + + #Now create output files + if fleurinp and not daemon: + from aiida.orm import load_node + wf=load_node(pk) + relax_output=wf.outputs.output_relax_wc_para.get_dict() + relax_output["Relax-uuid"]=wf.uuid + relax_output["retrieved-uuid"]=wf.outputs.last_scf.last_calc.retrieved.uuid + + + #json with dict + import json + with open("relax.json","w") as file: + json.dump(relax_output,file,indent=2) + #plot + from aiida_fleur.tools.plot.fleur import plot_fleur + plot_fleur([wf],save=True,show=False) + #store files + for file in ["relax.xml","out.xml","cdn1","cdn_last.hdf"]: + if file in wf.outputs.last_scf.last_calc.retrieved.list_object_names(): + with open(file,"wb") as f: + f.write(wf.outputs.last_scf.last_calc.retrieved.get_object_content(file,"rb")) @click.command('eos') @options.STRUCTURE_OR_FILE(default="inp.xml", show_default=True) @@ -320,7 +359,7 @@ def launch_eos(structure, inpgen, calc_parameters, fleur, wf_parameters, scf_par -@click.command('banddos') +@click.command('dos') @options.FLEURINP(default='inp.xml') @options.FLEUR() @options.WF_PARAMETERS() @@ -328,11 +367,39 @@ def launch_eos(structure, inpgen, calc_parameters, fleur, wf_parameters, scf_par @options.DAEMON() @options.SETTINGS() @options.OPTION_NODE() -def launch_banddos(fleurinp, fleur, wf_parameters, parent_folder, daemon, settings, option_node): +def launch_dos(fleurinp, fleur, wf_parameters, parent_folder, daemon, settings, option_node): """ - Launch a banddos workchain + Launch a banddos workchain (special command to set the dos as a default mode) """ + if wf_parameters==None: + wf_parameters=Dict({"mode":"dos"}) + else: + wf_dict=wf_parameters.get_dict() + wf_dict["mode"]="dos" + wf_parameters=Dict(wf_dict) + + launch_banddos(fleurinp, fleur, wf_parameters, parent_folder, daemon, settings, option_node) + +@click.command('band') +@options.FLEURINP(default='inp.xml') +@options.FLEUR() +@options.WF_PARAMETERS() +@options.REMOTE() +@options.DAEMON() +@options.SETTINGS() +@options.OPTION_NODE() +def launch_band(fleurinp, fleur, wf_parameters, parent_folder, daemon, settings, option_node): + """ + Launch a banddos workchain in 'band' mode + """ + #Band is default + launch_banddos(fleurinp, fleur, wf_parameters, parent_folder, daemon, settings, option_node) + +def launch_banddos(fleurinp, fleur, wf_parameters, parent_folder, daemon, settings, option_node): + """ + Launch a banddos workchain + """ workchain_class = WorkflowFactory('fleur.banddos') inputs = { 'wf_parameters': wf_parameters, @@ -354,6 +421,10 @@ def launch_banddos(fleurinp, fleur, wf_parameters, parent_folder, daemon, settin import json with open("banddos.json","w") as file: json.dump(banddos_output,file,indent=2) + #the banddos.hdf file + with open(f"banddos.hdf","wb") as f: + f.write(wf.outputs.banddos_calc.retrieved.get_object_content("banddos.hdf",'rb')) + #plot from aiida_fleur.tools.plot.fleur import plot_fleur plot_fleur(wf,save=True,show=False) diff --git a/aiida_fleur/cmdline/util/types.py b/aiida_fleur/cmdline/util/types.py index 8b5e83cd0..093413264 100644 --- a/aiida_fleur/cmdline/util/types.py +++ b/aiida_fleur/cmdline/util/types.py @@ -26,15 +26,28 @@ '"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}' + "band":'{\n' + '// Select your k-points\n' + '"kpath": "auto"// This can be "auto" (meaning you try to pick a good choice automatically from inp.xml) or "seek" to use seek-k-path\n' + '//",klistname": "path-3"//You can specify directly a list in the inp.xml/kpts.xml\n' + '//",kpoints_number": 200\n' + '//",kpoints_distance": 0.1\n' + '//",kpoints_explicit": None\n}', + "dos":'{\n' + '// Select your k-points\n' + '//"klistname": "path-3",//You can specify directly a list in the inp.xml/kpts.xml\n' + '//"kpoints_number": 200,\n' + '//"kpoints_distance": 0.1,\n' + '// These parameters are relevant for the DOS mode\n' + '"sigma": 0.005\n' + '",emin": -0.50\n' + '",emax": 0.90\n}', + "relax":'{\n' + '"film_distance_relaxation": "False", // if True, sets relaxXYZ="FFT" for all atoms\n' + '"force_criterion": 0.049, // Sets the threshold of the largest force\n' + '"relax_iter": 5 // Maximum number of optimization iterations\n' + '}' + } @@ -78,10 +91,11 @@ def convert(self,value,param,ctx): 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 + if 'SCF-uuid' in dict_from_file: + scf_wf=load_node(dict_from_file["SCF-uuid"]) + return scf_wf.outputs.last_calc.remote_folder + if 'retrieved-uuid' in dict_from_file: + return dict_from_file["retrieved-uuid"] except: return None @@ -104,14 +118,17 @@ def convert(self,value,param,ctx): try: with open(value,"r") as f: import json - wf_param=json.load(f) - except: + from json_minify import json_minify + + wf_param=json.loads(json_minify(f.read())) + except RuntimeError as error: + print(error) 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() + return wf_dict #Now load from aiida wf_dict = types.DataParamType(sub_classes=('aiida.data:core.dict',)).convert(value, param, ctx) diff --git a/aiida_fleur/parsers/fleur.py b/aiida_fleur/parsers/fleur.py index 1c4d1b126..cb7b6082c 100644 --- a/aiida_fleur/parsers/fleur.py +++ b/aiida_fleur/parsers/fleur.py @@ -280,7 +280,7 @@ def parse(self, **kwargs): except NotExistent: old_relax_text = '' else: - if relax_name in fleurinp.list_object_names(): + if relax_name in fleurinp.base.repository.list_object_names(): with fleurinp.open(relax_name, 'r') as rlx: old_relax_text = rlx.read() else: diff --git a/aiida_fleur/tools/plot/fleur.py b/aiida_fleur/tools/plot/fleur.py index 5d9687b57..afb64532f 100644 --- a/aiida_fleur/tools/plot/fleur.py +++ b/aiida_fleur/tools/plot/fleur.py @@ -62,11 +62,12 @@ def plot_fleur(*args, save=False, show_dict=False, show=True, backend=None, **kw for arg in args: if isinstance(arg, list): # try plot together + print("mn:",arg) p1 = plot_fleur_mn(arg, save=save, show=show, backend=backend, **kwargs) if len(p1) == 1: p1 = p1[0] else: - #print(arg) + print("sn:",arg) # plot alone p1 = plot_fleur_sn(arg, show_dict=show_dict, show=show, save=save, backend=backend, **kwargs) all_plots.append(p1) @@ -90,7 +91,7 @@ def plot_fleur_sn(node, show_dict=False, **kwargs): except KeyError as exc: raise ValueError('Sorry, I do not know how to visualize' f' this node in plot_fleur: {workflow_name} {node}') from exc - + print(plot_nodes) plot_result = plotf(*plot_nodes, **kwargs) return plot_result @@ -432,16 +433,44 @@ def plot_fleur_banddos_wc(param_node, return plot_res -def plot_fleur_relax_wc(node, labels=None, save=False, show=True, **kwargs): +def plot_fleur_relax_wc(node, labels=None, save=False, show=True, backend='bokeh', **kwargs): """ This methods takes an AiiDA output parameter node from a relaxation workchain and plots some information about atom movements and forces """ + from masci_tools.vis.common import convergence_plot + if labels is None: - labels = [] + labels = [n.pk for n in node] + + if isinstance(node, list): + if len(node) > 2: + return # TODO + else: + node = node[0] + + output_d = node.get_dict() + forces = output_d.get('force') + total_energies=output_d.get('energy') + iteration=range(len(forces)) + + add_args = {} + if backend == 'bokeh': + add_args['legend_label'] = labels + else: + add_args['plot_label'] = labels - raise NotImplementedError + plot_res = convergence_plot(iteration, + forces, + total_energies, + show=show, + save_plots=save, + backend=backend, + saveas_distance="force_convergence", + ylabel_distance="Largest force [Htr/bohr]", + **kwargs) + return plot_res def plot_fleur_corehole_wc(nodes, labels=None, save=False, show=True, **kwargs): """ @@ -618,6 +647,7 @@ def plot_fleur_cfcoeff_wc(param_node, 'FleurBandWorkChain': plot_fleur_band_wc, 'FleurBandDosWorkChain': plot_fleur_banddos_wc, 'FleurCFCoeffWorkChain': plot_fleur_cfcoeff_wc, + 'FleurRelaxWorkChain': plot_fleur_relax_wc, #'fleur_corehole_wc' : plot_fleur_corehole_wc, #support of < 1.5 release #'fleur_initial_cls_wc' : plot_fleur_initial_cls_wc, #support of < 1.5 release #'FleurInitialCLSWorkChain' : plot_fleur_initial_cls_wc,