Skip to content

API Reference

multipoles.MultipoleExpansion

Bases: object

Perform a multipole expansion for a given charge or mass distribution.

Determines the spherical multipole moments of the given distribution and can calculate the solution of the electrostatic or gravitational potential based on the multipole expansion.

Attributes:

Name Type Description
l_max int

The maximum multipole moment of the expansion.

exterior bool

Whether this is an exterior expansion.

interior bool

Whether this is an interior expansion.

center ndarray

The expansion center.

center_of_charge ndarray

The center of charge, weighted by the absolute values of the charges.

total_charge float

The total charge.

validity_radius float

The exterior expansion converges outside, the interior expansion inside the sphere of this radius about the expansion center.

multipole_moments dict

The multipole moments q_lm (exterior) or I_lm (interior), with keys (l, m).

dipole_moment property

The dipole moment vector p = Σ q (r - center), about the expansion center.

quadrupole_moment property

The traceless quadrupole moment tensor, about the expansion center.

Q_ij = Σ q (3 x_i x_j - r^2 δ_ij), with x = r - center, so that the potential is Φ = q / r + p·x / r^3 + Σ_ij Q_ij x_i x_j / (2 r^5) + ...

multipole_moments_array property

The multipole moments q_lm as array of shape (l_max + 1, 2 l_max + 1).

Index it as [l, m], with negative m wrapping around as usual in Python. Entries with |m| > l are zero.

__init__(charge_dist, l_max, exterior=None, interior=None, center=None, charge_tol=1e-06)

Create a MultipoleExpansion object for a given charge or mass distribution.

Parameters:

Name Type Description Default
charge_dist dict

Description of the charge distribution.

For discrete charge distributions (point charges) the dict must contain exactly the following items:

  • 'discrete': True
  • 'charges': a list of dicts, one for each point charge, with the items
    • 'q': the charge (positive or negative number)
    • 'xyz': the location of the charge in cartesian coordinates (tuple, list or array of length 3)

For continuous charge distributions (charge density) the dict must contain exactly the following items:

  • 'discrete': False
  • 'rho': the charge density on a grid (3D numpy array)
  • 'xyz': the grid, a 3-tuple of 3D coordinate arrays as returned by np.meshgrid(x, y, z, indexing='ij')
required
l_max int

The maximum multipole moment to consider (0 = monopole, 1 = dipole, etc.).

required
exterior bool

Whether to perform an exterior expansion (default). If False, the interior expansion is used.

None
interior bool

Whether to perform an interior expansion. Alternative to exterior; only one of both needs to be given.

None
center array_like

The point (x, y, z) about which to expand. Defaults to the center of charge, weighted by the absolute values of the charges.

None
charge_tol float

Fraction of the total absolute charge of a continuous distribution that may be ignored when determining the region where the expansion is valid (see validity_radius). Smooth densities like gaussians never vanish exactly, so without such a tolerance no point of the grid would count as outside the distribution.

1e-06

The exterior expansion converges only outside the smallest sphere about the center that contains all charge, the interior expansion only inside the largest sphere about the center that contains no charge. The radius of this sphere is stored in validity_radius. Evaluating the expansion at points where it does not converge emits an ExpansionValidityWarning. To turn these warnings into errors, use warnings.simplefilter("error", ExpansionValidityWarning).

Raises:

Type Description
InvalidChargeDistributionException

If charge_dist is malformed, the total absolute charge is zero, or the grid is invalid.

InvalidExpansionException

If interior and exterior contradict each other, or an interior expansion is requested with charge at the expansion center.

ValueError

If center is not a finite point in 3D.

Examples:

A discrete charge distribution: two point charges with positive and negative unit charge located on the z-axis:

>>> from multipoles import MultipoleExpansion
>>> charge_dist = {
...     'discrete': True,
...     'charges': [
...         {'q': 1, 'xyz': (0, 0, 1)},
...         {'q': -1, 'xyz': (0, 0, -1)},
...     ],
... }
>>> Phi = MultipoleExpansion(charge_dist, l_max=2)

A continuous charge distribution: the same charges, smeared out as gaussians on a cubic grid of edge length 16 centered at the origin:

>>> import numpy as np
>>> x, y, z = [np.linspace(-8, 8, 81)] * 3
>>> XYZ = np.meshgrid(x, y, z, indexing='ij')
>>> def gaussian(XYZ, xyz0, sigma):
...     g = np.ones_like(XYZ[0])
...     for k in range(3):
...         g *= np.exp(-(XYZ[k] - xyz0[k])**2 / sigma**2)
...     return g * (sigma**2 * np.pi)**-1.5
>>> rho = gaussian(XYZ, (0, 0, 1), 1.5) - gaussian(XYZ, (0, 0, -1), 1.5)
>>> Phi = MultipoleExpansion({'discrete': False, 'rho': rho, 'xyz': XYZ}, l_max=2)

