Skip to content

Commit

Permalink
Migrate the app to AiiDA 2.x (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
yakutovicha authored Jun 23, 2023
1 parent 5e696fa commit 76c2f9c
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 64 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/di.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:

strategy:
matrix:
tag: [stable, latest]
tag: [edge, latest]
browser: [chrome, firefox]
fail-fast: false

Expand All @@ -26,7 +26,7 @@ jobs:
- name: Test app
uses: aiidalab/aiidalab-test-app-action@v2
with:
image: aiidalab/aiidalab-docker-stack:${{ matrix.tag }}
image: aiidalab/full-stack:${{ matrix.tag }}
browser: ${{ matrix.browser }}
name: empa-molecules

Expand Down
55 changes: 32 additions & 23 deletions empa_molecules/steps.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import aiidalab_widgets_base as awb
import ipywidgets as ipw
import traitlets
import traitlets as tr
from aiida import engine, orm, plugins

from .widgets import NodeViewWidget
Expand All @@ -12,7 +12,7 @@
class StructureSelectionStep(ipw.VBox, awb.WizardAppWidgetStep):
"""Integrated widget for the selection of structures from different sources."""

confirmed_structure = traitlets.Instance(StructureData, allow_none=True)
confirmed_structure = tr.Instance(StructureData, allow_none=True)

def __init__(self, description=None, **kwargs):
self.manager = awb.StructureManagerWidget(
Expand Down Expand Up @@ -57,7 +57,7 @@ def __init__(self, description=None, **kwargs):
**kwargs
)

@traitlets.default("state")
@tr.default("state")
def _default_state(self):
return self.State.READY

Expand All @@ -73,12 +73,12 @@ def _update_state(self, _=None):
else:
self.state = self.State.SUCCESS

@traitlets.observe("confirmed_structure")
@tr.observe("confirmed_structure")
def _observe_confirmed_structure(self, _):
with self.hold_trait_notifications():
self._update_state()

@traitlets.observe("state")
@tr.observe("state")
def _observe_state(self, change):
with self.hold_trait_notifications():
state = change["new"]
Expand All @@ -100,8 +100,8 @@ def reset(self): # unconfirm
class ConfigureGaussianCalculationStep(ipw.VBox, awb.WizardAppWidgetStep):
"""Widget to prepare gaussian inputs."""

inputs = traitlets.Dict()
input_structure = traitlets.Instance(StructureData, allow_none=True)
inputs = tr.Dict()
input_structure = tr.Instance(StructureData, allow_none=True)

def __init__(self, **kwargs):
self.dft_functional = ipw.Dropdown(
Expand Down Expand Up @@ -200,7 +200,7 @@ def confirm(self, _=None):
}
self.state = self.State.SUCCESS

@traitlets.default("state")
@tr.default("state")
def _default_state(self):
return self.State.INIT

Expand All @@ -210,23 +210,23 @@ class SubmitGaussianCalculationStep(ipw.VBox, awb.WizardAppWidgetStep):

# We use traitlets to connect the different steps.
# Note that we can use dlinked transformations, they do not need to be of the same type.
inputs = traitlets.Dict()
process = traitlets.Instance(orm.ProcessNode, allow_none=True)
inputs = tr.Dict()
value = tr.Unicode(allow_none=True)

def __init__(self, **kwargs):
self.configuration_label = ipw.HTML("Specify computational resources.")

# Codes.
self.gaussian_code_dropdown = awb.ComputationalResourcesWidget(
description="Gaussian", input_plugin="gaussian"
description="Gaussian", default_calc_job_plugin="gaussian"
)
self.gaussian_code_dropdown.observe(self._update_state, ["value"])
self.cubegen_code_dropdown = awb.ComputationalResourcesWidget(
description="Cubegen", input_plugin="gaussian.cubegen"
description="Cubegen", default_calc_job_plugin="gaussian.cubegen"
)
self.cubegen_code_dropdown.observe(self._update_state, ["value"])
self.formchk_code_dropdown = awb.ComputationalResourcesWidget(
description="Formchk", input_plugin="gaussian.formchk"
description="Formchk", default_calc_job_plugin="gaussian.formchk"
)
self.formchk_code_dropdown.observe(self._update_state, ["value"])

Expand Down Expand Up @@ -255,10 +255,14 @@ def __init__(self, **kwargs):
self.observe(self._update_state, ["inputs"])

self.btn_submit_mol_opt = awb.SubmitButtonWidget(
GaussianSpinWorkChain, input_dictionary_function=self.prepare_spin_calc
GaussianSpinWorkChain, inputs_generator=self.prepare_spin_calc
)
self.btn_submit_mol_opt.btn_submit.disabled = True
traitlets.dlink((self.btn_submit_mol_opt, "process"), (self, "process"))
tr.dlink(
(self.btn_submit_mol_opt, "process"),
(self, "value"),
transform=lambda x: x.uuid if x else None,
)

super().__init__(
[
Expand Down Expand Up @@ -316,9 +320,9 @@ def prepare_spin_calc(self):
builder[key] = value

# Codes.
builder.gaussian_code = self.gaussian_code_dropdown.value
builder.formchk_code = self.formchk_code_dropdown.value
builder.cubegen_code = self.cubegen_code_dropdown.value
builder.gaussian_code = orm.load_code(self.gaussian_code_dropdown.value)
builder.formchk_code = orm.load_code(self.formchk_code_dropdown.value)
builder.cubegen_code = orm.load_code(self.cubegen_code_dropdown.value)

# Resources.
builder.options = orm.Dict(
Expand All @@ -338,11 +342,12 @@ def prepare_spin_calc(self):


class ViewGaussianWorkChainStatusAndResultsStep(ipw.VBox, awb.WizardAppWidgetStep):
process = traitlets.Instance(orm.ProcessNode, allow_none=True)
value = tr.Unicode(allow_none=True)

def __init__(self, **kwargs):
self.process = None
self.process_tree = awb.ProcessNodesTreeWidget()
ipw.dlink((self, "process"), (self.process_tree, "process"))
ipw.dlink((self, "value"), (self.process_tree, "value"))

self.node_view = NodeViewWidget(layout={"width": "auto", "height": "auto"})
ipw.dlink(
Expand All @@ -360,7 +365,7 @@ def __init__(self, **kwargs):
self._update_state,
],
)
ipw.dlink((self, "process"), (self.process_monitor, "process"))
ipw.dlink((self, "value"), (self.process_monitor, "value"))

super().__init__([self.process_status], **kwargs)

Expand All @@ -369,7 +374,7 @@ def can_reset(self):
return self.state is not self.State.ACTIVE

def reset(self):
self.process = None
self.value = None

def _update_state(self):
if self.process is None:
Expand All @@ -390,6 +395,10 @@ def _update_state(self):
elif process_state is engine.ProcessState.FINISHED:
self.state = self.State.SUCCESS

@traitlets.observe("process")
@tr.observe("value")
def _observe_process(self, change):
if self.value is None:
self.process = None
else:
self.process = orm.load_node(self.value)
self._update_state()
2 changes: 1 addition & 1 deletion empa_molecules/templates/search.j2
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
{% for row in rows %}

<tr>
<td> <a target="_blank" href="./spin_calculation.ipynb?pk={{ row['pk'] }}"> {{ row['pk'] }}</a> </td>
<td> <a target="_blank" href="./spin_calculation.ipynb?uuid={{ row['uuid'] }}"> {{ row['pk'] }}</a> </td>
<td> {{ row['ctime'] }} </td>
<td> {{ row['formula'] }} </td>
<td>{{ row['description'] }} </td>
Expand Down
2 changes: 0 additions & 2 deletions empa_molecules/version.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#!/usr/bin/env python

"""This module contains project version information."""

__version__ = "v23.06.1"
24 changes: 12 additions & 12 deletions empa_molecules/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def _observe_node(self, change):

class WorkChainSelectorWidget(ipw.HBox):
# The PK of a 'aiida.workflows:quantumespresso.pw.bands' WorkChainNode.
value = traitlets.Int(allow_none=True)
value = traitlets.Unicode(allow_none=True)

# When this trait is set to a positive value, the work chains are automatically
# refreshed every `auto_refresh_interval` seconds.
Expand All @@ -59,7 +59,7 @@ def __init__(self, **kwargs):
ipw.dlink(
(self.work_chains_selector, "value"),
(self, "value"),
transform=lambda pk: None if pk is self._NO_PROCESS else pk,
transform=lambda uuid: None if uuid is self._NO_PROCESS else uuid,
)

self.refresh_work_chains_button = ipw.Button(description="Refresh")
Expand All @@ -82,6 +82,7 @@ def __init__(self, **kwargs):
@dataclass
class WorkChainData:
pk: int
uuid: str
ctime: str
state: str
formula: str
Expand All @@ -97,12 +98,12 @@ def find_work_chains(cls):
order_by={"ctime": "desc"},
)
projected = builder.get_projected(
query_set, projections=["pk", "ctime", "state"]
query_set, projections=["pk", "uuid", "ctime", "state"]
)

for process in projected[1:]:
pk = process[0]
formula = orm.load_node(pk).inputs.structure.get_formula()
uuid = process[0]
formula = orm.load_node(uuid).inputs.structure.get_formula()
yield cls.WorkChainData(formula=formula, *process)

@traitlets.default("busy")
Expand All @@ -127,7 +128,7 @@ def refresh_work_chains(self, _=None):
self.work_chains_selector.options = [
("New calculation...", self._NO_PROCESS)
] + [
(self.FMT_WORKCHAIN.format(wc=wc), wc.pk)
(self.FMT_WORKCHAIN.format(wc=wc), wc.uuid)
for wc in self.find_work_chains()
]

Expand Down Expand Up @@ -170,7 +171,7 @@ def _observe_value(self, change):

new = self._NO_PROCESS if change["new"] is None else change["new"]

if new not in {pk for _, pk in self.work_chains_selector.options}:
if new not in {uuid for _, uuid in self.work_chains_selector.options}:
self.refresh_work_chains()

self.work_chains_selector.value = new
Expand Down Expand Up @@ -379,8 +380,7 @@ def observe_inp_pks(_=None):
style=style,
)

# ---------
# date selection
# Date selection
dt_now = datetime.datetime.now()
dt_from = dt_now - datetime.timedelta(days=20)
self.date_start = ipw.Text(
Expand All @@ -398,7 +398,6 @@ def observe_inp_pks(_=None):
)

self.date_text = ipw.HTML(value="<p>Select the date range:</p>", width="150px")
# ---------

search_crit = [
ipw.HBox([inp_pks, pks_wrong_syntax]),
Expand Down Expand Up @@ -431,15 +430,15 @@ def search(self):
# html table header
column_names = {
"pk": "PK",
"uuid": "UUID",
"ctime": "Creation Time",
"formula": "Formula",
"description": "Descrition",
"energy": "Energy (eV)",
"thumbnail": "Thumbnail",
}

# query AiiDA database

# Query AiiDA database
qb = orm.QueryBuilder()
qb.append(self.workchain_class, filters=self.prepare_query_filters())
qb.order_by({self.workchain_class: {"ctime": "desc"}})
Expand All @@ -453,6 +452,7 @@ def search(self):

row = {
"pk": node.pk,
"uuid": node.uuid,
"ctime": node.ctime.strftime("%Y-%m-%d %H:%M"),
"formula": node.outputs.gs_structure.get_formula(),
"description": node.description,
Expand Down
2 changes: 1 addition & 1 deletion search.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.9"
"version": "3.9.13"
}
},
"nbformat": 4,
Expand Down
9 changes: 4 additions & 5 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,10 @@ project_urls =
[options]
packages = find:
install_requires =
aiida-core~=1.6
aiida-gaussian>=1.4.3
aiida-nanotech-empa>=0.5.3
aiidalab>=21.11.0
aiidalab-widgets-base~=1.3
aiida-core~=2.2
aiida-gaussian~=2.0
aiida-nanotech-empa>=1.0.0b3
aiidalab-widgets-base~=2.0
python_requires = >=3.7

[options.extras_require]
Expand Down
Loading

0 comments on commit 76c2f9c

Please sign in to comment.