Git Product home page Git Product logo

multipoles's Introduction

multipoles

PyPI version build

multipoles is a Python package for multipole expansions of the solutions of the Poisson equation (e.g. electrostatic or gravitational potentials). It can handle discrete and continuous charge or mass distributions.

Installation

Simply use pip:

pip install --upgrade multipoles

Documentation

The documentation is available here.

Theory

For a given function $\rho(x,y,z)$, the solution $\Phi(x,y,z)$ of the Poisson equation $\nabla^2\Phi=-4\pi \rho$ with vanishing Dirichlet boundary conditions at infinity is

$$\Phi(x,y,z)=\int d^3r'\frac{\rho(r')}{|r-r'|}$$

Examples of this are the electrostatic and Newtonian gravitational potential. If you need to evaluate $\Phi(x,y,z)$ at many points, calculating the integral for each point is computationally expensive. As a faster alternative, we can express $\Phi(x,y,z)$ in terms of the multipole moments $q_{lm}$ or $I_{lm}$ (note some literature uses the subscripts $(\cdot)_{nm}$):

$$\Phi(x,y,z)=\sum_{l=0}^\infty\underbrace{\sqrt{\frac{4\pi}{2l+1}}\sum_{m=-l}^lY_{lm}(\theta, \varphi)\frac{q_{lm}}{r^{l+1}}}_{\Phi^{(l)}}$$

for a exterior expansion, or

$$\Phi(x,y,z)=\sum_{l=0}^\infty\underbrace{\sqrt{\frac{4\pi}{2l+1}}\sum_{m=-l}^lY_{lm}(\theta, \varphi)I_{lm}r^{l}}_{\Phi^{(l)}}$$

for an interior expansion; where $r, \theta, \varphi$ are the usual spherical coordinates corresponding to the cartesian coordinates $x, y, z$ and $Y_{lm}(\theta, \varphi)$ are the spherical harmonics.

The multipole moments for the exterior expansion are:

$$q_{lm} = \sqrt{\frac{4\pi}{2l+1}}\int d^3 r' \rho(r')r'^l Y^*_{lm}(\theta', \varphi')$$

and the multipole moments for the interior expansion are:

$$I_{lm} = \sqrt{\frac{4\pi}{2l+1}}\int d^3 r' \frac{\rho(r')}{r'^{l+1}} Y^*_{lm}(\theta', \varphi')$$

This approach is usually much faster because the contributions $\Phi^{(l)}$ are getting smaller with increasing l. So we just have to calculate a few integrals for obtaining some $q_{lm}$ or $I_{lm}$.

Some literature considers the $\sqrt{\frac{4\pi}{2l+1}}$ as part of the definition of $Y_{lm}(\theta, \varphi)$.

Examples

Discrete Charge Distribution

As example for a discrete charge distribution we model two point charges with positive and negative unit charge located on the z-axis:

from multipoles import MultipoleExpansion

# Prepare the charge distribution dict for the MultipoleExpansion object:

charge_dist = {
    'discrete': True,     # point charges are discrete charge distributions
    'charges': [
        {'q': 1, 'xyz': (0, 0, 1)},
        {'q': -1, 'xyz': (0, 0, -1)},
    ]
}

l_max = 2   # where to stop the infinite multipole sum; here we expand up to the quadrupole (l=2)

Phi = MultipoleExpansion(charge_dist, l_max)

# We can evaluate the multipole expanded potential at a given point like this:

x, y, z = 30.5, 30.6, 30.7
value = Phi(x, y, z)

# The multipole moments are stored in a dict, where the keys are (l, m) and the values q_lm:
Phi.multipole_moments

Continuous Charge Distribution

As an example for a continuous charge distribution, we smear out the point charges from the previous example:

from multipoles import MultipoleExpansion
import numpy as np

# First we set up our grid, a cube of length 10 centered at the origin:

npoints = 101
edge = 10
x, y, z = [np.linspace(-edge/2., edge/2., npoints)]*3
XYZ = np.meshgrid(x, y, z, indexing='ij')


# We model our smeared out charges as gaussian functions:

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)
    g *= (sigma**2*np.pi)**-1.5
    return g

sigma = 1.5   # the width of our gaussians

# Initialize the charge density rho, which is a 3D numpy array:
rho = gaussian(XYZ, (0, 0, 1), sigma) - gaussian(XYZ, (0, 0, -1), sigma)


# Prepare the charge distribution dict for the MultipoleExpansion object:

charge_dist = {
    'discrete': False,     # we have a continuous charge distribution here
    'rho': rho,
    'xyz': XYZ
}

# The rest is the same as for the discrete case:

l_max = 2   # where to stop the infinite multipole sum; here we expand up to the quadrupole (l=2)

