Coverage for src/chebpy/_singular_construction.py: 100%
34 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-22 21:50 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-22 21:50 +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 .settings import _preferences as prefs
15from .utilities import Domain
18def _piece_singularity(sing: str, is_first: bool, is_last: bool) -> str | None:
19 """Return the singularity side for one piece, or ``None`` for a smooth piece.
21 Maps the whole-domain ``sing`` request onto the side (if any) that a
22 single piece carries, given whether it is the first and/or last piece.
24 Args:
25 sing: One of ``"left"``, ``"right"``, ``"both"``.
26 is_first: Whether the piece is the leftmost in the domain.
27 is_last: Whether the piece is the rightmost in the domain.
29 Returns:
30 ``"left"``, ``"right"``, ``"both"``, or ``None`` (a smooth Bndfun piece).
31 """
32 left = is_first and sing in ("left", "both")
33 right = is_last and sing in ("right", "both")
34 if left and right:
35 return "both"
36 if left:
37 return "left"
38 if right:
39 return "right"
40 return None
43def generate_singular_funs(
44 f: Callable[..., Any],
45 domain: Any,
46 *,
47 sing: str,
48 params: Any,
49) -> list[Any]:
50 """Build per-piece funs for a Chebfun with endpoint singularities.
52 The leftmost / rightmost pieces (depending on ``sing``) are built as
53 :class:`~chebpy.singfun.Singfun` instances using the Adcock-Richardson
54 clustering map; all interior pieces are ordinary :class:`Bndfun`.
56 Args:
57 f: Callable evaluating the function in logical coordinates.
58 domain: Breakpoint sequence; outermost endpoints must be finite.
59 sing: One of ``"left"``, ``"right"``, ``"both"``.
60 params: A :class:`~chebpy.maps.MapParams` instance carrying
61 ``(L, alpha)`` for the slit-strip clustering map. ``None`` means
62 use :class:`~chebpy.maps.MapParams` defaults.
64 Returns:
65 list: Per-piece funs ready to feed to :class:`Chebfun`.
67 Raises:
68 ValueError: If ``sing`` is not one of the recognised values.
69 """
70 # Local import: chebfun and singfun are siblings under classicfun and
71 # would otherwise risk a cyclic top-level import.
72 from .maps import MapParams
73 from .singfun import Singfun
75 if sing not in ("left", "right", "both"):
76 msg = f"sing must be 'left', 'right', or 'both'; got {sing!r}"
77 raise ValueError(msg)
78 if params is None:
79 params = MapParams()
80 dom = Domain(domain if domain is not None else prefs.domain)
81 intervals = list(dom.intervals)
82 n_pieces = len(intervals)
83 funs: list[Any] = []
84 for i, interval in enumerate(intervals):
85 piece_sing = _piece_singularity(sing, i == 0, i == n_pieces - 1)
86 if piece_sing is None:
87 funs.append(Bndfun.initfun_adaptive(f, interval))
88 else:
89 funs.append(Singfun.initfun_adaptive(f, interval, sing=piece_sing, params=params))
90 return funs