-
Notifications
You must be signed in to change notification settings - Fork 17
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
Enhanced computational resource widget with resource setup #566
Merged
Merged
Changes from 23 commits
Commits
Show all changes
28 commits
Select commit
Hold shift + click to select a range
9d952b9
add new ComputationalResourcesWidget with nodes and cpus
superstar54 b7d1752
use new widget in submission
superstar54 85d888c
update test
superstar54 4664bcf
fix test for pdos
superstar54 63a6a35
fix code not exist when setting
superstar54 4edf38b
Merge branch 'feature/new_computational_resource_widget' of https://g…
superstar54 38f5db2
backward compatibility for v2023.11
superstar54 4ec0817
change name to QEAppComputationalResourcesWidget, and add blocker if …
superstar54 97c48ec
only add blocker for selected codes
superstar54 cb1ac30
update doc, ingore hidden codes
superstar54 e180ec4
Merge branch 'main' into feature/new_computational_resource_widget
superstar54 3e429a9
use composite method, add override for parallelization
superstar54 80727e5
fix test
superstar54 e9314c2
Merge branch 'main' into feature/new_computational_resource_widget
superstar54 c7f1f59
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 2dbfc3d
Merge branch 'main' into feature/new_computational_resource_widget
superstar54 5ee2b24
Merge branch 'main' into feature/new_computational_resource_widget
superstar54 a25abe4
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] f8d6a85
delete empty resource.py file
superstar54 029e957
update XAS plugin
superstar54 56ab9bb
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] ad0c844
add setup resource detail
superstar54 4a0cb83
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 14e6f7c
rename PwCodeResourceSetupWidget, add test
superstar54 9a70a27
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 724982b
Merge branch 'main' into feature/new_computational_resource_widget
superstar54 5720107
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] e81ece7
Merge branch 'main' into feature/new_computational_resource_widget
AndresOrtegaGuerrero File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,24 +6,21 @@ | |
|
||
from __future__ import annotations | ||
|
||
import os | ||
|
||
import ipywidgets as ipw | ||
import traitlets as tl | ||
from aiida import orm | ||
from aiida.common import NotExistent | ||
from aiida.engine import ProcessBuilderNamespace, submit | ||
from aiidalab_widgets_base import ComputationalResourcesWidget, WizardAppWidgetStep | ||
from aiidalab_widgets_base import WizardAppWidgetStep | ||
from IPython.display import display | ||
|
||
from aiidalab_qe.app.parameters import DEFAULT_PARAMETERS | ||
from aiidalab_qe.app.utils import get_entry_items | ||
from aiidalab_qe.common.setup_codes import QESetupWidget | ||
from aiidalab_qe.common.setup_pseudos import PseudosInstallWidget | ||
from aiidalab_qe.common.widgets import PWscfWidget, QEAppComputationalResourcesWidget | ||
from aiidalab_qe.workflows import QeAppWorkChain | ||
|
||
from .resource import ParallelizationSettings, ResourceSelectionWidget | ||
|
||
|
||
class SubmitQeAppWorkChainStep(ipw.VBox, WizardAppWidgetStep): | ||
"""Step for submission of a bands workchain.""" | ||
|
@@ -62,17 +59,11 @@ def __init__(self, qe_auto_setup=True, **kwargs): | |
self.message_area = ipw.Output() | ||
self._submission_blocker_messages = ipw.HTML() | ||
|
||
self.pw_code = ComputationalResourcesWidget( | ||
self.pw_code = PWscfWidget( | ||
description="pw.x:", default_calc_job_plugin="quantumespresso.pw" | ||
) | ||
|
||
self.resources_config = ResourceSelectionWidget() | ||
self.parallelization = ParallelizationSettings() | ||
|
||
self.set_resource_defaults() | ||
|
||
self.pw_code.observe(self._update_state, "value") | ||
self.pw_code.observe(self._update_resources, "value") | ||
|
||
# add plugin's entry points | ||
self.codes = {"pw": self.pw_code} | ||
|
@@ -87,8 +78,6 @@ def __init__(self, qe_auto_setup=True, **kwargs): | |
self.codes[name] = code | ||
code.observe(self._update_state, "value") | ||
self.code_children.append(self.codes[name]) | ||
# set default codes | ||
self.set_selected_codes(DEFAULT_PARAMETERS["codes"]) | ||
# | ||
self.submit_button = ipw.Button( | ||
description="Submit", | ||
|
@@ -124,15 +113,15 @@ def __init__(self, qe_auto_setup=True, **kwargs): | |
super().__init__( | ||
children=[ | ||
*self.code_children, | ||
self.resources_config, | ||
self.parallelization, | ||
self.message_area, | ||
self.sssp_installation_status, | ||
self.qe_setup_status, | ||
self._submission_blocker_messages, | ||
self.submit_button, | ||
] | ||
) | ||
# set default codes | ||
self.set_selected_codes(DEFAULT_PARAMETERS["codes"]) | ||
|
||
@tl.observe("internal_submission_blockers", "external_submission_blockers") | ||
def _observe_submission_blockers(self, _change): | ||
|
@@ -166,6 +155,16 @@ def _identify_submission_blockers(self): | |
if not self.sssp_installation_status.installed: | ||
yield "The SSSP library is not installed." | ||
|
||
# check if the QEAppComputationalResourcesWidget is used | ||
for name, code in self.codes.items(): | ||
# skip if the code is not displayed, convenient for the plugin developer | ||
if code.layout.display == "none": | ||
continue | ||
if not isinstance(code, QEAppComputationalResourcesWidget): | ||
yield ( | ||
f"Error: hi, plugin developer, please use the QEAppComputationalResourcesWidget from aiidalab_qe.common.widgets for code {name}." | ||
) | ||
|
||
def _update_state(self, _=None): | ||
# If the previous step has failed, this should fail as well. | ||
if self.previous_step_state is self.State.FAIL: | ||
|
@@ -198,14 +197,12 @@ def _toggle_install_widgets(self, change): | |
|
||
def _auto_select_code(self, change): | ||
if change["new"] and not change["old"]: | ||
for name, code_widget in self.codes.items(): | ||
for name, code in self.codes.items(): | ||
if not DEFAULT_PARAMETERS["codes"].get(name): | ||
continue | ||
try: | ||
code_widget.refresh() | ||
code_widget.value = orm.load_code( | ||
DEFAULT_PARAMETERS["codes"][name] | ||
).uuid | ||
code.code_selection.refresh() | ||
code.value = orm.load_code(DEFAULT_PARAMETERS["codes"][name]).uuid | ||
except NotExistent: | ||
pass | ||
|
||
|
@@ -224,55 +221,6 @@ def _show_alert_message(self, message, alert_class="info"): | |
) | ||
) | ||
|
||
def _update_resources(self, change): | ||
if change["new"] and ( | ||
change["old"] is None | ||
or orm.load_code(change["new"]).computer.pk | ||
!= orm.load_code(change["old"]).computer.pk | ||
): | ||
self.set_resource_defaults(orm.load_code(change["new"]).computer) | ||
|
||
def get_resources(self): | ||
resources = { | ||
"num_machines": self.resources_config.num_nodes.value, | ||
"num_mpiprocs_per_machine": self.resources_config.num_cpus.value, | ||
"npools": self.parallelization.npools.value, | ||
} | ||
return resources | ||
|
||
def set_resources(self, resources): | ||
self.resources_config.num_nodes.value = resources["num_machines"] | ||
self.resources_config.num_cpus.value = resources["num_mpiprocs_per_machine"] | ||
self.parallelization.npools.value = resources["npools"] | ||
|
||
def set_resource_defaults(self, computer=None): | ||
if computer is None or computer.hostname == "localhost": | ||
self.resources_config.num_nodes.disabled = True | ||
self.resources_config.num_nodes.value = 1 | ||
self.resources_config.num_cpus.max = os.cpu_count() | ||
self.resources_config.num_cpus.value = 1 | ||
self.resources_config.num_cpus.description = "CPUs" | ||
self.parallelization.npools.value = 1 | ||
else: | ||
default_mpiprocs = computer.get_default_mpiprocs_per_machine() | ||
self.resources_config.num_nodes.disabled = False | ||
self.resources_config.num_cpus.max = default_mpiprocs | ||
self.resources_config.num_cpus.value = default_mpiprocs | ||
self.resources_config.num_cpus.description = "CPUs/node" | ||
self.parallelization.npools.value = self._get_default_parallelization() | ||
|
||
self._check_resources() | ||
|
||
def _get_default_parallelization(self): | ||
"""A _very_ rudimentary approach for obtaining a minimal npools setting.""" | ||
num_mpiprocs = ( | ||
self.resources_config.num_nodes.value * self.resources_config.num_cpus.value | ||
) | ||
|
||
for i in range(1, num_mpiprocs + 1): | ||
if num_mpiprocs % i == 0 and num_mpiprocs // i < self.MAX_MPI_PER_POOL: | ||
return i | ||
|
||
def _check_resources(self): | ||
"""Check whether the currently selected resources will be sufficient and warn if not.""" | ||
if not self.pw_code.value: | ||
|
@@ -331,10 +279,14 @@ def get_selected_codes(self): | |
|
||
return: A dict with the code names as keys and the code UUIDs as values. | ||
""" | ||
codes = {key: code.value for key, code in self.codes.items()} | ||
codes = { | ||
key: code.parameters | ||
for key, code in self.codes.items() | ||
if code.layout.display != "none" | ||
} | ||
return codes | ||
|
||
def set_selected_codes(self, codes): | ||
def set_selected_codes(self, code_data): | ||
"""Set the inputs in the GUI based on a set of codes.""" | ||
|
||
# Codes | ||
|
@@ -347,12 +299,20 @@ def _get_code_uuid(code): | |
|
||
with self.hold_trait_notifications(): | ||
for name, code in self.codes.items(): | ||
if name not in code_data: | ||
continue | ||
# check if the code is installed and usable | ||
# note: if code is imported from another user, it is not usable and thus will not be | ||
# treated as an option in the ComputationalResourcesWidget. | ||
code_options = [o[1] for o in self.pw_code.code_select_dropdown.options] | ||
if _get_code_uuid(codes.get(name)) in code_options: | ||
code.value = _get_code_uuid(codes.get(name)) | ||
code_options = [ | ||
o[1] for o in code.code_selection.code_select_dropdown.options | ||
] | ||
if _get_code_uuid(code_data.get(name)["code"]) in code_options: | ||
# get code uuid from code label in case of using DEFAULT_PARAMETERS | ||
code_data.get(name)["code"] = _get_code_uuid( | ||
code_data.get(name)["code"] | ||
) | ||
code.parameters = code_data.get(name) | ||
|
||
def update_codes_display(self): | ||
"""Hide code if no related property is selected.""" | ||
|
@@ -408,53 +368,50 @@ def _create_builder(self) -> ProcessBuilderNamespace: | |
from copy import deepcopy | ||
|
||
self.ui_parameters = deepcopy(self.input_parameters) | ||
self.ui_parameters["resources"] = self.get_resources() | ||
# add codes and resource info into ui_parameters | ||
self.ui_parameters.update(self.get_submission_parameters()) | ||
submission_parameters = self.get_submission_parameters() | ||
self.ui_parameters.update(submission_parameters) | ||
builder = QeAppWorkChain.get_builder_from_protocol( | ||
structure=self.input_structure, | ||
parameters=deepcopy(self.ui_parameters), | ||
) | ||
|
||
self._update_builder(builder, self.MAX_MPI_PER_POOL) | ||
self._update_builder(builder, submission_parameters["codes"]) | ||
|
||
return builder | ||
|
||
def _update_builder(self, buildy, max_mpi_per_pool): | ||
resources = self.get_resources() | ||
npools = resources.pop("npools", 1) | ||
"""Update the resources and parallelization of the ``QeAppWorkChain`` builder.""" | ||
for k, v in buildy.items(): | ||
if isinstance(v, (dict, ProcessBuilderNamespace)): | ||
if k == "pw" and v["pseudos"]: | ||
v["parallelization"] = orm.Dict(dict={"npool": npools}) | ||
if k == "projwfc": | ||
v["settings"] = orm.Dict(dict={"cmdline": ["-nk", str(npools)]}) | ||
if k == "dos": | ||
v["metadata"]["options"]["resources"] = { | ||
"num_machines": 1, | ||
"num_mpiprocs_per_machine": min( | ||
max_mpi_per_pool, | ||
resources["num_mpiprocs_per_machine"], | ||
), | ||
} | ||
# Continue to the next item to avoid overriding the resources in the | ||
# recursive `update_builder` call. | ||
continue | ||
if k == "resources": | ||
buildy["resources"] = resources | ||
else: | ||
self._update_builder(v, max_mpi_per_pool) | ||
def _update_builder(self, builder, codes): | ||
"""Update the resources and parallelization of the ``relax`` builder.""" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This update should also affect the other plugins right ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If other plugins use the same |
||
# update resources | ||
builder.relax.base.pw.metadata.options.resources = { | ||
"num_machines": codes.get("pw")["nodes"], | ||
"num_mpiprocs_per_machine": codes.get("pw")["ntasks_per_node"], | ||
"num_cores_per_mpiproc": codes.get("pw")["cpus_per_task"], | ||
} | ||
builder.relax.base.pw.parallelization = orm.Dict( | ||
dict=codes["pw"]["parallelization"] | ||
) | ||
|
||
def set_submission_parameters(self, parameters): | ||
self.set_resources(parameters["resources"]) | ||
# backward compatibility for v2023.11 | ||
# which have a separate "resources" section for pw code | ||
if "resources" in parameters: | ||
parameters["codes"] = { | ||
key: {"code": value} for key, value in parameters["codes"].items() | ||
} | ||
parameters["codes"]["pw"]["nodes"] = parameters["resources"]["num_machines"] | ||
parameters["codes"]["pw"]["cpus"] = parameters["resources"][ | ||
"num_mpiprocs_per_machine" | ||
] | ||
parameters["codes"]["pw"]["parallelization"] = { | ||
"npool": parameters["resources"]["npools"] | ||
} | ||
self.set_selected_codes(parameters["codes"]) | ||
|
||
def get_submission_parameters(self): | ||
"""Get the parameters for the submission step.""" | ||
return { | ||
"codes": self.get_selected_codes(), | ||
"resources": self.get_resources(), | ||
} | ||
|
||
def reset(self): | ||
|
@@ -463,4 +420,3 @@ def reset(self): | |
self.process = None | ||
self.input_structure = None | ||
self.set_selected_codes(DEFAULT_PARAMETERS["codes"]) | ||
self.set_resource_defaults() |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The name makes more sense if we call:
PWscfWidget
->PwCodeResourceSetupWidget
QEAppComputationalResourcesWidget
->ComputationalResourceSetupWidget
(Mind with the "s", I think it is good to use singular format.)The naming in AWB for
ComputationalResourcesWidget
might not ideal, and we can probably change it toComputationalResourceSelctionWidget
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the suggestion. I've renamed it to
PwCodeResourceSetupWidget
. Regarding the nameComputationalResourceSetupWidget
, I'll wait for the changes in AWB to be finalized before making updates here.