|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
| 3 | +import weakref |
3 | 4 | import contextlib |
4 | 5 | import functools |
5 | 6 | import itertools |
6 | 7 | import warnings |
7 | | -from typing import Any |
| 8 | +from typing import Any, Iterable |
8 | 9 |
|
9 | 10 | import petsc4py |
10 | 11 |
|
@@ -90,12 +91,42 @@ def munge(keys): |
90 | 91 | if option in new: |
91 | 92 | warnings.warn( |
92 | 93 | f"Ignoring duplicate option: {option} (existing value " |
93 | | - f"{new[option]}, new value {value})", |
| 94 | + f"{new[option]}, new value {value})", PetscToolsWarning |
94 | 95 | ) |
95 | 96 | new[option] = value |
96 | 97 | return new |
97 | 98 |
|
98 | 99 |
|
| 100 | +def _warn_unused_options(all_options: Iterable, used_options: Iterable, |
| 101 | + options_prefix: str = ""): |
| 102 | + """ |
| 103 | + Raise warnings for PETSc options which were not used. |
| 104 | +
|
| 105 | + This is meant only as a weakref.finalize callback for the OptionsManager. |
| 106 | +
|
| 107 | + Parameters |
| 108 | + ---------- |
| 109 | + all_options : |
| 110 | + The full set of options passed to the OptionsManager. |
| 111 | + used_options : |
| 112 | + The options which were used during the OptionsManager's lifetime. |
| 113 | + options_prefix : |
| 114 | + The options_prefix of the OptionsManager. |
| 115 | +
|
| 116 | + Raises |
| 117 | + ------ |
| 118 | + PetscToolsWarning : |
| 119 | + For every entry in all_options which is not in used_options. |
| 120 | + """ |
| 121 | + unused_options = set(all_options) - set(used_options) |
| 122 | + |
| 123 | + for option in sorted(unused_options): |
| 124 | + warnings.warn( |
| 125 | + f"Unused PETSc option: {options_prefix+option}", |
| 126 | + PetscToolsWarning |
| 127 | + ) |
| 128 | + |
| 129 | + |
99 | 130 | class OptionsManager: |
100 | 131 | """Class that helps with managing setting PETSc options. |
101 | 132 |
|
@@ -238,8 +269,17 @@ def __init__(self, parameters: dict, options_prefix: str | None): |
238 | 269 | # since that does not DTRT for flag options. |
239 | 270 | for k, v in self.options_object.getAll().items(): |
240 | 271 | if k.startswith(self.options_prefix): |
241 | | - self.parameters[k[len(self.options_prefix) :]] = v |
| 272 | + self.parameters[k[len(self.options_prefix):]] = v |
242 | 273 | self._setfromoptions = False |
| 274 | + # Keep track of options used between invocations of inserted_options(). |
| 275 | + self._used_options = set() |
| 276 | + |
| 277 | + # Decide whether to warn for unused options |
| 278 | + with self.inserted_options(): |
| 279 | + if self.options_object.getInt("options_left", 0) > 0: |
| 280 | + weakref.finalize(self, _warn_unused_options, |
| 281 | + self.to_delete, self._used_options, |
| 282 | + options_prefix=self.options_prefix) |
243 | 283 |
|
244 | 284 | def set_default_parameter(self, key: str, val: Any) -> None: |
245 | 285 | """Set a default parameter value. |
@@ -292,6 +332,8 @@ def inserted_options(self): |
292 | 332 | yield |
293 | 333 | finally: |
294 | 334 | for k in self.to_delete: |
| 335 | + if self.options_object.used(self.options_prefix + k): |
| 336 | + self._used_options.add(k) |
295 | 337 | del self.options_object[self.options_prefix + k] |
296 | 338 |
|
297 | 339 | @functools.cached_property |
|
0 commit comments