"""
pysurfacefun
============
High-order patch-based tools for solving variable-coefficient elliptic
partial differential equations on smooth surfaces.
The implementation uses Chebyshev surface patches, strong-form surface
differentiation, local patch solution operators, and hierarchical
Schur-complement merging for fast repeated elliptic solves.
"""
import numpy as np
from .core import (
PDO,
SurfaceFunction,
SurfaceMesh,
SurfaceOp,
SurfaceVectorFunction,
boundingbox,
chebpts,
chebpts2,
compose,
conj,
cos,
cross,
div,
divergence,
diff,
diffmat,
diffx,
diffy,
diffz,
dot,
exp,
from_rhino,
grad as _quad_grad,
hodge,
imag,
integral as _quad_integral,
lap as _quad_lap,
log,
log10,
maxEst,
mean2,
minEst,
norm as _quad_norm,
normal,
normalize,
plot_surface,
plot_vector_field,
prolong,
real,
real_spherical_harmonic,
randnfun3,
resample as _quad_resample,
resample_mesh as _quad_resample_mesh,
sin,
solve_laplace_beltrami_sphere,
sphere,
sqrt,
stellarator,
surfacearea,
surfacefun as _quad_surfacefun,
surfacefunv as _quad_surfacefunv,
surfaceop,
sum2,
torus,
vector_norm as _quad_vector_norm,
write_vtu,
smooth_random_function_3d,
)
from .tri import (
LevelSetSurface,
TriangleSurfaceFunction,
TriangleSurfaceMesh,
TriangleSurfaceOp,
TriangleSurfaceVectorFunction,
extract_tri_mesh_arrays,
icosphere_tri,
koornwinder_pkd,
levelset_surface,
levelset_surface_quad,
levelset_surface_tri,
levelset_surface_tri_from_mat,
load_mat_tri_mesh,
load_mat_surface_mesh,
node_family_points,
orient_tri_faces_outward,
plot_wireframe,
plot_tri_surface,
project_to_levelset,
recursive_nodes,
refine_quad_mesh,
refine_surface_mesh,
refine_tri_mesh,
reference_triangle_quadrature_weights,
shifted_lobatto_nodes,
surface_mesh_arrays,
triangle_boundary_meshio_mesh,
triangle_meshio_mesh,
triangle_surface_mesh_arrays,
tri_edge_indices,
tri_patch_boundary_segments,
tri_wireframe_edge_indices,
tri_resample,
tri_resample_mesh,
tri_resampled_patch_geometry,
tri_resampled_patch_values,
tri_diff,
tri_integral2,
tri_lap,
tri_reference_nodes,
tri_strong_diffmat,
tri_surfacearea,
tri_surfacefun,
tri_surfaceop,
triangulate_faces,
trilattice,
write_tri_patch_boundaries_vtp,
write_tri_vtp,
write_tri_vtu,
write_triangle_meshio,
wireframe,
)
from .problems import (
SurfaceEquation,
SurfaceIVP,
SurfaceIVPSolver,
SurfaceLBVP,
SurfaceLBVPSolver,
SurfaceProblem,
)
from .evaluator import (
EvaluationRecord,
EvaluationTask,
Evaluator,
JSONLinesOutputHandler,
NPZOutputHandler,
OutputHandler,
VTKOutputHandler,
)
SurfaceField = SurfaceFunction | TriangleSurfaceFunction
SurfaceVectorField = SurfaceVectorFunction | TriangleSurfaceVectorFunction
surfacefun = _quad_surfacefun
surfacefunv = _quad_surfacefunv
[docs]
def field(func, dom: SurfaceMesh | TriangleSurfaceMesh) -> SurfaceField:
"""Construct a scalar field on a quadrilateral or triangular surface mesh."""
if isinstance(dom, TriangleSurfaceMesh):
return tri_surfacefun(func, dom)
if isinstance(dom, SurfaceMesh):
return _quad_surfacefun(func, dom)
raise TypeError("field expects a SurfaceMesh or TriangleSurfaceMesh domain")
[docs]
def vector_field(
fx,
fy=None,
fz=None,
dom: SurfaceMesh | TriangleSurfaceMesh | None = None,
) -> SurfaceVectorField:
"""Construct a vector field on a quadrilateral or triangular surface mesh."""
if isinstance(fx, (SurfaceVectorFunction, TriangleSurfaceVectorFunction)) and fy is None and fz is None:
return fx
if isinstance(fx, SurfaceMesh) and fy is None and fz is None:
return _quad_surfacefunv(fx)
if isinstance(fx, TriangleSurfaceMesh) and fy is None and fz is None:
zero = field(0.0, fx)
return TriangleSurfaceVectorFunction((zero, zero.copy(), zero.copy()))
if isinstance(fx, SurfaceFunction) and isinstance(fy, SurfaceFunction) and isinstance(fz, SurfaceFunction):
return _quad_surfacefunv(fx, fy, fz)
if isinstance(fx, TriangleSurfaceFunction) and isinstance(fy, TriangleSurfaceFunction) and isinstance(fz, TriangleSurfaceFunction):
return TriangleSurfaceVectorFunction((fx, fy, fz))
if dom is None:
raise ValueError("dom is required when components are not already surface fields")
if fy is None or fz is None:
raise ValueError("all three vector components are required")
if isinstance(dom, SurfaceMesh):
return _quad_surfacefunv(fx, fy, fz, dom)
if isinstance(dom, TriangleSurfaceMesh):
return TriangleSurfaceVectorFunction((field(fx, dom), field(fy, dom), field(fz, dom)))
raise TypeError("vector_field expects a SurfaceMesh or TriangleSurfaceMesh domain")
[docs]
def lap(f: SurfaceField) -> SurfaceField:
"""Surface Laplacian for quadrilateral or triangular scalar fields."""
if isinstance(f, TriangleSurfaceFunction):
return tri_lap(f)
if isinstance(f, SurfaceFunction):
return _quad_lap(f)
raise TypeError("lap expects a scalar surface field")
[docs]
def laplacian(f: SurfaceField) -> SurfaceField:
"""Alias for :func:`lap`."""
return lap(f)
[docs]
def grad(f: SurfaceField) -> SurfaceVectorField:
"""Surface gradient for quadrilateral or triangular scalar fields."""
if isinstance(f, TriangleSurfaceFunction):
return TriangleSurfaceVectorFunction((tri_diff(f, 1), tri_diff(f, 2), tri_diff(f, 3)))
if isinstance(f, SurfaceFunction):
return _quad_grad(f)
raise TypeError("grad expects a scalar surface field")
[docs]
def gradient(f: SurfaceField) -> SurfaceVectorField:
"""Alias for :func:`grad`."""
return grad(f)
[docs]
def vector_norm(f: SurfaceVectorField) -> SurfaceField:
"""Pointwise magnitude of a quadrilateral or triangular vector field."""
if isinstance(f, TriangleSurfaceVectorFunction):
a, b, c = f.components
vals = [np.sqrt(x * x + y * y + z * z) for x, y, z in zip(a.vals, b.vals, c.vals)]
return TriangleSurfaceFunction(f.domain, vals)
if isinstance(f, SurfaceVectorFunction):
return _quad_vector_norm(f)
raise TypeError("vector_norm expects a vector surface field")
[docs]
def integral(f: SurfaceField, reduce: bool = True):
"""Surface integral for quadrilateral or triangular scalar fields."""
if isinstance(f, TriangleSurfaceFunction):
return tri_integral2(f, reduce=reduce)
if isinstance(f, SurfaceFunction):
return _quad_integral(f, reduce=reduce)
raise TypeError("integral expects a scalar surface field")
[docs]
def integral2(f: SurfaceField, reduce: bool = True):
"""Alias for :func:`integral`."""
return integral(f, reduce=reduce)
[docs]
def norm(f: SurfaceField | SurfaceVectorField, p: int | float | str = 2, reduce: bool = True):
"""Norm of a quadrilateral or triangular scalar/vector surface field."""
if isinstance(f, (SurfaceFunction, SurfaceVectorFunction)):
return _quad_norm(f, p=p, reduce=reduce)
if isinstance(f, TriangleSurfaceVectorFunction):
mag = vector_norm(f)
if p == 2 and reduce:
return float(np.sqrt(integral(mag * mag)))
return norm(mag, p=p, reduce=reduce)
if not isinstance(f, TriangleSurfaceFunction):
raise TypeError("norm expects a scalar or vector surface field")
if p in (np.inf, "inf", "max"):
per_patch = np.asarray([np.max(np.abs(v)) for v in f.vals])
return float(np.max(per_patch)) if reduce else per_patch
if p == "H1":
g = grad(f)
parts = [norm(f, 2, False)]
parts += [norm(comp, 2, False) for comp in g.components]
per_patch = np.sqrt(sum(part * part for part in parts))
return float(np.sqrt(np.sum(per_patch * per_patch))) if reduce else per_patch
if p == "lap":
n0 = norm(f, 2, False)
n1 = norm(lap(f), 2, False)
per_patch = np.sqrt(n0 * n0 + n1 * n1)
return float(np.sqrt(np.sum(per_patch * per_patch))) if reduce else per_patch
p_float = float(p)
per_patch = tri_integral2(TriangleSurfaceFunction(f.domain, [np.abs(v) ** p_float for v in f.vals]), reduce=False)
per_patch = per_patch ** (1.0 / p_float)
if reduce:
return float(np.sum(per_patch**p_float) ** (1.0 / p_float))
return per_patch
[docs]
def resample(obj, n: int):
"""Resample a scalar surface function or surface mesh."""
if isinstance(obj, TriangleSurfaceFunction):
return tri_resample(obj, n)
if isinstance(obj, TriangleSurfaceMesh):
return tri_resample_mesh(obj, n)
if isinstance(obj, SurfaceFunction):
return _quad_resample(obj, n)
if isinstance(obj, SurfaceMesh):
return _quad_resample_mesh(obj, n)
raise TypeError("resample expects a surface function or surface mesh")
[docs]
def resample_mesh(dom, n: int):
"""Resample a quadrilateral or triangular surface mesh."""
if isinstance(dom, TriangleSurfaceMesh):
return tri_resample_mesh(dom, n)
if isinstance(dom, SurfaceMesh):
return _quad_resample_mesh(dom, n)
raise TypeError("resample_mesh expects a surface mesh")
__all__ = [
"PDO",
"SurfaceField",
"SurfaceFunction",
"SurfaceEquation",
"SurfaceIVP",
"SurfaceIVPSolver",
"SurfaceLBVP",
"SurfaceLBVPSolver",
"SurfaceMesh",
"SurfaceOp",
"SurfaceProblem",
"SurfaceVectorField",
"SurfaceVectorFunction",
"boundingbox",
"chebpts",
"chebpts2",
"compose",
"conj",
"cos",
"cross",
"div",
"divergence",
"diff",
"diffmat",
"diffx",
"diffy",
"diffz",
"dot",
"exp",
"field",
"from_rhino",
"grad",
"gradient",
"hodge",
"imag",
"integral",
"integral2",
"lap",
"laplacian",
"log",
"log10",
"maxEst",
"mean2",
"minEst",
"norm",
"normal",
"normalize",
"plot_surface",
"plot_vector_field",
"prolong",
"real",
"real_spherical_harmonic",
"randnfun3",
"resample",
"resample_mesh",
"sin",
"solve_laplace_beltrami_sphere",
"sphere",
"sqrt",
"stellarator",
"smooth_random_function_3d",
"surfacearea",
"surfacefun",
"surfacefunv",
"surfaceop",
"sum2",
"torus",
"vector_norm",
"vector_field",
"write_vtu",
"LevelSetSurface",
"TriangleSurfaceFunction",
"TriangleSurfaceMesh",
"TriangleSurfaceOp",
"TriangleSurfaceVectorFunction",
"EvaluationRecord",
"EvaluationTask",
"Evaluator",
"JSONLinesOutputHandler",
"NPZOutputHandler",
"OutputHandler",
"VTKOutputHandler",
"extract_tri_mesh_arrays",
"icosphere_tri",
"koornwinder_pkd",
"levelset_surface",
"levelset_surface_quad",
"levelset_surface_tri",
"levelset_surface_tri_from_mat",
"load_mat_tri_mesh",
"load_mat_surface_mesh",
"node_family_points",
"orient_tri_faces_outward",
"plot_wireframe",
"plot_tri_surface",
"project_to_levelset",
"recursive_nodes",
"refine_quad_mesh",
"refine_surface_mesh",
"refine_tri_mesh",
"reference_triangle_quadrature_weights",
"shifted_lobatto_nodes",
"surface_mesh_arrays",
"triangle_boundary_meshio_mesh",
"triangle_meshio_mesh",
"triangle_surface_mesh_arrays",
"tri_edge_indices",
"tri_patch_boundary_segments",
"tri_wireframe_edge_indices",
"tri_resample",
"tri_resample_mesh",
"tri_resampled_patch_geometry",
"tri_resampled_patch_values",
"tri_diff",
"tri_integral2",
"tri_lap",
"tri_reference_nodes",
"tri_strong_diffmat",
"tri_surfacearea",
"tri_surfacefun",
"tri_surfaceop",
"triangulate_faces",
"trilattice",
"write_tri_patch_boundaries_vtp",
"write_tri_vtp",
"write_tri_vtu",
"write_triangle_meshio",
"wireframe",
]