diff --git a/aiidalab_qe/node_view.py b/aiidalab_qe/node_view.py index 1e4d40947..4b7f0ace0 100644 --- a/aiidalab_qe/node_view.py +++ b/aiidalab_qe/node_view.py @@ -6,16 +6,19 @@ """ import json + import ipywidgets as ipw import nglview from aiida.orm import Node from aiidalab_widgets_base import ProcessMonitor, register_viewer_widget from aiidalab_widgets_base.viewers import BandsDataViewer, StructureDataViewer from ase import Atoms +from importlib_resources import files from jinja2 import Environment from monty.json import MontyEncoder, jsanitize from traitlets import Instance, Int, List, Unicode, Union, default, observe, validate +from aiidalab_qe import static from aiidalab_qe.report import generate_report_dict @@ -167,12 +170,12 @@ def __init__(self, caption, body, *args, **kwargs): class SummaryView(ipw.VBox): def __init__(self, wc_node, **kwargs): + self.wc_node = wc_node def _fmt_yes_no(truthy): - return "yes" if truthy else "no" + return "Yes" if truthy else "No" - style = "background-color: #fafafa; line-height: normal" report = generate_report_dict(self.wc_node) env = Environment() @@ -181,50 +184,16 @@ def _fmt_yes_no(truthy): "fmt_yes_no": _fmt_yes_no, } ) - template = env.from_string( - """ -
-            
-                
-                    
-                    
-                
-                
-                    
-                    
-                
-                
-                    
-                    
-                
-                
-                    
-                    
-                
-                
-                    
-                    
-                
-                {% if bands_computed %}
-                
-                    
-                    
-                
-                {% endif %}
-                {% if pdos_computed %}
-                
-                    
-                    
-                
-                {% endif %}
-            
Structure geometry optimized:{{ relaxed | fmt_yes_no }}
Pseudopotential library:{{ pseudo_family }}
Energy cutoff (wave functions):{{ energy_cutoff_wfc }}
Energy cutoff (charge density):{{ energy_cutoff_rho }}
K-point mesh distance (SCF):{{ scf_kpoints_distance }}
K-point line distance (Bands){{ bands_kpoints_distance }}
K-point mesh distance (NSCF){{ nscf_kpoints_distance }}
-
- """ + template = files(static).joinpath("workflow_summary.jinja").read_text() + style = files(static).joinpath("style.css").read_text() + self.summary_view = ipw.HTML( + env.from_string(template).render(style=style, **report) + ) + super().__init__( + children=[self.summary_view], + layout=ipw.Layout(min_height="380px"), + **kwargs, ) - - self.summary_view = ipw.HTML(template.render(style=style, **report)) - - super().__init__(children=[self.summary_view], **kwargs) @register_viewer_widget("process.workflow.workchain.WorkChainNode.") diff --git a/aiidalab_qe/report.py b/aiidalab_qe/report.py index 7bbcde5cb..9f2624e09 100644 --- a/aiidalab_qe/report.py +++ b/aiidalab_qe/report.py @@ -1,11 +1,16 @@ -from aiida.common.exceptions import NotExistentAttributeError +from aiida.plugins import WorkflowFactory + +PwBaseWorkChain = WorkflowFactory("quantumespresso.pw.base") + FUNCTIONAL_LINK_MAP = { "PBE": "https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.77.3865", "PBEsol": "https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.100.136406", } -PSEUDO_LINK_MAP = {"SSSP": "https://www.materialscloud.org/discover/sssp/table"} +PSEUDO_LINK_MAP = { + "SSSP": "https://www.materialscloud.org/discover/sssp/table/efficiency" +} PROTOCOL_PSEUDO_MAP = { "fast": "SSSP/1.1/PBE/efficiency", @@ -13,10 +18,33 @@ "precise": "SSSP/1.1/PBE/precision", } +FUNCTIONAL_REPORT_MAP = { + "LDA": "local density approximation (LDA)", + "PBE": "generalized gradient approximation of Perdew-Burke-Ernzerhof (PBE)", + "PBEsol": "the revised generalized gradient approximation of Perdew-Burke-Ernzerhof (PBE) for solids", +} + def _generate_report_dict(qeapp_wc): builder_parameters = qeapp_wc.get_extra("builder_parameters", {}) + # Properties + run_relax = builder_parameters["relax_type"] != "none" + run_bands = builder_parameters["run_bands"] + run_pdos = builder_parameters["run_pdos"] + + yield "relaxed", run_relax + yield "relax_method", builder_parameters["relax_type"].upper() + yield "bands_computed", run_bands + yield "pdos_computed", run_pdos + + # Material settings + yield "material_magnetic", builder_parameters["spin_type"].title() + yield "electronic_type", builder_parameters["electronic_type"].title() + + # Calculation settings + yield "protocol", builder_parameters["protocol"] + try: pseudo_family = builder_parameters.get("pseudo_family", None) if pseudo_family is None: @@ -27,16 +55,19 @@ def _generate_report_dict(qeapp_wc): pseudo_family_list = pseudo_family.split("/") pseudo_library = pseudo_family_list[0] - yield "pseudo_library", pseudo_family_list[0] + yield "pseudo_library", pseudo_library if pseudo_library == "SSSP": yield "pseudo_version", pseudo_family_list[1] - yield "functional", pseudo_family_list[2] + functional = pseudo_family_list[2] + yield "functional", functional yield "pseudo_protocol", pseudo_family_list[3] else: raise NotImplementedError except (KeyError, AttributeError): pass + yield "pseudo_link", PSEUDO_LINK_MAP[pseudo_library] + yield "functional_link", FUNCTIONAL_LINK_MAP[functional] energy_cutoff_wfc = None energy_cutoff_rho = None @@ -44,37 +75,16 @@ def _generate_report_dict(qeapp_wc): bands_kpoints_distance = None nscf_kpoints_distance = None - for work_chain in qeapp_wc.called: - - if energy_cutoff_wfc is None or energy_cutoff_rho is None: - try: - parameters = work_chain.inputs.relax.base.pw.parameters.get_dict() - energy_cutoff_wfc = round(parameters["SYSTEM"]["ecutwfc"]) - energy_cutoff_rho = round(parameters["SYSTEM"]["ecutrho"]) - except NotExistentAttributeError: - pass - - if scf_kpoints_distance is None: - try: - scf_kpoints_distance = work_chain.inputs.base__kpoints_distance.value - except (AttributeError, NotExistentAttributeError): - pass - try: - scf_kpoints_distance = work_chain.inputs.scf__kpoints_distance.value - except (AttributeError, NotExistentAttributeError): - pass - - if bands_kpoints_distance is None: - try: - bands_kpoints_distance = work_chain.inputs.bands_kpoints_distance.value - except (AttributeError, NotExistentAttributeError): - pass - - if nscf_kpoints_distance is None: - try: - nscf_kpoints_distance = work_chain.inputs.nscf__kpoints_distance.value - except (AttributeError, NotExistentAttributeError): - pass + if run_relax: + pw_parameters = qeapp_wc.inputs.relax.base.pw.parameters.get_dict() + scf_kpoints_distance = qeapp_wc.inputs.relax.base.kpoints_distance.value + if run_bands: + pw_parameters = qeapp_wc.inputs.bands.scf.pw.parameters.get_dict() + scf_kpoints_distance = qeapp_wc.inputs.bands.scf.kpoints_distance.value + bands_kpoints_distance = qeapp_wc.inputs.bands.bands_kpoints_distance.value + + energy_cutoff_wfc = round(pw_parameters["SYSTEM"]["ecutwfc"]) + energy_cutoff_rho = round(pw_parameters["SYSTEM"]["ecutrho"]) yield "energy_cutoff_wfc", energy_cutoff_wfc yield "energy_cutoff_rho", energy_cutoff_rho @@ -82,23 +92,12 @@ def _generate_report_dict(qeapp_wc): yield "bands_kpoints_distance", bands_kpoints_distance yield "nscf_kpoints_distance", nscf_kpoints_distance - yield "relaxed", "relax__base__pw__parameters" in qeapp_wc.inputs - yield "bands_computed", "bands__bands__pw__parameters" in qeapp_wc.inputs - yield "pdos_computed", "pdos__dos__parameters" in qeapp_wc.inputs - def generate_report_dict(qeapp_wc): """Generate a dictionary for reporting the inputs for the `QeAppWorkChain`""" return dict(_generate_report_dict(qeapp_wc)) -FUNCTIONAL_REPORT_MAP = { - "LDA": "local density approximation (LDA)", - "PBE": "generalized gradient approximation of Perdew-Burke-Ernzerhof (PBE)", - "PBEsol": "the revised generalized gradient approximation of Perdew-Burke-Ernzerhof (PBE) for solids", -} - - def generate_report_text(report_dict): """Generate a text for reporting the inputs for the `QeAppWorkChain` diff --git a/aiidalab_qe/static/__init__.py b/aiidalab_qe/static/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/aiidalab_qe/static/style.css b/aiidalab_qe/static/style.css new file mode 100644 index 000000000..446bcf711 --- /dev/null +++ b/aiidalab_qe/static/style.css @@ -0,0 +1,42 @@ +:root { + --lab-blue: #2097F3; + --lab-background: #d3ecff; +} + +table { + font-family: arial, sans-serif; + border-collapse: collapse; + border:1px solid #bbbbbb; + width: 100%; +} +h3 { + margin-top: 0px; +} +td, tr { + border-left: 2px solid var(--lab-blue); + text-align: left; + padding: 8px; +} +tr { + background-color: var(--lab-background); +} +tr:nth-child(even) { + background-color: #ffffff; +} +td:nth-child(even) { + border-left: 0px; +} +/* Create two equal columns that floats next to each other */ +.column { + float: left; + width: 50%; + padding: 10px; + height: 300px; /* Should be removed. Only for demonstration */ +} + +/* Clear floats after the columns */ +.row:after { + content: ""; + display: table; + clear: both; +} diff --git a/aiidalab_qe/static/welcome.jinja b/aiidalab_qe/static/welcome.jinja new file mode 100644 index 000000000..de670d07e --- /dev/null +++ b/aiidalab_qe/static/welcome.jinja @@ -0,0 +1,27 @@ + + + + + + +
+

Welcome to the AiiDAlab Quantum ESPRESSO app! 👋

+ + The Quantum ESPRESSO app (or QEapp for short) is a graphical interface for calculating material properties based on density-functional theory (DFT). + Each of the properties is calculated by workflows powered by the AiiDA engine, and maintained in the Quantum ESPRESSO plugin for AiiDA. + +

The QEapp allows you to calculate material properties in a simple 3-step process:

+ +
    +
  1. 🔍 Step 1: Select the structure you want to run.
  2. +
  3. ⚙️ Step 2: Select the properties you are interested in.
  4. +
  5. 🚀 Step 3: Submit your workflow!
  6. +
+ +

New users can go straight to the first step and select their structure. Once you've already calculated some properties, you can select the corresponding workflow using the dropdown below.

+

Happy computing! 🎉

+
+ + diff --git a/aiidalab_qe/static/workflow_summary.jinja b/aiidalab_qe/static/workflow_summary.jinja new file mode 100644 index 000000000..44d033574 --- /dev/null +++ b/aiidalab_qe/static/workflow_summary.jinja @@ -0,0 +1,85 @@ + + + + + + +
+
+

Workchain settings

+ + + + + + {% if relaxed %} + + + + + {% endif %} + + + + + + + + + + + + + +
Structure geometry optimized{{ relaxed | fmt_yes_no }}
Optimization method{{ relax_method }}
Work chain protocol{{ protocol }}
Magnetism{{ material_magnetic }}
Electronic type{{ electronic_type }}
+
+
+

Advanced settings

+ + + + + + + + + + + + + + + + + + + + + + {% if bands_computed %} + + + + + {% endif %} + {% if pdos_computed %} + + + + + {% endif %} +
Functional + + {{ functional }} + +
Pseudopotential library + + {{ pseudo_library }} {{ pseudo_protocol }} v{{ pseudo_version }} + +
Energy cutoff (wave functions){{ energy_cutoff_wfc }} Ry
Energy cutoff (charge density){{ energy_cutoff_rho }} Ry
K-point mesh distance (SCF){{ scf_kpoints_distance }} Å-1
K-point line distance (Bands){{ bands_kpoints_distance }} Å-1
K-point mesh distance (NSCF){{ nscf_kpoints_distance }}
+
+
+ + \ No newline at end of file diff --git a/qe.ipynb b/qe.ipynb index 71a296ab8..fdb5526af 100644 --- a/qe.ipynb +++ b/qe.ipynb @@ -16,11 +16,63 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "cbcd2e00e9b04fb4a237039b76f4663d", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/opt/conda/lib/python3.7/site-packages/aiida/orm/computers.py:757: AiidaDeprecationWarning: this property is deprecated, use the `label` property instead\n", + " warnings.warn('this property is deprecated, use the `label` property instead', AiidaDeprecationWarning) # pylint: disable=no-member\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "8730b3d71edb443c8dbd45981c549e86", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value='\\n