Phi = MultipoleExpansion(charge_dist, l_max)

x, y, z = 30.5, 30.6, 30.7
value = Phi(x, y, z)

multipoles's People

Contributors

dmadisetti avatar maroba avatar matthias-baer avatar nordic-node avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

multipoles's Issues

Inaccurate results

Describe the bug

The packages give inaccurate answers for some charge distributions.

To Reproduce

Consider the following code.

import numpy as np
from multipoles import MultipoleExpansion

# Prepare the charge distribution dict for the MultipoleExpansion object:
charge_dist = {
    'discrete': True,     # point charges are discrete charge distributions
    'charges': [
        {'q': 1, 'xyz': (0, 0, 1)},
        {'q': -1.53, 'xyz': (0, 0, -1)},
        {'q': -1, 'xyz': (0, 1.3, -1)}
    ]
}
def true_potential(x, y, z):
    sum_ = 0.0
    
    for c in charge_dist['charges']:
        p = np.array(c['xyz'])
        r = np.linalg.norm(np.array([x,y,z]) - p)
        sum_ += c['q']/r
    
    return sum_

x, y, z = 30.5, 30.6, 30.7
Phi = MultipoleExpansion(charge_dist, 15)
print(true_potential(x, y, z))
print(Phi(x, y, z))
print('Accuracy: ', Phi(x, y, z)/true_potential(x, y, z) - 1)

The accuracy is only 5e-3, and does not improve when increasing l_max.

Expected behavior

I expect an accuracy down to floating point limit (1e-15) for large value of l_max.

Desktop (please complete the following information):

Linux, Debian, Python 3.9

Additional context

I believe there is a sign error related to the spherical harmonics functions used by this package. To be specific, I believe for some values of l, m the sign of the imaginary part of the spherical harmonics functions used is incorrect. I'm still investigating.. I will let you know when I figure it out.

Phi(x,y,z) gives different answers if entire system is shifted

charge_dist = {'discrete': True, 'charges': [{'q': 1, 'xyz': (0, 0, 0.5)}, {'q': -1, 'xyz': (0, 0, -1)}]}
l_max = 3
Phi = MultipoleExpansion(charge_dist, l_max)
Phi(0,0,0)

and

charge_dist = {'discrete': True, 'charges': [{'q': 1, 'xyz': (5, 5, 5.5)}, {'q': -1, 'xyz': (5, 5, 4)}]}
l_max = 3
Phi = MultipoleExpansion(charge_dist, l_max)
Phi(5,5,5)

give different answers despite being the same arrangement of charges around the evaluation point, is this correct?

Document error bounds

So Beatson/Greengard list the L1 error as bounded here:

$$L1 < frac{\sum_i |q_i|}{r-a} (\frac{a}{r})^{lmax+1}$$

But this is the exterior expansion. I don't have as many resources on the interior expansion.

I think it would be nice to show

  • This project keeps these bound
  • Add documentation on the bounds

Feature request: evaluate multipole expansion on single axis

Discussed in #10

Originally posted by nordic-node July 14, 2023
Hi,

instead of evaluating the multipole expansion point by point, like

phi = np.array([mpe(r, 0, 0, l_max=l_max) for r in rr])

where mpe is a multipole expansion object.

I would rather like to write something like this:

phi = mpe(rr, 0, 0, l_max=l_max)

As far as I can see, the call function just allows to pass a point. and the getitem function wants slices or masks on the same grid
that was used for the charge distribution. Is there a possibility to do the same for outside of that grid?

Cheers, Sara

Weird Harmonic Artifacts

Discussed in #15

Originally posted by levi2234 December 5, 2023

Astrometric Halo Merger artifacts

While applying the Multipole Expansion on a set of 2 Gaussian distributed clusters I encountered a weird artifact dependent on the value of L_min. When the Gaussian cluster has a separation of multiple cluster standard deviations a deep potential well occurs exactly between the two clusters. As the clusters move inward this well resolves itself. I would like to know if this is a problem that should be adressed or if it is expected behaviour. If it is the latter I think I must look to some other solution.

To sketch the problem I have included a video mock collision for multiple modes for both haloes and single points. As a reference I have also included an discrete example of what I would expect the potential field to look like.

HaloCollisionMultipoleEvolution_l_max_4.mp4
HaloCollisionMultipoleEvolution_l_max_8.mp4
HaloCollisionMultipoleEvolution_l_max_12.mp4
two_particles.test.12.mp4
two_particles.test.mp4
HaloCollisionDiscreteEvolution.mp4
HaloCollisionMultipoleEvolution_l_max_1.mp4
HaloCollisionMultipoleEvolution_l_max_2.mp4
HaloCollisionMultipoleEvolution_l_max_3.mp4

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.