Skip to content

Commit

Permalink
Avoid storing the process node in step 4, opting for loading from uui…
Browse files Browse the repository at this point in the history
…d on the fly (thread safety)
  • Loading branch information
edan-bainglass committed Nov 16, 2024
1 parent e671688 commit cd4015c
Show file tree
Hide file tree
Showing 11 changed files with 42 additions and 41 deletions.
4 changes: 2 additions & 2 deletions src/aiidalab_qe/app/result/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ def _update_kill_button_layout(self):
return
process_node = self._model.fetch_process_node()
if (
not self._model.has_process
not process_node
or process_node.is_finished
or process_node.is_excepted
or self.state
Expand All @@ -236,7 +236,7 @@ def _update_status(self):

def _update_state(self):
process_node = self._model.fetch_process_node()
if not self._model.has_process:
if not process_node:
self.state = self.State.INIT
elif process_node.process_state in (
ProcessState.CREATED,
Expand Down
12 changes: 6 additions & 6 deletions src/aiidalab_qe/app/result/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ def update(self):
self._update_process_remote_folder_state()

def kill_process(self):
if self.has_process:
control.kill_processes([self.process_node])
if process_node := self.fetch_process_node():
control.kill_processes([process_node])

def clean_remote_data(self):
if not self.has_process:
if not (process_node := self.fetch_process_node()):
return
for called_descendant in self.process_node.called_descendants:
for called_descendant in process_node.called_descendants:
if isinstance(called_descendant, orm.CalcJobNode):
with contextlib.suppress(Exception):
called_descendant.outputs.remote_folder._clean()
Expand All @@ -35,10 +35,10 @@ def reset(self):
self.process_info = ""

def _update_process_remote_folder_state(self):
if not self.has_process:
if not (process_node := self.fetch_process_node()):
return
cleaned = []
for called_descendant in self.process_node.called_descendants:
for called_descendant in process_node.called_descendants:
if isinstance(called_descendant, orm.CalcJobNode):
with contextlib.suppress(Exception):
cleaned.append(called_descendant.outputs.remote_folder.is_empty)
Expand Down
3 changes: 2 additions & 1 deletion src/aiidalab_qe/app/result/summary/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ def _generate_report_parameters(self):
"""
from aiida.orm.utils.serialize import deserialize_unsafe

qeapp_wc = self.process_node
if not (qeapp_wc := self.fetch_process_node()):
return {"error": "WorkChain not found."}

ui_parameters = qeapp_wc.base.extras.get("ui_parameters", {})
if isinstance(ui_parameters, str):
Expand Down
23 changes: 14 additions & 9 deletions src/aiidalab_qe/app/result/viewer/viewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,18 @@ def render(self):
if self.rendered:
return

node = self._model.process_node
node = self._model.fetch_process_node()

self.title = ipw.HTML(f"""
<hr style="height:2px;background-color:#2097F3;">
<h4>
QE App Workflow (pk: {node.pk}) &mdash; {node.inputs.structure.get_formula()}
</h4>
""")
self.title = ipw.HTML()

title = "<hr style='height:2px;background-color:#2097F3;'>"
if node:
formula = node.inputs.structure.get_formula()
title += f"\n<h4>QE App Workflow (pk: {node.pk}) &mdash; {formula}</h4>"
else:
title += "\n<h4>QE App Workflow</h4>"

self.title.value = title

self.tabs = ipw.Tab(selected_index=None)

Expand All @@ -69,7 +73,7 @@ def render(self):

self._update_tabs()

if node.is_finished:
if node and node.is_finished:
self._add_workflow_output_widget()

def _update_tabs(self):
Expand All @@ -87,7 +91,8 @@ def _update_tabs(self):
self.summary.render()

def _add_workflow_output_widget(self):
self.summary.children += (WorkChainOutputs(self._model.process_node),)
process_node = self._model.fetch_process_node()
self.summary.children += (WorkChainOutputs(node=process_node),)

def _add_structure_panel(self):
structure_model = StructureResultsModel()
Expand Down
21 changes: 7 additions & 14 deletions src/aiidalab_qe/common/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,36 +49,29 @@ def _link_model(self, model: T):

class HasProcess(tl.HasTraits):
process_uuid = tl.Unicode(allow_none=True)
monitor_counter = tl.Int(0)

process_node = None

@property
def has_process(self):
return self.process_node is not None
monitor_counter = tl.Int(0) # used for continuous updates

@property
def inputs(self):
return self.process_node.inputs if self.has_process else []
process_node = self.fetch_process_node()
return process_node.inputs if process_node else []

@property
def properties(self):
return self.process_node.inputs.properties if self.has_process else []
process_node = self.fetch_process_node()
return process_node.inputs.properties if process_node else []

@property
def outputs(self):
return self.process_node.outputs if self.has_process else []
process_node = self.fetch_process_node()
return process_node.outputs if process_node else []

def fetch_process_node(self):
try:
return orm.load_node(self.process_uuid) if self.process_uuid else None
except NotExistent:
return None

@tl.observe("process_uuid")
def _on_process_uuid_change(self, _):
self.process_node = self.fetch_process_node()


class Confirmable(tl.HasTraits):
confirmed = tl.Bool(False)
Expand Down
4 changes: 2 additions & 2 deletions src/aiidalab_qe/common/panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ def _get_child_outputs(self, child="this"):
return AttributeDict({key: getattr(node.outputs, key) for key in node.outputs})

def _fetch_child_process_node(self, child="this") -> orm.ProcessNode | None:
if not self.process_node:
if not self.process_uuid:
return
uuid = getattr(self, f"_{child}_process_uuid")
label = getattr(self, f"_{child}_process_label")
Expand All @@ -240,7 +240,7 @@ def _fetch_child_process_node(self, child="this") -> orm.ProcessNode | None:
orm.QueryBuilder()
.append(
orm.WorkChainNode,
filters={"uuid": self.process_node.uuid},
filters={"uuid": self.process_uuid},
tag="root_process",
)
.append(
Expand Down
4 changes: 3 additions & 1 deletion src/aiidalab_qe/plugins/xas/result/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,16 @@ class XasResultsModel(ResultsModel):
_this_process_label = "XspectraCrystalWorkChain"

def update_spectrum_options(self):
if not (process_node := self.fetch_process_node()):
return
outputs = self._get_child_outputs()
(
self.final_spectra,
self.equivalent_sites_data,
) = export_xas_data(outputs)
xas_workchain = next(
node
for node in self.process_node.called
for node in process_node.called
if node.process_label == self._this_process_label
)
core_workchains = {
Expand Down
2 changes: 1 addition & 1 deletion tests/test_plugins_bands.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ def test_result(generate_qeapp_workchain):

workchain = generate_qeapp_workchain()
model = BandsResultsModel()
model.process_node = workchain.node
model.process_uuid = workchain.node.uuid
result = BandsResults(model=model)
result.render()

Expand Down
2 changes: 1 addition & 1 deletion tests/test_plugins_electronic_structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ def test_electronic_structure(generate_qeapp_workchain):

workchain = generate_qeapp_workchain()
model = ElectronicStructureResultsModel()
model.process_node = workchain.node
model.process_uuid = workchain.node.uuid
result = ElectronicStructureResults(model=model)
result.render()

Expand Down
2 changes: 1 addition & 1 deletion tests/test_plugins_pdos.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ def test_result(generate_qeapp_workchain):

workchain = generate_qeapp_workchain()
model = PdosResultsModel()
model.process_node = workchain.node
model.process_uuid = workchain.node.uuid
result = PdosResults(model=model)
result.render()

Expand Down
6 changes: 3 additions & 3 deletions tests/test_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def test_summary_report(data_regression, generate_qeapp_workchain):
"""Test the summary report can be properly generated."""
workchain = generate_qeapp_workchain()
model = WorkChainSummaryModel()
model.process_node = workchain.node
model.process_uuid = workchain.node.uuid
report_parameters = model._generate_report_parameters()
data_regression.check(report_parameters)

Expand All @@ -54,7 +54,7 @@ def test_summary_report_advanced_settings(data_regression, generate_qeapp_workch
spin_type="collinear", electronic_type="metal", initial_magnetic_moments=0.1
)
model = WorkChainSummaryModel()
model.process_node = workchain.node
model.process_uuid = workchain.node.uuid
report_parameters = model._generate_report_parameters()
assert report_parameters["initial_magnetic_moments"]["Si"] == 0.1

Expand All @@ -63,7 +63,7 @@ def test_summary_view(generate_qeapp_workchain):
"""Test the report html can be properly generated."""
workchain = generate_qeapp_workchain()
model = WorkChainSummaryModel()
model.process_node = workchain.node
model.process_uuid = workchain.node.uuid
report_html = model.generate_report_html()
parsed = BeautifulSoup(report_html, "html.parser")
# find the td with the text "Initial Magnetic Moments"
Expand Down

0 comments on commit cd4015c

Please sign in to comment.