-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- app folder with the graphical interface (i.e. widgets); - koopmansworkchain
- Loading branch information
1 parent
e5a9875
commit 7b6feca
Showing
6 changed files
with
321 additions
and
2 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,20 @@ | ||
# from aiidalab_qe.bands.result import Result | ||
from aiidalab_qe.common.panel import OutlinePanel | ||
|
||
from .result import Result | ||
from .setting import Setting | ||
from .workchain import workchain_and_builder | ||
|
||
|
||
class Outline(OutlinePanel): | ||
title = "Koopmans electronic band structure" | ||
help = """Koopmans DFPT workflow""" | ||
|
||
# for now, no codes are provided. | ||
|
||
property = { | ||
"outline": Outline, | ||
"setting": Setting, | ||
"result": Result, | ||
"workchain": workchain_and_builder, | ||
} |
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,103 @@ | ||
"""Koopmans results view widgets""" | ||
from aiidalab_qe.common.panel import ResultPanel | ||
|
||
from aiidalab_qe.common.bandpdoswidget import cmap, get_bands_labeling,BandPdosPlotly | ||
|
||
import numpy as np | ||
import json | ||
|
||
def replace_symbols_with_uppercase(data): | ||
symbols_mapping = { | ||
"$\Gamma$": "\u0393", | ||
"$\\Gamma$": "\u0393", | ||
"$\\Delta$": "\u0394", | ||
"$\\Lambda$": "\u039B", | ||
"$\\Sigma$": "\u03A3", | ||
"$\\Epsilon$": "\u0395", | ||
} | ||
|
||
for sublist in data: | ||
for i, element in enumerate(sublist): | ||
if element in symbols_mapping: | ||
sublist[i] = symbols_mapping[element] | ||
|
||
def get_bands_from_koopmans(koopmans_output): | ||
full_data = { | ||
"dft": None, | ||
"koopmans": None, | ||
} | ||
parameters = {} | ||
|
||
dft_bands = koopmans_output.interpolated_dft | ||
data = json.loads( | ||
dft_bands._exportcontent("json", comments=False)[0] | ||
) | ||
# The fermi energy from band calculation is not robust. | ||
data["fermi_energy"] = 0 | ||
data["pathlabels"] = get_bands_labeling(data) | ||
replace_symbols_with_uppercase(data["pathlabels"]) | ||
|
||
bands = dft_bands._get_bandplot_data(cartesian=True, prettify_format=None, join_symbol=None, get_segments=True) | ||
parameters["energy_range"] = { | ||
"ymin": np.min(bands["y"]) - 0.1, | ||
"ymax": np.max(bands["y"]) + 0.1, | ||
} | ||
data["band_type_idx"] = bands["band_type_idx"] | ||
data["x"] = bands["x"] | ||
data["y"] = bands["y"] | ||
full_data["dft"] = [data, parameters] | ||
|
||
koop_bands = koopmans_output.interpolated_koopmans | ||
data = json.loads( | ||
koop_bands._exportcontent("json", comments=False)[0] | ||
) | ||
# The fermi energy from band calculation is not robust. | ||
data["fermi_energy"] = 0 | ||
data["pathlabels"] = get_bands_labeling(data) | ||
replace_symbols_with_uppercase(data["pathlabels"]) | ||
|
||
bands = koop_bands._get_bandplot_data(cartesian=True, prettify_format=None, join_symbol=None, get_segments=True) | ||
parameters["energy_range"] = { | ||
"ymin": np.min(bands["y"]) - 0.1, | ||
"ymax": np.max(bands["y"]) + 0.1, | ||
} | ||
data["band_type_idx"] = bands["band_type_idx"] | ||
data["x"] = bands["x"] | ||
data["y"] = bands["y"] | ||
full_data["koopmans"] = [data, parameters] | ||
|
||
return full_data | ||
|
||
class Result(ResultPanel): | ||
"""Result panel for the bands calculation.""" | ||
|
||
title = "Koopmans bands" | ||
workchain_labels = ["koopmans"] | ||
|
||
def __init__(self, node=None, **kwargs): | ||
super().__init__(node=node, **kwargs) | ||
|
||
def _update_view(self): | ||
# Check if the workchain has the outputs | ||
try: | ||
koopmans_output = self.node.outputs.koopmans | ||
except AttributeError: | ||
koopmans_output = None | ||
|
||
bands = get_bands_from_koopmans(self.node.outputs.koopmans) | ||
|
||
fig = BandPdosPlotly(bands_data=bands["koopmans"][0]).bandspdosfigure | ||
fig_drop = BandPdosPlotly(bands_data=bands["dft"][0]).bandspdosfigure | ||
fig.add_scatter(y=fig_drop.data[0].y,x=fig_drop.data[0].x, name='DFT') | ||
del fig_drop | ||
|
||
trace_koopmans = fig.data[0] | ||
trace_koopmans.name = 'Koopmans' | ||
trace_koopmans.showlegend = True | ||
|
||
fig.layout.title.text = 'Interpolated Koopmans band structure' | ||
fig.layout.autosize = True | ||
|
||
self.children = [ | ||
fig, | ||
] |
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,88 @@ | ||
# -*- coding: utf-8 -*- | ||
"""Panel for Koopmans bands.""" | ||
|
||
import ipywidgets as ipw | ||
import json | ||
import pathlib | ||
import tempfile | ||
|
||
from aiidalab_qe.common.panel import Panel | ||
|
||
class Setting(Panel): | ||
title = "Koopmans bands" | ||
identifier = "koopmans" | ||
|
||
def __init__(self, **kwargs): | ||
self.settings_help = ipw.HTML( | ||
"""<div style="line-height: 140%; padding-top: 0px; padding-bottom: 5px"> | ||
The Koopmans band structure is computed using the AiiDA-enabled version of the <b> | ||
<a href="https://koopmans-functionals.org/en/latest/" | ||
target="_blank">Koopmans package</b></a> (E. Linscott et al., | ||
<a href="https://pubs.acs.org/doi/10.1021/acs.jctc.3c00652" | ||
target="_blank">J. Chem. Theory Comput. 2023 <b>19</b>, 20, 2023</a>) and the <a href="https://github.com/mikibonacci/aiida-koopmans" | ||
target="_blank">aiida-koopmans</b></a> plugin, co-developed by Miki Bonacci, Julian Geiger and Edward Linscott (Paul Scherrer Institut, Switzerland). | ||
<br> | ||
<br> | ||
For now, we allow one way to provide Koopmans settings, i.e. through the upload button below. You should pass | ||
the same file that is needed to run a standard Koopmans@AiiDA simulation, i.e. the codes should be set there, | ||
and not in step 3 of the app (this is just a temporary limitation). | ||
<br> | ||
<br> | ||
Only DFPT workflow is available. | ||
</div>""", | ||
layout=ipw.Layout(width="400"), | ||
) | ||
|
||
# Upload buttons | ||
self.upload_widget = ipw.FileUpload( | ||
description="Upload Koopmans json file", | ||
multiple=False, | ||
layout={"width": "initial"}, | ||
) | ||
self.upload_widget.observe(self._on_upload_json, "value") | ||
|
||
self.reset_uploads = ipw.Button( | ||
description="Discard uploaded file", | ||
icon="pencil", | ||
button_style="warning", | ||
disabled=False, | ||
layout=ipw.Layout(width="auto"), | ||
) | ||
self.reset_uploads.observe(self._on_reset_uploads_button_clicked, "value") | ||
|
||
self.children = [ | ||
self.settings_help, | ||
ipw.HBox(children=[self.upload_widget,self.reset_uploads]), | ||
] | ||
super().__init__(**kwargs) | ||
|
||
|
||
def _on_reset_uploads_button_clicked(self, change): | ||
self.upload_widget.value.clear() | ||
self.upload_widget._counter = 0 | ||
|
||
def _on_upload_json(self, change): | ||
# TO BE IMPLEMENTED | ||
if change["new"] != change["old"]: | ||
uploaded_filename = next(iter(self.upload_widget.value)) | ||
content = self.upload_widget.value[uploaded_filename]['content'] | ||
self.input_dictionary = json.loads(content.decode('utf-8')) # Decode content and parse JSON | ||
print("Uploaded JSON content:") | ||
print(self.input_dictionary) | ||
|
||
|
||
|
||
def get_panel_value(self): | ||
"""Return a dictionary with the input parameters for the plugin.""" | ||
return { | ||
"input_dictionary": self.input_dictionary, | ||
} | ||
|
||
def set_panel_value(self, input_dict): | ||
"""Load a dictionary with the input parameters for the plugin.""" | ||
self.input_dictionary = input_dict.get("input_dictionary", {}) | ||
|
||
def reset(self): | ||
"""Reset the panel to its default values.""" | ||
self.input_dictionary = {} |
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 @@ | ||
from aiida_koopmans.gui.koopmansworkchain import KoopmansWorkChain | ||
from aiida import orm | ||
|
||
def get_builder(codes, structure, parameters, **kwargs): | ||
"""Get a builder for the PwBandsWorkChain.""" | ||
kcw_wf = KoopmansWorkChain.get_builder() | ||
kcw_wf.input_dictionary = orm.Dict(parameters["koopmans"].pop("input_dictionary",{})) | ||
return kcw_wf | ||
|
||
|
||
workchain_and_builder = { | ||
"workchain": KoopmansWorkChain, | ||
#"exclude": ("structure", "relax"), | ||
"get_builder": get_builder, | ||
#"update_inputs": update_inputs, | ||
} |
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,86 @@ | ||
from aiida import orm | ||
from aiida.engine.processes.workchains.workchain import WorkChain | ||
from aiida.engine import calcfunction | ||
import numpy as np | ||
|
||
|
||
from koopmans.workflows import KoopmansDFPTWorkflow, SinglepointWorkflow | ||
|
||
class KoopmansWorkChain(WorkChain): | ||
"""WorkChain to run the koopmans package. Very simple, it is only needed for the GUI. | ||
Args: | ||
inputs (orm.Dict): inputs as obtained from loading the json file. | ||
""" | ||
@classmethod | ||
def define(cls, spec): | ||
"""Define the process specification.""" | ||
# yapf: disable | ||
super().define(spec) | ||
spec.input('input_dictionary', valid_type=orm.Dict, required=False,) | ||
spec.input('structure', valid_type=orm.StructureData, required=False, | ||
help="needed if we run in the GUI and we relax the structure before.") | ||
|
||
spec.outline( | ||
cls.setup, | ||
cls.run_process, | ||
cls.results, | ||
) | ||
|
||
spec.output("alphas", valid_type=orm.List, required= False) | ||
spec.output("interpolated_dft", valid_type=orm.BandsData, required=False) | ||
spec.output("interpolated_koopmans", valid_type=orm.BandsData, required=False) | ||
|
||
@classmethod | ||
def from_json(cls,): | ||
pass | ||
|
||
def setup(self): | ||
wf = SinglepointWorkflow._fromjsondct(self.inputs.input_dictionary.get_dict()) | ||
self.ctx.workflow = KoopmansDFPTWorkflow.fromparent(wf) | ||
return | ||
|
||
def run_process(self): | ||
# for now in the DFPT AiiDA wfl we just run_and_get_node, so no need to have the context. | ||
self.ctx.workflow._run() | ||
return | ||
|
||
def results(self): | ||
|
||
parent = orm.load_node(self.ctx.workflow.dft_wchains_pk[0]) | ||
bands_dft = merge_bands(parent.outputs.remote_folder, method="dft") | ||
bands_koopmans = merge_bands(parent.outputs.remote_folder, method="koopmans") | ||
|
||
self.out("interpolated_dft",bands_dft) | ||
self.out("interpolated_koopmans",bands_koopmans) | ||
|
||
return | ||
|
||
|
||
@calcfunction | ||
def merge_bands(remote_pw, method="dft"): | ||
# I want to have both dft and koopmans method and call this calcfunction once, | ||
# but for now it is fine to call it twice. | ||
# remote_pw is needed to access self (KoopmansWorkChain) in the calcfunction | ||
workchain = remote_pw.creator.caller.caller | ||
|
||
bands = {"dft":[],"koopmans":[]} | ||
method_loop = "dft" | ||
for job in workchain.called: | ||
if job.process_type == "aiida.calculations:koopmans": | ||
method_loop = "koopmans" | ||
if job.process_type == "aiida.workflows:wannier90_workflows.bands": | ||
bands[method_loop].append(job.outputs.band_structure) | ||
|
||
for method_merge in [method.value]: | ||
new_bands_array = bands[method_merge][0].get_bands() | ||
for i in range(1,len(bands[method_merge])): | ||
new_bands_array = np.concatenate((new_bands_array,bands[method_merge][i].get_bands()),axis=1) | ||
|
||
# Create a band structure object | ||
merged_bands = bands[method_merge][0].clone() | ||
merged_bands.set_bands(new_bands_array) | ||
# merged_bands.store() | ||
#bands[method].append(merged_bands) | ||
|
||
return merged_bands |