Source code for firedrake.utils

# Some generic python utilities not really specific to our work.
import collections.abc
from decorator import decorator
from pyop2.utils import cached_property  # noqa: F401
from pyop2.datatypes import ScalarType, as_cstr
from pyop2.datatypes import RealType     # noqa: F401
from pyop2.datatypes import IntType      # noqa: F401
from pyop2.datatypes import as_ctypes    # noqa: F401
from pyop2.mpi import MPI
from firedrake.petsc import get_petsc_variables


# MPI key value for storing a per communicator universal identifier
FIREDRAKE_UID = MPI.Comm.Create_keyval()

RealType_c = as_cstr(RealType)
ScalarType_c = as_cstr(ScalarType)
IntType_c = as_cstr(IntType)

complex_mode = (get_petsc_variables()["PETSC_SCALAR"].lower() == "complex")

# Remove this (and update test suite) when Slate supports complex mode.
SLATE_SUPPORTS_COMPLEX = False


def _new_uid(comm):
    uid = comm.Get_attr(FIREDRAKE_UID)
    if uid is None:
        uid = 0
    comm.Set_attr(FIREDRAKE_UID, uid + 1)
    return uid


def _init():
    """Cause :func:`pyop2.init` to be called in case the user has not done it
    for themselves. The result of this is that the user need only call
    :func:`pyop2.init` if she wants to set a non-default option, for example
    to switch the debug or log level."""
    from pyop2 import op2
    from firedrake.parameters import parameters
    if not op2.initialised():
        op2.init(**parameters["pyop2_options"])


[docs] def unique(iterable): """ Return tuple of unique items in iterable, items must be hashable """ # Use dict to preserve order and compare by hash unique_dict = {} for item in iterable: unique_dict[item] = None return tuple(unique_dict.keys())
[docs] def unique_name(name, nameset): """Return name if name is not in nameset, or a deterministic uniquified name if name is in nameset. The new name is inserted into nameset to prevent further name clashes.""" if name not in nameset: nameset.add(name) return name idx = 0 while True: newname = "%s_%d" % (name, idx) if newname in nameset: idx += 1 else: nameset.add(name) return newname
[docs] def known_pyop2_safe(f): """Decorator to mark a function as being PyOP2 type-safe. This switches the current PyOP2 type checking mode to the value given by the parameter "type_check_safe_par_loops", and restores it after the function completes.""" from firedrake.parameters import parameters def wrapper(f, *args, **kwargs): opts = parameters["pyop2_options"] check = opts["type_check"] safe = parameters["type_check_safe_par_loops"] if check == safe: return f(*args, **kwargs) opts["type_check"] = safe try: return f(*args, **kwargs) finally: opts["type_check"] = check return decorator(wrapper, f)
[docs] def tuplify(item): """Convert an object into a hashable equivalent. This is particularly useful for caching dictionaries of parameters such as `form_compiler_parameters` from :func:`firedrake.assemble.assemble`. :arg item: The object to attempt to 'tuplify'. :returns: The object interpreted as a tuple. For hashable objects this is simply a 1-tuple containing `item`. For dictionaries the function is called recursively on the values of the dict. For example, `{"a": 5, "b": 8}` returns `(("a", (5,)), ("b", (8,)))`. """ if isinstance(item, collections.abc.Hashable): return (item,) if not isinstance(item, dict): raise ValueError(f"tuplify does not know how to handle objects of type {type(item)}") return tuple((k, tuplify(item[k])) for k in sorted(item))
[docs] def split_by(condition, items): """Split an iterable in two according to some condition. :arg condition: Callable applied to each item in ``items``, returning ``True`` or ``False``. :arg items: Iterable to split apart. :returns: A 2-tuple of the form ``(yess, nos)``, where ``yess`` is a tuple containing the entries of ``items`` where ``condition`` is ``True`` and ``nos`` is a tuple of those where ``condition`` is ``False``. """ result = [], [] for item in items: if condition(item): result[0].append(item) else: result[1].append(item) return tuple(result[0]), tuple(result[1])
[docs] def assert_empty(iterator): """Check that an iterator has been fully consumed. Raises ------ AssertionError If the provided iterator is not empty. Notes ----- This function should only be used for assertions (where the program is immediately terminated on failure). If the iterator is not empty then the latest value is discarded. """ try: next(iterator) raise AssertionError("Iterator is not empty") except StopIteration: pass