# panel-bie

Panel-based boundary integral equation (BIE) solvers for 2D Laplace and Stokes problems.

[![PyPI version](https://img.shields.io/pypi/v/panel-bie.svg)](https://pypi.org/project/panel-bie/)
[![Python versions](https://img.shields.io/pypi/pyversions/panel-bie.svg)](https://pypi.org/project/panel-bie/)
[![License](https://img.shields.io/github/license/egoughnour/panel-bie.svg)](https://github.com/egoughnour/panel-bie/blob/main/LICENSE)
[![CI](https://github.com/egoughnour/panel-bie/actions/workflows/ci.yml/badge.svg)](https://github.com/egoughnour/panel-bie/actions/workflows/ci.yml)
[![Documentation](https://img.shields.io/badge/docs-GitHub%20Pages-blue.svg)](https://egoughnour.github.io/panel-bie/)
[![codecov](https://codecov.io/gh/egoughnour/panel-bie/branch/main/graph/badge.svg)](https://codecov.io/gh/egoughnour/panel-bie)

## Overview

This package provides Nyström discretization methods for solving boundary integral equations on smooth 2D curves. It includes:

- **Double-layer potential** for Laplace (Dirichlet problems)
- **Single-layer potential** for Laplace (Neumann problems)
- **Combined-field formulations** for robust exterior problems
- **Curvature-corrected diagonal** for improved conditioning
- **Near-field evaluation** with Helsing-Ojala quadrature corrections

## Installation

```bash
pip install panel-bie

# With plotting support
pip install panel-bie[plot]
```

## Quick Start

```python
from panel_bie import DoubleLayerLaplace, solve_dirichlet
from superellipse import Superellipse

# Create geometry
curve = Superellipse(a=1, b=1, p=8)
disc = curve.panel_discretization(panels_per_quadrant=4, nodes_per_panel=16)

# Set up boundary data
g = lambda pt: pt[0]**2 - pt[1]**2  # harmonic function

# Solve
solver = DoubleLayerLaplace(disc)
mu = solver.solve(g)

# Evaluate at interior point
u_0 = solver.evaluate(mu, point=[0.0, 0.0])
```

## CLI

```bash
# Solve Dirichlet problem from geometry file
panel-bie solve --geometry curve.json --bc "x**2 - y**2" --eval 0,0

# Compute condition number of discretized operator
panel-bie condition --geometry curve.json --panels 8 --nodes 16
```

## Theory

### Double-Layer Potential

For Laplace's equation, the double-layer potential representation:

$$u(x) = \int_\Gamma \mu(y) \frac{\partial G(x,y)}{\partial n_y} \, ds_y$$

where $G(x,y) = \frac{1}{2\pi} \log|x-y|$ is the fundamental solution.

The density $\mu$ satisfies the second-kind integral equation:

$$\left(-\frac{1}{2}I + K\right)\mu = g$$

where $K$ is the double-layer operator.

### Diagonal Correction

For smooth boundaries, the kernel $K(x,y)$ has a finite limit as $y \to x$:

$$K(x,x) = \frac{\kappa(x)}{4\pi}$$

where $\kappa(x)$ is the signed curvature. This package uses this analytic limit rather than numerical regularization.

### Near-Field Evaluation

For target points close to the boundary, standard quadrature loses accuracy. This package implements the Helsing-Ojala method for accurate near-boundary evaluation.

## API Reference

### `DoubleLayerLaplace(discretization)`

Double-layer potential solver for interior Dirichlet problems.

| Method | Description |
|--------|-------------|
| `assemble()` | Build the discretized operator matrix |
| `solve(g)` | Solve for density given boundary data g |
| `evaluate(mu, point)` | Evaluate solution at interior point |
| `evaluate_grid(mu, grid)` | Evaluate on a grid of points |

### `SingleLayerLaplace(discretization)`

Single-layer potential solver for Neumann problems.

### `Geometry` protocol

Any geometry object providing:
- `points`: (N, 2) boundary nodes
- `normals`: (N, 2) outward unit normals
- `weights`: (N,) quadrature weights
- `curvature`: (N,) signed curvature

Compatible with `superellipse.PanelDiscretization`.

## References

- Kress, R. (2014). *Linear Integral Equations*, 3rd ed. Springer.
- Helsing, J. & Ojala, R. (2008). On the evaluation of layer potentials close to their sources. *J. Comput. Phys.* 227:2899–2921.

## License

MIT License. See [LICENSE](LICENSE) for details.
