Fix bugs in pseudopotentials panel - #1504
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1504 +/- ##
==========================================
- Coverage 75.42% 75.35% -0.07%
==========================================
Files 115 115
Lines 7365 7378 +13
==========================================
+ Hits 5555 5560 +5
- Misses 1810 1818 +8
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
6580499 to
0f03354
Compare
|
@AndresOrtegaGuerrero I did not add any tests for this. I'll open an issue to add tests, but I don't have time at the moment. |
1688c59 to
ceccbee
Compare
| def update_functionals(self): | ||
| if self.locked or not (self.functional and self.family): | ||
| return | ||
| self.functionals = ( | ||
| [self.functional] * len(self.input_structure.kinds) | ||
| if self.has_structure | ||
| else [] | ||
| ) |
There was a problem hiding this comment.
There is no need for functionals. We have all the information we need for what it was used for. Discarding it to reduce complexity.
| def get_cutoffs_by_index(self, index: int) -> list[float]: | ||
| return ( | ||
| [self.cutoffs[0][index], self.cutoffs[1][index]] | ||
| if len(self.cutoffs[0]) > index | ||
| else [0.0, 0.0] | ||
| ) |
There was a problem hiding this comment.
Cleaner to have this helper here than in the settings panel.
| def update_functional(self): | ||
| pseudos: list[UpfData] = [] | ||
| for uuid in self.dictionary.values(): | ||
| try: | ||
| assert uuid is not None | ||
| pseudo = orm.load_node(uuid) | ||
| pseudos.append(pseudo) | ||
| except (AssertionError, exceptions.NotExistent): | ||
| continue | ||
| functional_set = {pp.base.extras.get("functional", None) for pp in pseudos} | ||
| functional = functional_set.pop() if len(functional_set) == 1 else None | ||
| self._defaults["functional"] = functional | ||
| self.functional = self._get_default("functional") |
There was a problem hiding this comment.
This is called on change of pseudos in the dictionary. It collects each pseudo's functional (from extras), and sets the functional trait to the common functional if exists, otherwise None - functional is disabled in the UI.
| @@ -288,65 +307,23 @@ def on_default_pseudo( | |||
| pseudo = orm.load_node(self._model.dictionary.get(kind_name)) | |||
| except Exception: | |||
| pseudo = None | |||
| model.uploaded = model.pseudo is pseudo | |||
| model.pseudo = pseudo | |||
| model.cutoffs = ( | |||
| [ | |||
| self._model.cutoffs[0][index], # type: ignore | |||
| self._model.cutoffs[1][index], # type: ignore | |||
| ] | |||
| if len(self._model.cutoffs[0]) > index # type: ignore | |||
| else [0.0, 0.0] | |||
| ) | |||
| model.cutoffs = self._model.get_cutoffs_by_index(index) | |||
| model.update_pseudo_info() | |||
| model.uploaded = False | |||
|
|
|||
| self._model.observe( | |||
| on_default_pseudo, | |||
| synchronize_pseudo, | |||
| "dictionary", | |||
| ) | |||
|
|
|||
| def on_pseudo_upload( | |||
| change, | |||
| model: PseudoPotentialUploaderModel = uploader_model, | |||
| kind_name=kind.name, | |||
| index=index, | |||
| ): | |||
| if not (change["new"] and model.pseudo): | |||
| return | |||
|
|
|||
| self._model.family = None | |||
| self._model.library = None | |||
| self._model.functional = None | |||
|
|
|||
| self._model.dictionary = { | |||
| **self._model.dictionary, | |||
| kind_name: model.pseudo.uuid, | |||
| } | |||
|
|
|||
| functional = model.pseudo.base.extras.get("functional", None) | |||
| functionals = [*self._model.functionals] | |||
| functionals[index] = functional | |||
| # The following double-setting is done to force the blockers check, | |||
| # which now also bail early if the functionals are empty. | |||
| self._model.functionals = [] | |||
| self._model.functionals = functionals | |||
|
|
|||
| cutoffs: list = deepcopy(self._model.cutoffs) # type: ignore | |||
| cutoffs[0][index] = model.cutoffs[0] # type: ignore | |||
| cutoffs[1][index] = model.cutoffs[1] # type: ignore | |||
| self._model.cutoffs = cutoffs | |||
|
|
|||
| uploader_model.observe( | |||
| on_pseudo_upload, | |||
| "uploaded", | |||
| ) | |||
|
|
|||
| uploader = PseudoPotentialUploader(model=uploader_model) | |||
| uploader.render() | |||
|
|
|||
| on_default_pseudo() | |||
|
|
|||
| self._links.extend(uploader.links) | |||
|
|
|||
| children.append(uploader) | |||
|
|
|||
| synchronize_pseudo() | |||
There was a problem hiding this comment.
This is mostly renaming and reshuffling for clarity, plus adjusting the logic w.r.t dropping functionals.
| @@ -218,22 +218,29 @@ def set_model_state(self, state: dict): | |||
| pseudos.library = library | |||
| pseudos.family = pseudo_family_string | |||
| else: | |||
| # Otherwise, we assume a custom pseudo upload (warning enabled). | |||
| # Everything is set to None except the functional, which we extract from the pseudos. | |||
| pseudos.show_upload_warning = True | |||
| functionals = [] | |||
| try: | |||
| pp_uuid = next(iter(PW["pseudos"].values())) | |||
| pseudo_info = get_pseudo_info(pp_uuid) | |||
| pseudos.functional = pseudo_info["functional"] | |||
| except Exception: | |||
| for pp_uuid in PW["pseudos"].values(): | |||
| pseudo_info = get_pseudo_info(pp_uuid) | |||
| functionals.append(pseudo_info["functional"]) | |||
| if len(set(functionals)) == 1: | |||
| pseudos.functional = functionals[0] | |||
| else: | |||
| pseudos.functional = None | |||
| except Exception as err: | |||
| print(f"Error loading pseudos: {err}") | |||
| pseudos.functional = None | |||
| pseudos.library = None | |||
| pseudos.family = None | |||
| pseudos.show_upload_warning = True | |||
|
|
|||
| pseudos.functionals = [pseudos.functional] * num_kinds | |||
|
|
|||
| if pseudos_dictionary := PW.get("pseudos"): | |||
| pseudos.dictionary = pseudos_dictionary | |||
| pseudos.ecutwfc = SYSTEM.get("ecutwfc", 0.0) | |||
| pseudos.ecutrho = SYSTEM.get("ecutrho", 0.0) | |||
| if pseudos_dictionary := PW.get("pseudos"): | |||
| # We set these outside of the context manager to avoid a reset by other trait notifications. | |||
| pseudos.dictionary = pseudos_dictionary | |||
| pseudos.ecutwfc = SYSTEM.get("ecutwfc", 0.0) | |||
| pseudos.ecutrho = SYSTEM.get("ecutrho", 0.0) | |||
There was a problem hiding this comment.
This part I suspect can be simplified, but immediate efforts failed. Keeping as is for now.
The panel was incorrectly set when loaded from a process (or duplicated).
bb0d104 to
ec4bbe2
Compare
|
@AndresOrtegaGuerrero did you have any other comments? I'd like to get this in before releasing a new QE app version 🙏 If there are any issues that I missed, we can address them with a patch release next month (including further tests of course). |
| pseudos: list[UpfData] = [] | ||
| for uuid in self.dictionary.values(): | ||
| try: | ||
| assert uuid is not None |
There was a problem hiding this comment.
why not if uuid is None: ? , I guess is to put raise an Error ?
There was a problem hiding this comment.
uuid is rarely None. In practice, it shouldn't ever be None. Hence, instead of checking for a condition that is rarely there (if ever), I rather try what SHOULD work, catching the rare cases where something is out of sync. Ideally, I would implement a better logging system for debugging and add debug messages in these rare events, to let us know what happened. Actually, though it would be annoying for the user, perhaps it is good to still print out something. I'll update this block, separating the two errors with dedicated print messages.
There was a problem hiding this comment.
Looking at some older code (see the _check_blockers method in the same file, I indeed split the two error paths, with the assert error leading to a blocker w.r.t non-existent pseudos in the selected sssp library for the selected element. I recall this being an issue for Ce at some point. I should do something similar for the new code.
There was a problem hiding this comment.
Yes, there are missing Psedos for SOC for lanthanides... yes
AndresOrtegaGuerrero
left a comment
There was a problem hiding this comment.
LGMT! I left a comment but it was more like a question
cbc1b71 to
5d0807b
Compare
The panel was incorrectly set when loaded from a process (or duplicated).
Closes #1495
Note that this doesn't work on its own due to a larger issue with the blockers system. This is fixed in another PR.