diff --git a/setup.cfg b/setup.cfg index d9e6a107d..2a1909ac9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -58,6 +58,7 @@ aiidalab_qe.properties = xps = aiidalab_qe.plugins.xps:xps electronic_structure = aiidalab_qe.plugins.electronic_structure:electronic_structure xas = aiidalab_qe.plugins.xas:xas + hp = aiidalab_qe.plugins.hp:hp [aiidalab] title = Quantum ESPRESSO diff --git a/src/aiidalab_qe/common/setup_codes.py b/src/aiidalab_qe/common/setup_codes.py index bfa95a8f3..6092c6bac 100644 --- a/src/aiidalab_qe/common/setup_codes.py +++ b/src/aiidalab_qe/common/setup_codes.py @@ -41,6 +41,7 @@ "pw2wannier90", "q2r", "xspectra", + "hp", ) diff --git a/src/aiidalab_qe/plugins/hp/__init__.py b/src/aiidalab_qe/plugins/hp/__init__.py new file mode 100644 index 000000000..15f015864 --- /dev/null +++ b/src/aiidalab_qe/plugins/hp/__init__.py @@ -0,0 +1,24 @@ +from aiidalab_qe.common.panel import OutlinePanel +from aiidalab_widgets_base import ComputationalResourcesWidget + +from .result import Result +from .workchain import workchain_and_builder + + +class HpOutline(OutlinePanel): + title = "HP calculation" + help = """""" + + +hp_code = ComputationalResourcesWidget( + description="hp.x", + default_calc_job_plugin="quantumespresso.hp", +) + + +hp = { + "outline": HpOutline, + "code": {"hp": hp_code}, + "result": Result, + "workchain": workchain_and_builder, +} diff --git a/src/aiidalab_qe/plugins/hp/result.py b/src/aiidalab_qe/plugins/hp/result.py new file mode 100644 index 000000000..fdf79342f --- /dev/null +++ b/src/aiidalab_qe/plugins/hp/result.py @@ -0,0 +1,67 @@ +import ipywidgets as ipw +from aiidalab_qe.common.panel import ResultPanel + + +class Result(ResultPanel): + title = "Bader Charge" + workchain_labels = ["bader"] + + def __init__(self, node=None, **kwargs): + super().__init__(node=node, **kwargs) + self.summary_view = ipw.HTML() + + def _update_view(self): + structure = self.node.inputs.bader.structure + bader_charge = self.outputs.bader.bader.bader_charge.get_array("charge") + self._generate_table(structure, bader_charge) + self.children = [ + ipw.HBox( + children=[self.summary_view], + layout=ipw.Layout(justify_content="space-between", margin="10px"), + ), + ] + + def _generate_table(self, structure, bader_charge): + # get index and element form AiiDA StructureData + site_index = [site.kind_name for site in structure.sites] + + # Start of the HTML string for the table + html_str = """
+

Bader Charge Table

+ + + + + + + """ + + # Add rows to the table based on the bader_charge + for i in range(len(site_index)): + html_str += f""" + + + + + """ + + # Close the table and div tags + html_str += """ +
Site IndexElementBader Charge
{i}{site_index[i]}{bader_charge[i]:1.3f}
+
""" + self.summary_view = ipw.HTML(html_str) diff --git a/src/aiidalab_qe/plugins/hp/workchain.py b/src/aiidalab_qe/plugins/hp/workchain.py new file mode 100644 index 000000000..5fc1f494e --- /dev/null +++ b/src/aiidalab_qe/plugins/hp/workchain.py @@ -0,0 +1,68 @@ +from aiida_quantumespresso_hp.workflows.hp.main import HpWorkChain + + +def check_codes(pw_code, hp_code): + """Check that the codes are installed on the same computer.""" + if ( + not any( + [ + pw_code is None, + hp_code is None, + ] + ) + and len( + set( + ( + pw_code.computer.pk, + hp_code.computer.pk, + ) + ) + ) + != 1 + ): + raise ValueError( + "All selected codes must be installed on the same computer. This is because the " + "HP calculations rely on large files that are not retrieved by AiiDA." + ) + + +def get_builder(codes, structure, parameters, **kwargs): + pw_code = codes.get("pw") + hp_code = codes.get("hp") + check_codes(pw_code, hp_code) + protocol = parameters["workchain"]["protocol"] + + overrides = { + "parallelize_atoms": True, + "parallelize_qpoints": True, + "hp": { + "hubbard_structure": structure, + "metadata": { + "options": { + "resources": { + "num_machines": 1, + "num_mpiprocs_per_machine": 2, + }, + } + }, + }, + "qpoints_distance": 1000, # to get few q points + } + if hp_code is not None: + builder = HpWorkChain.get_builder_from_protocol( + code=hp_code, + protocol=protocol, + overrides=overrides, + **kwargs, + ) + else: + raise ValueError("The hp_code and bader_code are required.") + return builder + + +workchain_and_builder = { + "workchain": HpWorkChain, + "exclude": ("clean_workdir", "structure", "relax"), + "get_builder": get_builder, + "input_from_ctx": {"hp.parent_folder": "scf_folder"}, +} diff --git a/src/aiidalab_qe/workflows/__init__.py b/src/aiidalab_qe/workflows/__init__.py index ac292cb4a..aa6f32f25 100644 --- a/src/aiidalab_qe/workflows/__init__.py +++ b/src/aiidalab_qe/workflows/__init__.py @@ -204,7 +204,8 @@ def inspect_relax(self): f"PwRelaxWorkChain failed with exit status {workchain.exit_status}" ) return self.exit_codes.ERROR_SUB_PROCESS_FAILED_RELAX - + # save the remote folder so that the next workchain can use it + self.ctx.scf_folder = workchain.outputs.remote_folder if "output_structure" in workchain.outputs: self.ctx.current_structure = workchain.outputs.output_structure self.ctx.current_number_of_bands = ( @@ -228,6 +229,9 @@ def run_plugin(self): ) inputs.metadata.call_link_label = name inputs.structure = self.ctx.current_structure + # set the scf parent folder and other inputs from the context + for key, value in entry_point.get("input_from_ctx", {}).items(): + setattr(inputs, key, self.ctx[value]) inputs = prepare_process_inputs(plugin_workchain, inputs) running = self.submit(plugin_workchain, **inputs) self.report(f"launching plugin {name} <{running.pk}>")