# tree_d_3d.strip

*module*

Line charges between two grounded plates, by conformal mapping and the FMM.

Charges are infinite line charges along z (2D Cartesian electrostatics) in the
strip ``0 < x < L`` between grounded plates at ``x = 0`` and ``x = L``, which
extend to infinity in y. The map

    zeta = exp(i pi z / L),   z = x + i y,

sends the strip onto the upper half-plane and both plates onto the real axis.
There, a grounded boundary needs a single image per charge, at the complex
conjugate point with the opposite charge, so the strip's Green's function is

    phi(z) = -2 k_e sum_j q_j ln| (zeta - zeta_j) / (zeta - conj(zeta_j)) |.

That is one free-space line-charge sum over 2N sources, which
:meth:`FlatQuadtree.fmm` evaluates in O(N). The field follows from the
complex derivative, ``E_x - i E_y = 2 k_e W'(zeta) (i pi / L) zeta`` with
``W = sum q log(zeta - zeta_j) - sum q log(zeta - conj(zeta_j))``. Every
charge feels the field of all other charges and of all images, including its
own: the image force of the plates. In the mapped plane a charge's own term
log(zeta - zeta_i) is not purely a self-interaction: it differs from
log(z - z_i) by a smooth part whose gradient, ``i pi / 2L``, is added back.

The map compresses distances by ``|d zeta / dz| = pi |zeta| / L``, which
changes by ``exp(pi Delta y / L)`` across the strip, so a softening length in
the physical plane has no fixed counterpart in the mapped plane. Softening is
therefore applied in the physical plane as a local correction: the strip's
Green's function is the free-space kernel plus a smooth harmonic part, and
softening only changes the free-space kernel near the charge, so pairs closer
than ``cutoff * softening`` get the difference between the softened and the
bare free-space interaction (the remainder beyond the cutoff is below 3% of
one pair's field there). Coincident charges are an error either way.

[View source](https://github.com/max-models/tree-d-3d/blob/devel/src/tree_d_3d/strip.py#L1-L1)

## tree_d_3d.strip.GroundedStripField

*class*

```python
class GroundedStripField
```

Field and potential of line charges between plates at x = 0 and x = L.

[View source](https://github.com/max-models/tree-d-3d/blob/devel/src/tree_d_3d/strip.py#L49-L248)

### tree_d_3d.strip.GroundedStripField.cutoff

*attribute* · *instance attribute*

```python
cutoff = float(cutoff)
```

[View source](https://github.com/max-models/tree-d-3d/blob/devel/src/tree_d_3d/strip.py#L79-L79)

### tree_d_3d.strip.GroundedStripField.k_e

*attribute* · *instance attribute*

```python
k_e = float(k_e)
```

[View source](https://github.com/max-models/tree-d-3d/blob/devel/src/tree_d_3d/strip.py#L72-L72)

### tree_d_3d.strip.GroundedStripField.leaf_capacity

*attribute* · *instance attribute*

```python
leaf_capacity = int(leaf_capacity)
```

[View source](https://github.com/max-models/tree-d-3d/blob/devel/src/tree_d_3d/strip.py#L71-L71)

### tree_d_3d.strip.GroundedStripField.method

*attribute* · *instance attribute*

```python
method = method
```

[View source](https://github.com/max-models/tree-d-3d/blob/devel/src/tree_d_3d/strip.py#L73-L73)

### tree_d_3d.strip.GroundedStripField.order

*attribute* · *instance attribute*

```python
order = int(order)
```

[View source](https://github.com/max-models/tree-d-3d/blob/devel/src/tree_d_3d/strip.py#L69-L69)

### tree_d_3d.strip.GroundedStripField.softening

*attribute* · *instance attribute*

```python
softening = float(softening)
```

[View source](https://github.com/max-models/tree-d-3d/blob/devel/src/tree_d_3d/strip.py#L78-L78)

### tree_d_3d.strip.GroundedStripField.theta

*attribute* · *instance attribute*

```python
theta = float(theta)
```

[View source](https://github.com/max-models/tree-d-3d/blob/devel/src/tree_d_3d/strip.py#L70-L70)

### tree_d_3d.strip.GroundedStripField.width

*attribute* · *instance attribute*

```python
width = float(width)
```

[View source](https://github.com/max-models/tree-d-3d/blob/devel/src/tree_d_3d/strip.py#L68-L68)

### tree_d_3d.strip.GroundedStripField.field_at_charges

*method*

```python
def field_at_charges(positions, charges, *, potential: bool = True) -> tuple[np.ndarray, np.ndarray]
```

Potential (V) and field (V/m, shape (2, N)) acting on every charge.

``positions`` has shape ``(2, N)``; ``charges`` are line charges per unit
length (C/m). With
``potential=False`` only the field is computed (the potential is
returned as zeros), which is cheaper.

[View source](https://github.com/max-models/tree-d-3d/blob/devel/src/tree_d_3d/strip.py#L134-L185)

### tree_d_3d.strip.GroundedStripField.potential

*method*

```python
def potential(positions, charges, points) -> np.ndarray
```

Potential (V) at arbitrary ``points`` strictly between the plates.

[View source](https://github.com/max-models/tree-d-3d/blob/devel/src/tree_d_3d/strip.py#L187-L189)

### tree_d_3d.strip.GroundedStripField.probe

*method*

```python
def probe(positions, charges, points) -> tuple[np.ndarray, np.ndarray]
```

Potential (V) and field (V/m, shape (2, M)) at ``points`` ``(2, M)``.

The points must lie strictly between the plates; they are probes, not
charges, so no self term applies.

[View source](https://github.com/max-models/tree-d-3d/blob/devel/src/tree_d_3d/strip.py#L191-L248)

### tree_d_3d.strip.GroundedStripField.to_plane

*method*

```python
def to_plane(positions) -> np.ndarray
```

Zeta = exp(i pi z / L), as a complex array; ``positions`` is ``(2, N)``.

[View source](https://github.com/max-models/tree-d-3d/blob/devel/src/tree_d_3d/strip.py#L81-L86)
