Coverage for src/chebpy/_singular_construction.py: 100%
35 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-01 13:43 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-01 13:43 +0000
1"""Construction of piecewise Chebfuns with endpoint singularities.
3Kept separate from :mod:`chebpy.chebfun` so the singularity-piece plumbing
4does not bloat the main class; it is used by the ``sing``-aware Chebfun
5constructors via :func:`generate_singular_funs`.
6"""
8from __future__ import annotations
10from collections.abc import Callable
11from typing import Any
13from .bndfun import Bndfun
14from .exceptions import InvalidSingularitySide
15from .maps import MapParams
16from .settings import _preferences as prefs
17from .singfun import Singfun
18from .utilities import Domain
21def _piece_singularity(sing: str, is_first: bool, is_last: bool) -> str | None:
22 """Return the singularity side for one piece, or ``None`` for a smooth piece.
24 Maps the whole-domain ``sing`` request onto the side (if any) that a
25 single piece carries, given whether it is the first and/or last piece.
27 Args:
28 sing: One of ``"left"``, ``"right"``, ``"both"``.
29 is_first: Whether the piece is the leftmost in the domain.
30 is_last: Whether the piece is the rightmost in the domain.
32 Returns:
33 ``"left"``, ``"right"``, ``"both"``, or ``None`` (a smooth Bndfun piece).
34 """
35 left = is_first and sing in ("left", "both")
36 right = is_last and sing in ("right", "both")
37 if left and right:
38 return "both"
39 if left:
40 return "left"
41 if right:
42 return "right"
43 return None
46def generate_singular_funs(
47 f: Callable[..., Any],
48 domain: Any,
49 *,
50 sing: str,
51 params: Any,
52) -> list[Any]:
53 """Build per-piece funs for a Chebfun with endpoint singularities.
55 The leftmost / rightmost pieces (depending on ``sing``) are built as
56 :class:`~chebpy.singfun.Singfun` instances using the Adcock-Richardson
57 clustering map; all interior pieces are ordinary :class:`Bndfun`.
59 Args:
60 f: Callable evaluating the function in logical coordinates.
61 domain: Breakpoint sequence; outermost endpoints must be finite.
62 sing: One of ``"left"``, ``"right"``, ``"both"``.
63 params: A :class:`~chebpy.maps.MapParams` instance carrying
64 ``(L, alpha)`` for the slit-strip clustering map. ``None`` means
65 use :class:`~chebpy.maps.MapParams` defaults.
67 Returns:
68 list: Per-piece funs ready to feed to :class:`Chebfun`.
70 Raises:
71 InvalidSingularitySide: If ``sing`` is not one of the recognised values.
72 """
73 if sing not in ("left", "right", "both"):
74 msg = f"sing must be 'left', 'right', or 'both'; got {sing!r}"
75 raise InvalidSingularitySide(msg)
76 if params is None:
77 params = MapParams()
78 dom = Domain(domain if domain is not None else prefs.domain)
79 intervals = list(dom.intervals)
80 n_pieces = len(intervals)
81 funs: list[Any] = []
82 for i, interval in enumerate(intervals):
83 piece_sing = _piece_singularity(sing, i == 0, i == n_pieces - 1)
84 if piece_sing is None:
85 funs.append(Bndfun.initfun_adaptive(f, interval))
86 else:
87 funs.append(Singfun.initfun_adaptive(f, interval, sing=piece_sing, params=params))
88 return funs