diff --git a/docs/source/_static/images/plugin_outline.png b/docs/source/_static/images/plugin_outline.png
index eebe430f6..559e53b90 100644
Binary files a/docs/source/_static/images/plugin_outline.png and b/docs/source/_static/images/plugin_outline.png differ
diff --git a/docs/source/_static/images/plugin_result.png b/docs/source/_static/images/plugin_result.png
index 919db9847..4a2e5ae51 100644
Binary files a/docs/source/_static/images/plugin_result.png and b/docs/source/_static/images/plugin_result.png differ
diff --git a/docs/source/_static/images/plugin_setting.png b/docs/source/_static/images/plugin_setting.png
index 7b9117324..20a3f252c 100644
Binary files a/docs/source/_static/images/plugin_setting.png and b/docs/source/_static/images/plugin_setting.png differ
diff --git a/docs/source/development/plugin.rst b/docs/source/development/plugin.rst
index 3c800a6db..4dcf5f157 100644
--- a/docs/source/development/plugin.rst
+++ b/docs/source/development/plugin.rst
@@ -6,104 +6,188 @@ Create your plugin
A QuantumESPRESSO app plugin will typically register new panels (setting, result), and workchain to extend the app's functionality.
-
Your First Plugin
================================
-Here is the an example plugin to print the formula of the input structure:
+Here is the an example plugin to calculate the equation of state (EOS) of a material.
+This plugin will register the following items:
+
+- Outline
+- Setting
+- Result
+- Workchain
+
+Let's go through each item in detail.
Outline
-----------------------
-A :class:`~aiidalab_qe.common.panel.Outline` widget can add an item in the properties section of the workflow panel.
-In this example, we add the ``Hello World`` outline.
+A :class:`~aiidalab_qe.common.panel.Outline` widget can add an item, as a checkbox, in the properties section of the workflow panel in the configuration step.
+You only need to set the title of the outline.
.. code-block:: python
from aiidalab_qe.common.panel import OutlinePanel
- class Outline(OutlinePanel):
- title = "Hello World"
+ class EosOutline(OutlinePanel):
+ title = "Equation of State (EOS)"
-The outline is a checkbox, as shown below.
+The ``EOS`` outline is shown below.
.. image:: ../_static/images/plugin_outline.png
:align: center
Setting
-----------------------
-A setting :class:`~aiidalab_qe.common.panel.Panel` widget will register a new panel in the configuration step, e.g. the ``Hello world`` panel.
+A setting :class:`~aiidalab_qe.common.panel.Panel` widget will register a new panel in the configuration step, e.g. the ``EOS setting`` panel.
.. image:: ../_static/images/plugin_setting.png
-In this class, you can add widgets to the GUI.
-The values of these widgets will be used in the WorkChain.
-you need to override the the following methods:
+In this panel, you can add widgets to set the input parameters for the plugin's WorkChain.
+For example, a ``FloatText`` widget to set the scale of the structure, and a ``IntText`` widget to set the number of points.
+Beside, you need to override the the following methods:
-- ``get_panel_value`` method to tell the app how to use the values from the widgets.
-- ``set_panel_value`` method to tell the app how to reload the panel values from the previous calculation.
-- ``reset`` method to reset the panel to the default values.
+- ``get_panel_value`` to tell the app how to get the values from the widgets.
+- ``set_panel_value`` to tell the app how to reload the panel values from the previous calculation.
+- ``reset`` to reset the panel to the default values.
.. code-block:: python
+ import ipywidgets as ipw
from aiidalab_qe.common.panel import Panel
class Setting(Panel):
- """"""
- title = "Hello world"
+
+ title = "EOS Settings"
def __init__(self, **kwargs):
- self.name = ipw.Text(value="", description="Your name:")
- self.children = [self.name]
+ self.settings_title = ipw.HTML(
+ """
+
Settings
"""
+ )
+ self.settings_help = ipw.HTML(
+ """
+ Please set the value of scale and number of points.
+
"""
+ )
+ self.scale = ipw.FloatText(
+ value=0.05,
+ description="Scale:",
+ disabled=False,
+ style={"description_width": "initial"},
+ )
+ self.npoint = ipw.IntText(
+ value=5,
+ description="Npoint:",
+ disabled=False,
+ style={"description_width": "initial"},
+ )
+
+ self.children=[
+ self.settings_title,
+ self.settings_help,
+ self.scale,
+ self.npoint,
+ ]
super().__init__(**kwargs)
def get_panel_value(self):
- """Get the panel value"""
- return {"name": Str(self.name.value)}
+ """Return a dictionary with the input parameters for the plugin."""
+ return {
+ "scale": self.scale.value,
+ "npoint": self.npoint.value,
+ }
def set_panel_value(self, input_dict):
- """Set the panel value"""
- self.name.value = input_dict.get("name", 1)
+ """Set a dictionary with the input parameters for the plugin."""
+ self.scale.value = input_dict.get("scale", 0.05)
+ self.npoint.value = input_dict.get("npoint", 5)
def reset(self):
- """Reset the panel"""
- self.name.value = ""
+ """Reset the input fields."""
+ self.scale.value = 0.05
+ self.npoint.value = 5
Result
-----------------------
-A :class:`~aiidalab_qe.common.panel.ResultPanel` will register a new panel in the results step. The ``Hello world`` panel is shown below:
+A :class:`~aiidalab_qe.common.panel.ResultPanel` widget will register a new panel in the results step.
+The ``EOS`` panel is shown below:
.. image:: ../_static/images/plugin_result.png
-In this class, you need to:
+In this widget, you need to:
- specify the `workchain_labels` to tell the app which workchains the panel is intended for.
- implement the ``_update_view`` method to tell the app how to show the results of the workchain.
-The output of the workchain will be stored in ``self.outputs.hello_world``.
-For example, the ``name`` and ``structure`` are the outputs of the ``HelloWorldWorkChain``.
+The output of the workchain will be stored in ``self.outputs.plugin_name``.
+For example, you can access the ``output_parameters`` output of the ``EOSWorkChain`` by ``self.outputs.eos.output_parameters``.
.. code-block:: python
from aiidalab_qe.common.panel import ResultPanel
+ import ipywidgets as ipw
+
class Result(ResultPanel):
- title = "Hello world"
- workchain_labels = ["hello_world"]
+ title = "EOS"
+ workchain_labels = ["eos"]
+
+ def __init__(self, node=None, **kwargs):
+ super().__init__(node=node, identifier="eos", **kwargs)
def _update_view(self):
- name = self.outputs.hello_world.name.value
- formula = self.outputs.hello_world.structure.get_formula()
+ import plotly.graph_objects as go
+ from ase.eos import EquationOfState
+ # init figure
+ g = go.FigureWidget(
+ layout=go.Layout(
+ title=dict(text="Equation of State"),
+ barmode="overlay",
+ )
+ )
+ g.layout.xaxis.title = "Volume (A^3)"
+ g.layout.yaxis.title = "Energy (eV)"
+ # get the output parameters
+ eos = self.outputs.eos.output_parameters.get_dict()
+ volumes = eos["volumes"]
+ energies = eos["energies"]
+ eos = EquationOfState(volumes, energies, eos="birchmurnaghan")
+ v0, e0, B = eos.fit()
+ plotdata = eos.getplotdata()
+ g.add_scatter(x=volumes, y=energies, mode="markers", marker=dict(size=10), name="DFT")
+ g.add_scatter(x=plotdata[4], y=plotdata[5], mode="lines", name="Birch-Murnaghan")
+ #
self.summary_view = ipw.HTML(
- f"""
Hello {name}
The input structure is: {formula} """.format()
+ """
+
Parameters
+
+
+ V0 |
+ {:1.3f} |
+
+
+ E0 |
+ {:1.3f} |
+
+
+ B |
+ {:1.3f} |
+
+
+
""".format(v0, e0, B)
)
- self.children = [ipw.HBox(children=[self.summary_view])]
-
+ self.children = [
+ ipw.HBox(
+ children=[self.summary_view, g],
+ layout=ipw.Layout(justify_content="space-between", margin="10px"),
+ ),
+ ]
WorkChain and Builder
-----------------------
-you need to implement a ``get_builder`` function to tell QuantumESPRESSO app how to use the input parameters from the GUI.
+You need to implement a ``get_builder`` function to tell QuantumESPRESSO app how to use the input parameters from the GUI.
The `parameters` passed to the `get_builder` function has the following structure:
@@ -130,76 +214,85 @@ The `parameters` passed to the `get_builder` function has the following structur
},
"bands": {"kpath_2d": "hexagonal"},
"pdos": {...},
- "hello_world": {...},
+ "eos": {...},
"plugin_1": {...},
}
-you need to decide which parameters are needed for the workchain, and how to use them.
-For example, the ``HelloWorldWorkChain`` needs the ``name`` parameter, which is defined in the ``Setting`` panel.
-The ``get_builder`` function will return a ``builder`` for the ``HelloWorldWorkChain``.
-The ``builder`` will be used to submit the workchain.
-
-
+You need to decide which parameters are needed for the workchain, and how to use them.
+For example, the ``EOSWorkChain`` needs the ``pw`` code and the ``advanced`` setting for the ``pw`` code.
+It also needs the ``parameters`` which are defined in the ``EOS Setting`` panel.
+The ``get_builder`` function will return a ``builder`` for the ``EOSWorkChain``, which will be attached to the main builder of the ``QeAppWorkchain``.
.. code-block:: python
- def get_builder(codes, structure, parameters):
- """Get the workchain specific parameters
- """
- parameters = parameters.get("hello_world", {})
- builder = HelloWorldWorkChain.get_builder_from_protocol(
- codes=codes,
- structure=structure,
- parameters=parameters,
- )
+ def get_builder(codes, structure, parameters, **kwargs):
+ protocol = parameters["workchain"].pop('protocol', "fast")
+ pw_code = codes.get("pw")
+ overrides = {
+ "pw": parameters["advanced"],
+ }
+ builder = EOSWorkChain.get_builder_from_protocol(
+ pw_code=pw_code,
+ structure=structure,
+ protocol=protocol,
+ electronic_type=ElectronicType(parameters["workchain"] ["electronic_type"]),
+ spin_type=SpinType(parameters["workchain"]["spin_type"]),
+ initial_magnetic_moments=parameters["advanced"] ["initial_magnetic_moments"],
+ parameters=parameters["eos"],
+ overrides=overrides,
+ **kwargs,
+ )
return builder
-Then register the workchain and builder in the `workchain_and_builder` dict, so that the QuantumESPRESSO app can load them.
+Then add the workchain and builder into the `workchain_and_builder` dict, so that the QuantumESPRESSO app can load them.
.. code-block:: python
# register the workchain and builder
workchain_and_builder = {
- "workchain": HelloWorldWorkChain,
+ "workchain": EOSWorkChain,
"get_builder": get_builder,
}
Entry point
-----------------------
-Finally, you need to register the entry point of the plugin. Here is the entry point for this plugin.
+Finally, you need to register the entry point of the plugin.
+Here is the entry point for this plugin.
.. code-block:: python
# this is the entry point of the plugin
- hello_world ={
- "outline": Outline,
- "setting": Setting,
- "workchain": workchain_and_builder,
- "result": Result,
+ eos ={
+ "outline": EosOutline,
+ "setting": Setting,
+ "workchain": workchain_and_builder,
+ "result": Result,
}
+
Install the plugin
-----------------------
To install the plugin, you can creating a new package or adding it to the `aiidalab_qe.plugins` folder.
-you need to add the path of ``hello_world`` to ``entry_points`` inside the setup file.
+You need to add the path of ``eos`` to ``entry_points`` inside the setup file.
.. code-block:: python
entry_points={
"aiidalab_qe.properties": [
- "hello_world = aiidalab_qe_hello_world:hello_world",
+ "eos = aiidalab_qe_plugin_demos.eos:eos",
],
},
-**Bringing It All Together**, You can find all the code above in this github repository: https://github.com/superstar54/aiidalab-qe-hello-world
+**Bringing It All Together**, you can find all the code above in the `aiidalab-qe-plugin-demos`_ repository.
+
Advanced usage
================================
A plugin does not need to register all the items (settings, workchain, results).
-The panel in each step is pluggable, which means you could only register you item in a plugin.
-For example, you can only add a new `Result` panel without doing any property calculation.
-The built-in `electronic_structure` plugin only has a result panel, which needs the result from both `pdos`` and `bands`` plugins.
-This is set by the `workchain_labels` attribute.
+The panel in each step is pluggable, which means you could only register one item in a plugin.
+For example, the built-in `electronic_structure` plugin only has a result panel without doing any property calculation.
+The pupose of the `electronic_structure` plugin is to show the results of the `bands`` and `pdos`` plugins together.
+In order to do this, it set add both ``pdos`` and ``bands`` to the ``workchain_labels``.
.. code-block:: python
@@ -207,7 +300,7 @@ This is set by the `workchain_labels` attribute.
title = "Electronic Structure"
workchain_labels = ["bands", "pdos"]
-Here is the entry point for this plugin.
+Here is the entry point for the `electronic_structure` plugin.
.. code-block:: python
@@ -219,26 +312,60 @@ Here is the entry point for this plugin.
Structure importer and editor
------------------------------
-The plugin API also allows the user to add a new structure importer and editor:
+The app also allows the user to add new panels in the structure selection step:
+
+- structure ``importer``: specific for particular structures, e.g. surface, adsorbate.
+- structure ``editor``: to edit a structure for the plugin, e.g. edit tags, and cut surface.
+
+You can find an example plugin, ``surface``, in the `aiidalab-qe-plugin-demos`_ repository.
+It adds a new structure importer and editor to generate the surface structure.
+Here is the entry point for the `surface` plugin.
+
+.. code-block:: python
+
+ from .importer import SurfaceImporter
+ from .editor import SurfaceEditor
-- add structure `importer` specific for particular structures, e.g. surface, adsorbate.
-- add a new `editor` to edit a structure for the plugin, e.g. edit tags, and cut surface.
-Here is the example for such plugin.
+ surface ={
+ "importer": SurfaceImporter,
+ "editor": SurfaceEditor,
+ }
+
+Computational Resources
+------------------------------
+You can also add new code in the plugin.
+Here is the example of the built-in `pdos` plugins with codes `dos.x` and `projwfc.x`:
.. code-block:: python
- from .structure_importer import StructureImporter
- from .structure_editor import StructureEditor
+ from aiidalab_widgets_base import ComputationalResourcesWidget
+
+ dos_code = ComputationalResourcesWidget(
+ description="dos.x",
+ default_calc_job_plugin="quantumespresso.dos",
+ )
+
+ projwfc_code = ComputationalResourcesWidget(
+ description="projwfc.x",
+ default_calc_job_plugin="quantumespresso.projwfc",
+ )
- my_plugin ={
- "importer": StructureImporter,
- "editor": StructureEditor,
+ pdos = {
+ "outline": PdosOutline,
+ "code": {"dos": dos_code, "projwfc": projwfc_code},
+ "setting": Setting,
+ "result": Result,
+ "workchain": workchain_and_builder,
}
+For the moment, the app does not support set up the newly added codes automatically.
+Thus, the user needs to set up the codes manually.
Further Reading
================================
-QuantumESPRESSO app comes with built-in plugins, which can be found in the ``aiidalab_qe.plugins`` folder.
+QuantumESPRESSO app comes with several built-in plugins, which can be found in the ``aiidalab_qe.plugins`` folder.
You can also use them as a start point to create your own plugins.
+
+.. _aiidalab-qe-plugin-demos: https://github.com/aiidalab/aiidalab-qe-plugin-demos