panel-bie 0.8.0
Panel-based boundary integral equation solvers for 2D Laplace and Stokes problems
License: MIT
Requires Python: >=3.10
Keywords: BEM, Laplace, Nystrom, Stokes, boundary-integral, potential-theory

# 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])
```

## Command-Line Interface

The package includes a CLI for quick experiments and diagnostics.

### Commands

**solve** - Solve a Dirichlet problem on a superellipse geometry:

```bash
# Create a geometry file
echo '{"a": 1.0, "b": 1.0, "p": 4.0}' > curve.json

# Solve with harmonic boundary data u = x² - y²
panel-bie solve -g curve.json --bc "x**2 - y**2" --eval 0,0

# With custom discretization
panel-bie solve -g curve.json --bc "x" --eval 0.5,0 --panels 8 --nodes 16
```

**condition** - Compute the condition number of the discretized operator:

```bash
# Default squircle (p=4)
panel-bie condition

# Higher-order Lamé curve
panel-bie condition --p 8 --panels 8

# Custom geometry
panel-bie condition --a 2.0 --b 1.0 --p 4 --panels 4 --nodes 16
```

**convergence** - Test convergence with increasing panel count:

```bash
# Default p=4 superellipse
panel-bie convergence

# Higher exponent
panel-bie convergence --p 8
```

### CLI Options

| Command | Option | Description |
|---------|--------|-------------|
| solve | `--geometry, -g` | JSON file with geometry (a, b, p, q) |
| solve | `--bc` | Boundary condition as Python expression |
| solve | `--eval` | Evaluation point as x,y |
| solve | `--panels` | Panels per quadrant (default: 4) |
| solve | `--nodes` | Nodes per panel (default: 16) |
| solve | `--beta` | Panel grading parameter (default: auto) |
| condition | `--a, --b, --p` | Superellipse parameters |
| condition | `--panels, --nodes` | Discretization parameters |
| condition | `--beta` | Panel grading parameter (default: auto) |
| convergence | `--a, --b, --p` | Superellipse parameters |

The `--beta` parameter controls panel grading near corners and is automatically computed to ensure numerical stability (beta must be less than p/2).

## 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.