diff --git a/nutils/sample.py b/nutils/sample.py index c9ffce779..93fd0d79b 100644 --- a/nutils/sample.py +++ b/nutils/sample.py @@ -451,7 +451,39 @@ def tri(self) -> numpy.ndarray: def hull(self) -> numpy.ndarray: return numpy.take(self._index, self._parent.hull) -class _Empty(Sample): +if os.environ.get('NUTILS_TENSORIAL', None) == 'test': # pragma: nocover + + from unittest import SkipTest + + class _TensorialSample(Sample): + + def getindex(self, ielem: int) -> numpy.ndarray: + raise SkipTest('`{}` does not implement `Sample.getindex`'.format(type(self).__qualname__)) + + def get_evaluable_indices(self, __ielem: evaluable.Array) -> evaluable.Array: + raise SkipTest('`{}` does not implement `Sample.get_evaluable_indices`'.format(type(self).__qualname__)) + + def get_evaluable_weights(self, __ielem: evaluable.Array) -> evaluable.Array: + raise SkipTest('`{}` does not implement `Sample.get_evaluable_weights`'.format(type(self).__qualname__)) + + def update_lower_args(self, __ielem: evaluable.Array, points_shape: _PointsShape, transform_chains: _TransformChainsMap, coordinates: _CoordinatesMap) -> Tuple[_PointsShape, _TransformChainsMap, _CoordinatesMap]: + raise SkipTest('`{}` does not implement `Sample.update_lower_args`'.format(type(self).__qualname__)) + + @property + def transforms(self) -> Tuple[Transforms, ...]: + raise SkipTest('`{}` does not implement `Sample.transforms`'.format(type(self).__qualname__)) + + @property + def points(self) -> Tuple[Transforms, ...]: + raise SkipTest('`{}` does not implement `Sample.points`'.format(type(self).__qualname__)) + + def basis(self) -> function.Array: + raise SkipTest('`{}` does not implement `Sample.basis`'.format(type(self).__qualname__)) + +else: + _TensorialSample = Sample + +class _Empty(_TensorialSample): def __init__(self, spaces: Tuple[str, ...], ndims: int) -> None: super().__init__(spaces, ndims, 0, 0) @@ -488,7 +520,7 @@ def __rmatmul__(self, __func: function.IntoArray) -> function.Array: def basis(self) -> function.Array: return function.zeros((0,), float) -class _Add(Sample): +class _Add(_TensorialSample): def __init__(self, sample1: Sample, sample2: Sample) -> None: assert sample1.spaces == sample2.spaces @@ -534,7 +566,7 @@ def integral(self, func: function.IntoArray) -> function.Array: def __rmatmul__(self, func: function.IntoArray) -> function.Array: return function.concatenate([func @ self._sample1, func @ self._sample2]) -class _Mul(Sample): +class _Mul(_TensorialSample): def __init__(self, sample1: Sample, sample2: Sample) -> None: assert set(sample1.spaces).isdisjoint(set(sample2.spaces)) @@ -612,7 +644,7 @@ def basis(self) -> Sample: basis2 = self._sample2.basis() return function.ravel(basis1[:,None] * basis2[None,:], axis=0) -class _TakeElements(Sample): +class _TakeElements(_TensorialSample): __cache__ = '_offsets' diff --git a/nutils/topology.py b/nutils/topology.py index d341f523c..eae573c54 100644 --- a/nutils/topology.py +++ b/nutils/topology.py @@ -39,7 +39,7 @@ from .elementseq import References from .pointsseq import PointsSequence from typing import Any, FrozenSet, Iterable, Iterator, List, Mapping, Optional, Sequence, Tuple, Union -import numpy, functools, collections.abc, itertools, functools, operator, numbers, pathlib, abc, treelog as log +import numpy, functools, collections.abc, itertools, functools, operator, numbers, pathlib, abc, treelog as log, os _ = numpy.newaxis _identity = lambda x: x @@ -878,12 +878,93 @@ def basis_discont(self, degree: int) -> function.Basis: coeffs = [ref.get_poly_coeffs('bernstein', degree=degree) for ref in self.references] return function.DiscontBasis(coeffs, self.f_index, self.f_coords) +if os.environ.get('NUTILS_TENSORIAL', None) == 'test': # pragma: nocover + + from unittest import SkipTest + + class _TensorialTopology(Topology): + + def take_unchecked(self, __indices: numpy.ndarray, __idim: int, __ndim: int) -> Topology: + raise SkipTest('`{}` does not implement `Topology.take_unchecked`'.format(type(self).__qualname__)) + + def __and__(self, other: Any) -> Topology: + result = super().__and__(other) + if type(self) == type(other) and result is NotImplemented: + raise SkipTest('`{}` does not implement `Topology.__and__`'.format(type(self).__qualname__)) + return result + + def __rand__(self, other: Any) -> Topology: + result = super().__and__(other) + if result is NotImplemented: + raise SkipTest('`{}` does not implement `Topology.__and__`'.format(type(self).__qualname__)) + return result + + def __sub__(self, other: Any) -> Topology: + if type(self) == type(other): + raise SkipTest('`{}` does not implement `Topology.__sub__`'.format(type(self).__qualname__)) + else: + return NotImplemented + + def __rsub__(self, other: Any) -> Topology: + if isinstance(other, Topology): + raise SkipTest('`{}` does not implement `Topology.__sub__`'.format(type(self).__qualname__)) + else: + return NotImplemented + + @property + def space(self) -> str: + raise SkipTest('`{}` does not implement `Topology.space`'.format(type(self).__qualname__)) + + @property + def transforms(self) -> transformseq.Transforms: + raise SkipTest('`{}` does not implement `Topology.transforms`'.format(type(self).__qualname__)) + + @property + def opposites(self) -> transformseq.Transforms: + raise SkipTest('`{}` does not implement `Topology.opposites`'.format(type(self).__qualname__)) + + @property + def border_transforms(self) -> transformseq.Transforms: + raise SkipTest('`{}` does not implement `Topology.border_transforms`'.format(type(self).__qualname__)) + + @property + def f_index(self) -> function.Array: + raise SkipTest('`{}` does not implement `Topology.f_index`'.format(type(self).__qualname__)) + + @property + def f_coords(self) -> function.Array: + raise SkipTest('`{}` does not implement `Topology.f_coords`'.format(type(self).__qualname__)) + + def refined_by(self, refine: Iterable[int]) -> Topology: + raise SkipTest('`{}` does not implement `Topology.refined_by`'.format(type(self).__qualname__)) + + def trim(self, levelset: function.Array, maxrefine: int, ndivisions: int = 8, name: str = 'trimmed', leveltopo: Optional[Topology] = None, *, arguments: Optional[_ArgDict] = None) -> Topology: + raise SkipTest('`{}` does not implement `Topology.trim`'.format(type(self).__qualname__)) + + def subset(self, topo: Topology, newboundary: Optional[Union[str, Topology]] = None, strict: bool = False) -> Topology: + raise SkipTest('`{}` does not implement `Topology.subset`'.format(type(self).__qualname__)) + + def withgroups(self, vgroups: Mapping[str, Union[str, Topology]] = {}, bgroups: Mapping[str, Union[str, Topology]] = {}, igroups: Mapping[str, Union[str, Topology]] = {}, pgroups: Mapping[str, Union[str, Topology]] = {}) -> Topology: + try: + return super().withgroups(vgroups, bgroups, igroups, pgroups) + except NotImplementedError: + raise SkipTest('`{}` does not implement `Topology.withgroups`'.format(type(self).__qualname__)) + + def indicator(self, subtopo: Union[str, Topology]) -> Topology: + raise SkipTest('`{}` does not implement `Topology.indicator`'.format(type(self).__qualname__)) + + def locate(self, geom, coords, *, tol=0, eps=0, maxiter=0, arguments=None, weights=None, maxdist=None, ischeme=None, scale=None) -> Sample: + raise SkipTest('`{}` does not implement `Topology.locate`'.format(type(self).__qualname__)) + +else: + _TensorialTopology = Topology + class _EmptyUnlowerable(function.Array): def lower(self, points_shape, transform_chains, coordinates) -> evaluable.Array: raise ValueError('cannot lower') -class _Empty(Topology): +class _Empty(_TensorialTopology): def __init__(self, spaces: Tuple[str, ...], space_dims: Tuple[int, ...], ndims: int) -> None: super().__init__(spaces, space_dims, References.empty(ndims)) @@ -923,7 +1004,7 @@ def basis_std(self, degree: int, *args, **kwargs) -> function.Array: def sample(self, ischeme: str, degree: int) -> Sample: return Sample.empty(self.spaces, self.ndims) -class _DisjointUnion(Topology): +class _DisjointUnion(_TensorialTopology): def __init__(self, topo1: Topology, topo2: Topology) -> None: if topo1.spaces != topo2.spaces or topo1.space_dims != topo2.space_dims or topo1.ndims != topo2.ndims: @@ -989,7 +1070,7 @@ def select(self, indicator: function.Array, ischeme: str = 'bezier2', **kwargs: topo2 = self.topo2.select(indicator, ischeme, **kwargs) return Topology.disjoint_union(topo1, topo2) -class _Mul(Topology): +class _Mul(_TensorialTopology): def __init__(self, topo1: Topology, topo2: Topology) -> None: if not set(topo1.spaces).isdisjoint(topo2.spaces): @@ -1124,7 +1205,7 @@ def basis(self, name: str, degree: Union[int, Sequence[int]], **kwargs) -> funct def sample(self, ischeme: str, degree: int) -> Sample: return self.topo1.sample(ischeme, degree) * self.topo2.sample(ischeme, degree) -class _Take(Topology): +class _Take(_TensorialTopology): def __init__(self, parent: Topology, indices: types.arraydata) -> None: self.parent = parent @@ -1137,7 +1218,7 @@ def __init__(self, parent: Topology, indices: types.arraydata) -> None: def sample(self, ischeme: str, degree: int) -> Sample: return self.parent.sample(ischeme, degree).take_elements(self.indices) -class _WithGroupAliases(Topology): +class _WithGroupAliases(_TensorialTopology): def __init__(self, parent: Topology, vgroups: Mapping[str, str] = {}, bgroups: Mapping[str, str] = {}, igroups: Mapping[str, str] = {}) -> None: self.parent = parent diff --git a/tests/test_docs.py b/tests/test_docs.py index f60dd8fbd..7b091f78c 100644 --- a/tests/test_docs.py +++ b/tests/test_docs.py @@ -46,6 +46,9 @@ def __repr__(self): __str__ = __repr__ +if os.environ.get('NUTILS_TENSORIAL', None): + DocTestCase = unittest.skip('disabled for tensorial topologies')(DocTestCase) + doctest = unittest.TestSuite() parser = _doctest.DocTestParser() finder = _doctest.DocTestFinder(parser=parser)