Evaluate the potential at arbitrary points with Phi(x, y, z) or on the grid points of a continuous distribution with Phi[...], and the field with Phi.field(x, y, z). The examples of these methods use Phi and gaussian from here.

__call__(*xyz, l_max=None)

Evaluate the multipole expansion at points with given coordinates.

Parameters:

Name Type Description Default
*xyz float | array_like

The x, y, z coordinates of the points where to evaluate the expansion. If three numbers are given, the expansion is evaluated at the single point (x, y, z). If arrays are given, it is evaluated at each point (x[i], y[i], z[i]). Arrays may have any shape; numbers and arrays are broadcast against each other.

()
l_max int

Only use contributions up to this angular momentum. Defaults to the l_max the expansion was created with, and must not exceed it.

None

Returns:

Type Description
float | ndarray

The value of the potential: a float for a single point, otherwise an array of the broadcast shape of the coordinates.

Warns:

Type Description
ExpansionValidityWarning

If some of the points lie where the expansion does not converge.

Examples:

>>> value = Phi(30.5, 30.6, 30.7)
>>> value = Phi(30.5, 30.6, 30.7, l_max=1)  # monopole and dipole only
>>> values = Phi(np.linspace(10, 20, 5), 0, 0)  # 5 points on the x-axis

__getitem__(key)

Evaluate the multipole expansion on grid points of the charge distribution.

Only available for continuous charge distributions.

Parameters:

Name Type Description Default
key index expression | array_like

Slices, indices or a boolean mask selecting the points of the grid charge_dist['xyz'] at which to evaluate the expansion.

required

Returns:

Type Description
ndarray

The values of the multipole expansion, in the shape defined by key.

Raises:

Type Description
TypeError

If the charge distribution is discrete.

Warns:

Type Description
ExpansionValidityWarning

If some of the points lie where the expansion does not converge.

Examples:

>>> x, y, z = [np.linspace(-8, 8, 81)] * 3
>>> X, Y, Z = np.meshgrid(x, y, z, indexing='ij')
>>> rho = gaussian((X, Y, Z), (0, 0, 0), 1.5)
>>> mpe = MultipoleExpansion({'discrete': False, 'rho': rho, 'xyz': (X, Y, Z)}, l_max=3)

Evaluate on the plane z = -8 (an 81x81 array):

>>> plane = mpe[:, :, 0]

Evaluate only on the boundary of the grid, using a boolean mask:

>>> mask = np.ones_like(rho, dtype=bool)
>>> mask[1:-1, 1:-1, 1:-1] = False
>>> boundary_values = np.zeros_like(rho)
>>> boundary_values[mask] = mpe[mask]

field(*xyz, l_max=None)

Evaluate the field E = -∇Φ of the multipole expansion.

The derivatives are computed analytically from the multipole moments.

Parameters:

Name Type Description Default
*xyz float | array_like

The x, y, z coordinates of the points, as for __call__.

()
l_max int

Only use contributions up to this angular momentum, as for __call__.

None

Returns:

Type Description
ndarray

The field components (E_x, E_y, E_z), stacked along the first axis: shape (3,) for a single point, otherwise (3, *shape) where shape is the broadcast shape of the coordinates.

Warns:

Type Description
ExpansionValidityWarning

If some of the points lie where the expansion does not converge.

Examples:

>>> Ex, Ey, Ez = Phi.field(30.5, 30.6, 30.7)
>>> E = Phi.field(np.linspace(10, 20, 5), 0, 0)  # shape (3, 5)

is_valid(*xyz)

Check where the multipole expansion converges.

Parameters:

Name Type Description Default
*xyz float | array_like

The x, y, z coordinates of the points, as for __call__.

()

Returns:

Type Description
bool | ndarray

Whether the expansion converges at each point, i.e. whether the point lies outside (exterior expansion) or inside (interior expansion) the sphere of radius validity_radius about the expansion center.

Examples:

>>> X, Y, Z = Phi.external_coords
>>> valid = Phi.is_valid(X, Y, Z)  # boolean mask of the grid
>>> values = np.full(valid.shape, np.nan)
>>> values[valid] = Phi[valid]

multipoles.ExpansionValidityWarning

Bases: UserWarning

Emitted when the expansion is evaluated at points where it does not converge.

multipoles.InvalidChargeDistributionException

Bases: Exception

Raised when the charge distribution is malformed or cannot be expanded, e.g. because keys are missing, its total absolute charge is zero, or the grid is invalid.

multipoles.InvalidExpansionException

Bases: Exception

Raised when the requested expansion is contradictory or impossible, e.g. an interior expansion with charge at the expansion center.