Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate the app to AiiDA 2.x #22

Merged
merged 7 commits into from
Jun 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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