Skip to content
Draft
Show file tree
Hide file tree
Changes from 5 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
1 change: 1 addition & 0 deletions petsctools/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .appctx import AppContext # noqa: F401
from .config import ( # noqa: F401
MissingPetscException,
get_config,
Expand Down
84 changes: 84 additions & 0 deletions petsctools/appctx.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import itertools
from functools import cached_property
from petsctools.exceptions import PetscToolsAppctxException


class _AppContextKey(int):
pass


class AppContext:
"""

.. code-block:: python3

appctx = AppContext()
some_data = MyCustomObject()

opts = OptionsManager(
parameters={
'pc_type': 'python',
'pc_python_type': 'MyCustomPC',
'custompc_somedata': appctx.add(some_data)},
options_prefix="")

with opts.inserted_options():
data = appctx.get('custompc_somedata')

"""

def __init__(self):
self._count = itertools.count()
self._data = {}
self._missing_key = self._keygen()

def _keygen(self, key=None):
return _AppContextKey(next(self._count) if key is None else key)

def _option_to_key(self, option):
key = self.options_object.getInt(option, self.missing_key)
print(f"_option_to_key: {key = }")
return self._keygen(key)

@property
def missing_key(self):
"""
Key instance representing a missing AppContext entry.
"""
# PETSc requires the default value for Options.getObj()
# to be the correct type, so we need a dummy key.
return self._missing_key

def add(self, val):
"""
Add a value to the application context and
return the autogenerated key for that value.

The autogenerated key should the value for the
corresponding entry in the solver_parameters dictionary.
"""
key = self._keygen()
self._data[key] = val
print(f"add: {key = }")
return key

def __getitem__(self, option):
"""
Return the value with the key saved in `PETSc.Options()[option]`.
"""
try:
return self._data[self._option_to_key(option)]
except KeyError:
raise PetscToolsAppctxException(
f"AppContext does not have an entry for {option}")

def get(self, option, default=None):
key = self._option_to_key(option)
if key == self.missing_key:
return default
return self._data[key]

@cached_property
def options_object(self):
from petsc4py import PETSc
return PETSc.Options()
4 changes: 4 additions & 0 deletions petsctools/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,9 @@ class PetscToolsNotInitialisedException(PetscToolsException):
"""Exception raised when petsctools should have been initialised."""


class PetscToolsAppctxException(PetscToolsException):
"""Exception raised when the Appctx is missing an entry."""


class PetscToolsWarning(UserWarning):
"""Generic base class for petsctools warnings."""
24 changes: 24 additions & 0 deletions tests/test_options.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import petsctools

PETSc = petsctools.init()

appctx = petsctools.AppContext()
reynolds = 10

opts = petsctools.OptionsManager(
parameters={
'fieldsplit_1_pc_type': 'python',
'fieldsplit_1_pc_python_type': 'firedrake.MassInvPC',
'fieldsplit_1_mass_reynolds': appctx.add(reynolds)},
options_prefix="")

with opts.inserted_options():
re = appctx.get('fieldsplit_1_mass_reynolds')
print(f"{re = }")
re = appctx['fieldsplit_1_mass_reynolds']
print(f"{re = }")

re = appctx.get('fieldsplit_0_mass_reynolds', 20)
print(f"{re = }")
re = appctx['fieldsplit_0_mass_reynolds']
print(f"{re = }")
Loading