# multipoles
<p><a href="https://badge.fury.io/py/multipoles"> <img src="https://badge.fury.io/py/multipoles.svg?branch=kill_cache=1" alt="PyPI version"></a> <a href=""> <img src="https://github.com/maroba/multipoles/actions/workflows/python-app.yml/badge.svg" alt="build"></a><a href="https://codecov.io/gh/maroba/multipoles"> <img src="https://codecov.io/gh/maroba/multipoles/branch/master/graph/badge.svg?token=JNH9SP7BRG" alt=""></a></p>
*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](https://maroba.github.io/multipoles/).
## 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 <i>l</i>. 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:
```python
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:
```python
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)
```
Raw data
{
"_id": null,
"home_page": null,
"name": "multipoles",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": "Matthias Baer <matthias.r.baer@googlemail.com>",
"keywords": "multipole expansion, physics, scientific-computing",
"author": "Matthias Baer",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/54/ee/ffe280e7d6facb06e06d7b59a344563568285500d4dca9c902834258834d/multipoles-0.4.1.tar.gz",
"platform": null,
"description": "# multipoles\n<p><a href=\"https://badge.fury.io/py/multipoles\"> <img src=\"https://badge.fury.io/py/multipoles.svg?branch=kill_cache=1\" alt=\"PyPI version\"></a> <a href=\"\"> <img src=\"https://github.com/maroba/multipoles/actions/workflows/python-app.yml/badge.svg\" alt=\"build\"></a><a href=\"https://codecov.io/gh/maroba/multipoles\"> <img src=\"https://codecov.io/gh/maroba/multipoles/branch/master/graph/badge.svg?token=JNH9SP7BRG\" alt=\"\"></a></p>\n\n*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.\n\n\n## Installation\n\nSimply use `pip`:\n\n```\npip install --upgrade multipoles\n```\n\n## Documentation\n\nThe documentation is available [here](https://maroba.github.io/multipoles/).\n\n## Theory\n\nFor 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\n\n$$\\Phi(x,y,z)=\\int d^3r'\\frac{\\rho(r')}{|r-r'|}$$\n\nExamples of this are the electrostatic and Newtonian gravitational potential.\nIf 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}$):\n\n$$\\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)}}$$\n\nfor a exterior expansion, or\n\n$$\\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)}}$$\n\nfor 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.\n\nThe multipole moments for the exterior expansion are:\n\n$$q_{lm} = \\sqrt{\\frac{4\\pi}{2l+1}}\\int d^3 r' \\rho(r')r'^l Y^*_{lm}(\\theta', \\varphi')$$\n\nand the multipole moments for the interior expansion are:\n\n$$I_{lm} = \\sqrt{\\frac{4\\pi}{2l+1}}\\int d^3 r' \\frac{\\rho(r')}{r'^{l+1}} Y^*_{lm}(\\theta', \\varphi')$$\n\nThis approach is usually much faster because the contributions $\\Phi^{(l)}$ are getting smaller with increasing <i>l</i>. So we just have to calculate a few integrals for obtaining some $q_{lm}$ or $I_{lm}$.\n\nSome literature considers the $\\sqrt{\\frac{4\\pi}{2l+1}}$ as part of the definition of $Y_{lm}(\\theta, \\varphi)$.\n\n## Examples\n\n### Discrete Charge Distribution\n\nAs example for a discrete charge distribution we model two point charges with positive and negative unit charge located on the z-axis:\n\n```python\nfrom multipoles import MultipoleExpansion\n\n# Prepare the charge distribution dict for the MultipoleExpansion object:\n\ncharge_dist = {\n 'discrete': True, # point charges are discrete charge distributions\n 'charges': [\n {'q': 1, 'xyz': (0, 0, 1)},\n {'q': -1, 'xyz': (0, 0, -1)},\n ]\n}\n\nl_max = 2 # where to stop the infinite multipole sum; here we expand up to the quadrupole (l=2)\n\nPhi = MultipoleExpansion(charge_dist, l_max)\n\n# We can evaluate the multipole expanded potential at a given point like this:\n\nx, y, z = 30.5, 30.6, 30.7\nvalue = Phi(x, y, z)\n\n# The multipole moments are stored in a dict, where the keys are (l, m) and the values q_lm:\nPhi.multipole_moments\n```\n\n### Continuous Charge Distribution\n\nAs an example for a continuous charge distribution, we smear out the point charges from the previous\nexample:\n\n```python\nfrom multipoles import MultipoleExpansion\nimport numpy as np\n\n# First we set up our grid, a cube of length 10 centered at the origin:\n\nnpoints = 101\nedge = 10\nx, y, z = [np.linspace(-edge/2., edge/2., npoints)]*3\nXYZ = np.meshgrid(x, y, z, indexing='ij')\n\n\n# We model our smeared out charges as gaussian functions:\n\ndef gaussian(XYZ, xyz0, sigma):\n g = np.ones_like(XYZ[0])\n for k in range(3):\n g *= np.exp(-(XYZ[k] - xyz0[k])**2 / sigma**2)\n g *= (sigma**2*np.pi)**-1.5\n return g\n\nsigma = 1.5 # the width of our gaussians\n\n# Initialize the charge density rho, which is a 3D numpy array:\nrho = gaussian(XYZ, (0, 0, 1), sigma) - gaussian(XYZ, (0, 0, -1), sigma)\n\n\n# Prepare the charge distribution dict for the MultipoleExpansion object:\n\ncharge_dist = {\n 'discrete': False, # we have a continuous charge distribution here\n 'rho': rho,\n 'xyz': XYZ\n}\n\n# The rest is the same as for the discrete case:\n\nl_max = 2 # where to stop the infinite multipole sum; here we expand up to the quadrupole (l=2)\n\nPhi = MultipoleExpansion(charge_dist, l_max)\n\nx, y, z = 30.5, 30.6, 30.7\nvalue = Phi(x, y, z)\n```\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "A Python package for multipole expansions of electrostatic or gravitational potentials",
"version": "0.4.1",
"project_urls": {
"Homepage": "https://github.com/maroba/multipoles",
"Issues": "https://github.com/maroba/multipoles/issues",
"source": "https://github.com/maroba/multipoles",
"tracker": "https://github.com/maroba/multipoles/issues"
},
"split_keywords": [
"multipole expansion",
" physics",
" scientific-computing"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "0abe74f8abc9d9b9138c902e473a98a2959f4581ff812b087d084c70b026fbbd",
"md5": "7f939897d89763b9c5f129cb8ef0698b",
"sha256": "e0ee1ea0f0d1873bd6b9c0ac21bf83e2e7811417010bfab90bc67052327e1763"
},
"downloads": -1,
"filename": "multipoles-0.4.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "7f939897d89763b9c5f129cb8ef0698b",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 8834,
"upload_time": "2024-12-13T18:09:29",
"upload_time_iso_8601": "2024-12-13T18:09:29.253574Z",
"url": "https://files.pythonhosted.org/packages/0a/be/74f8abc9d9b9138c902e473a98a2959f4581ff812b087d084c70b026fbbd/multipoles-0.4.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "54eeffe280e7d6facb06e06d7b59a344563568285500d4dca9c902834258834d",
"md5": "8394603446d42800b4b0004c40c038b5",
"sha256": "824e29c63102b74f5f08929b857ba898f9332df898f5d9e5ec8c6cc726317b3c"
},
"downloads": -1,
"filename": "multipoles-0.4.1.tar.gz",
"has_sig": false,
"md5_digest": "8394603446d42800b4b0004c40c038b5",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 143716,
"upload_time": "2024-12-13T18:09:32",
"upload_time_iso_8601": "2024-12-13T18:09:32.162791Z",
"url": "https://files.pythonhosted.org/packages/54/ee/ffe280e7d6facb06e06d7b59a344563568285500d4dca9c902834258834d/multipoles-0.4.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-12-13 18:09:32",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "maroba",
"github_project": "multipoles",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "multipoles"
}