123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 |
- from sympy.core.expr import Expr
- from sympy.core.function import Function, ArgumentIndexError
- from sympy.core.numbers import I, pi
- from sympy.core.singleton import S
- from sympy.core.symbol import Dummy
- from sympy.functions import assoc_legendre
- from sympy.functions.combinatorial.factorials import factorial
- from sympy.functions.elementary.complexes import Abs, conjugate
- from sympy.functions.elementary.exponential import exp
- from sympy.functions.elementary.miscellaneous import sqrt
- from sympy.functions.elementary.trigonometric import sin, cos, cot
- _x = Dummy("x")
- class Ynm(Function):
- r"""
- Spherical harmonics defined as
- .. math::
- Y_n^m(\theta, \varphi) := \sqrt{\frac{(2n+1)(n-m)!}{4\pi(n+m)!}}
- \exp(i m \varphi)
- \mathrm{P}_n^m\left(\cos(\theta)\right)
- Explanation
- ===========
- ``Ynm()`` gives the spherical harmonic function of order $n$ and $m$
- in $\theta$ and $\varphi$, $Y_n^m(\theta, \varphi)$. The four
- parameters are as follows: $n \geq 0$ an integer and $m$ an integer
- such that $-n \leq m \leq n$ holds. The two angles are real-valued
- with $\theta \in [0, \pi]$ and $\varphi \in [0, 2\pi]$.
- Examples
- ========
- >>> from sympy import Ynm, Symbol, simplify
- >>> from sympy.abc import n,m
- >>> theta = Symbol("theta")
- >>> phi = Symbol("phi")
- >>> Ynm(n, m, theta, phi)
- Ynm(n, m, theta, phi)
- Several symmetries are known, for the order:
- >>> Ynm(n, -m, theta, phi)
- (-1)**m*exp(-2*I*m*phi)*Ynm(n, m, theta, phi)
- As well as for the angles:
- >>> Ynm(n, m, -theta, phi)
- Ynm(n, m, theta, phi)
- >>> Ynm(n, m, theta, -phi)
- exp(-2*I*m*phi)*Ynm(n, m, theta, phi)
- For specific integers $n$ and $m$ we can evaluate the harmonics
- to more useful expressions:
- >>> simplify(Ynm(0, 0, theta, phi).expand(func=True))
- 1/(2*sqrt(pi))
- >>> simplify(Ynm(1, -1, theta, phi).expand(func=True))
- sqrt(6)*exp(-I*phi)*sin(theta)/(4*sqrt(pi))
- >>> simplify(Ynm(1, 0, theta, phi).expand(func=True))
- sqrt(3)*cos(theta)/(2*sqrt(pi))
- >>> simplify(Ynm(1, 1, theta, phi).expand(func=True))
- -sqrt(6)*exp(I*phi)*sin(theta)/(4*sqrt(pi))
- >>> simplify(Ynm(2, -2, theta, phi).expand(func=True))
- sqrt(30)*exp(-2*I*phi)*sin(theta)**2/(8*sqrt(pi))
- >>> simplify(Ynm(2, -1, theta, phi).expand(func=True))
- sqrt(30)*exp(-I*phi)*sin(2*theta)/(8*sqrt(pi))
- >>> simplify(Ynm(2, 0, theta, phi).expand(func=True))
- sqrt(5)*(3*cos(theta)**2 - 1)/(4*sqrt(pi))
- >>> simplify(Ynm(2, 1, theta, phi).expand(func=True))
- -sqrt(30)*exp(I*phi)*sin(2*theta)/(8*sqrt(pi))
- >>> simplify(Ynm(2, 2, theta, phi).expand(func=True))
- sqrt(30)*exp(2*I*phi)*sin(theta)**2/(8*sqrt(pi))
- We can differentiate the functions with respect
- to both angles:
- >>> from sympy import Ynm, Symbol, diff
- >>> from sympy.abc import n,m
- >>> theta = Symbol("theta")
- >>> phi = Symbol("phi")
- >>> diff(Ynm(n, m, theta, phi), theta)
- m*cot(theta)*Ynm(n, m, theta, phi) + sqrt((-m + n)*(m + n + 1))*exp(-I*phi)*Ynm(n, m + 1, theta, phi)
- >>> diff(Ynm(n, m, theta, phi), phi)
- I*m*Ynm(n, m, theta, phi)
- Further we can compute the complex conjugation:
- >>> from sympy import Ynm, Symbol, conjugate
- >>> from sympy.abc import n,m
- >>> theta = Symbol("theta")
- >>> phi = Symbol("phi")
- >>> conjugate(Ynm(n, m, theta, phi))
- (-1)**(2*m)*exp(-2*I*m*phi)*Ynm(n, m, theta, phi)
- To get back the well known expressions in spherical
- coordinates, we use full expansion:
- >>> from sympy import Ynm, Symbol, expand_func
- >>> from sympy.abc import n,m
- >>> theta = Symbol("theta")
- >>> phi = Symbol("phi")
- >>> expand_func(Ynm(n, m, theta, phi))
- sqrt((2*n + 1)*factorial(-m + n)/factorial(m + n))*exp(I*m*phi)*assoc_legendre(n, m, cos(theta))/(2*sqrt(pi))
- See Also
- ========
- Ynm_c, Znm
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Spherical_harmonics
- .. [2] https://mathworld.wolfram.com/SphericalHarmonic.html
- .. [3] https://functions.wolfram.com/Polynomials/SphericalHarmonicY/
- .. [4] https://dlmf.nist.gov/14.30
- """
- @classmethod
- def eval(cls, n, m, theta, phi):
- # Handle negative index m and arguments theta, phi
- if m.could_extract_minus_sign():
- m = -m
- return S.NegativeOne**m * exp(-2*I*m*phi) * Ynm(n, m, theta, phi)
- if theta.could_extract_minus_sign():
- theta = -theta
- return Ynm(n, m, theta, phi)
- if phi.could_extract_minus_sign():
- phi = -phi
- return exp(-2*I*m*phi) * Ynm(n, m, theta, phi)
- # TODO Add more simplififcation here
- def _eval_expand_func(self, **hints):
- n, m, theta, phi = self.args
- rv = (sqrt((2*n + 1)/(4*pi) * factorial(n - m)/factorial(n + m)) *
- exp(I*m*phi) * assoc_legendre(n, m, cos(theta)))
- # We can do this because of the range of theta
- return rv.subs(sqrt(-cos(theta)**2 + 1), sin(theta))
- def fdiff(self, argindex=4):
- if argindex == 1:
- # Diff wrt n
- raise ArgumentIndexError(self, argindex)
- elif argindex == 2:
- # Diff wrt m
- raise ArgumentIndexError(self, argindex)
- elif argindex == 3:
- # Diff wrt theta
- n, m, theta, phi = self.args
- return (m * cot(theta) * Ynm(n, m, theta, phi) +
- sqrt((n - m)*(n + m + 1)) * exp(-I*phi) * Ynm(n, m + 1, theta, phi))
- elif argindex == 4:
- # Diff wrt phi
- n, m, theta, phi = self.args
- return I * m * Ynm(n, m, theta, phi)
- else:
- raise ArgumentIndexError(self, argindex)
- def _eval_rewrite_as_polynomial(self, n, m, theta, phi, **kwargs):
- # TODO: Make sure n \in N
- # TODO: Assert |m| <= n ortherwise we should return 0
- return self.expand(func=True)
- def _eval_rewrite_as_sin(self, n, m, theta, phi, **kwargs):
- return self.rewrite(cos)
- def _eval_rewrite_as_cos(self, n, m, theta, phi, **kwargs):
- # This method can be expensive due to extensive use of simplification!
- from sympy.simplify import simplify, trigsimp
- # TODO: Make sure n \in N
- # TODO: Assert |m| <= n ortherwise we should return 0
- term = simplify(self.expand(func=True))
- # We can do this because of the range of theta
- term = term.xreplace({Abs(sin(theta)):sin(theta)})
- return simplify(trigsimp(term))
- def _eval_conjugate(self):
- # TODO: Make sure theta \in R and phi \in R
- n, m, theta, phi = self.args
- return S.NegativeOne**m * self.func(n, -m, theta, phi)
- def as_real_imag(self, deep=True, **hints):
- # TODO: Handle deep and hints
- n, m, theta, phi = self.args
- re = (sqrt((2*n + 1)/(4*pi) * factorial(n - m)/factorial(n + m)) *
- cos(m*phi) * assoc_legendre(n, m, cos(theta)))
- im = (sqrt((2*n + 1)/(4*pi) * factorial(n - m)/factorial(n + m)) *
- sin(m*phi) * assoc_legendre(n, m, cos(theta)))
- return (re, im)
- def _eval_evalf(self, prec):
- # Note: works without this function by just calling
- # mpmath for Legendre polynomials. But using
- # the dedicated function directly is cleaner.
- from mpmath import mp, workprec
- n = self.args[0]._to_mpmath(prec)
- m = self.args[1]._to_mpmath(prec)
- theta = self.args[2]._to_mpmath(prec)
- phi = self.args[3]._to_mpmath(prec)
- with workprec(prec):
- res = mp.spherharm(n, m, theta, phi)
- return Expr._from_mpmath(res, prec)
- def Ynm_c(n, m, theta, phi):
- r"""
- Conjugate spherical harmonics defined as
- .. math::
- \overline{Y_n^m(\theta, \varphi)} := (-1)^m Y_n^{-m}(\theta, \varphi).
- Examples
- ========
- >>> from sympy import Ynm_c, Symbol, simplify
- >>> from sympy.abc import n,m
- >>> theta = Symbol("theta")
- >>> phi = Symbol("phi")
- >>> Ynm_c(n, m, theta, phi)
- (-1)**(2*m)*exp(-2*I*m*phi)*Ynm(n, m, theta, phi)
- >>> Ynm_c(n, m, -theta, phi)
- (-1)**(2*m)*exp(-2*I*m*phi)*Ynm(n, m, theta, phi)
- For specific integers $n$ and $m$ we can evaluate the harmonics
- to more useful expressions:
- >>> simplify(Ynm_c(0, 0, theta, phi).expand(func=True))
- 1/(2*sqrt(pi))
- >>> simplify(Ynm_c(1, -1, theta, phi).expand(func=True))
- sqrt(6)*exp(I*(-phi + 2*conjugate(phi)))*sin(theta)/(4*sqrt(pi))
- See Also
- ========
- Ynm, Znm
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Spherical_harmonics
- .. [2] https://mathworld.wolfram.com/SphericalHarmonic.html
- .. [3] https://functions.wolfram.com/Polynomials/SphericalHarmonicY/
- """
- return conjugate(Ynm(n, m, theta, phi))
- class Znm(Function):
- r"""
- Real spherical harmonics defined as
- .. math::
- Z_n^m(\theta, \varphi) :=
- \begin{cases}
- \frac{Y_n^m(\theta, \varphi) + \overline{Y_n^m(\theta, \varphi)}}{\sqrt{2}} &\quad m > 0 \\
- Y_n^m(\theta, \varphi) &\quad m = 0 \\
- \frac{Y_n^m(\theta, \varphi) - \overline{Y_n^m(\theta, \varphi)}}{i \sqrt{2}} &\quad m < 0 \\
- \end{cases}
- which gives in simplified form
- .. math::
- Z_n^m(\theta, \varphi) =
- \begin{cases}
- \frac{Y_n^m(\theta, \varphi) + (-1)^m Y_n^{-m}(\theta, \varphi)}{\sqrt{2}} &\quad m > 0 \\
- Y_n^m(\theta, \varphi) &\quad m = 0 \\
- \frac{Y_n^m(\theta, \varphi) - (-1)^m Y_n^{-m}(\theta, \varphi)}{i \sqrt{2}} &\quad m < 0 \\
- \end{cases}
- Examples
- ========
- >>> from sympy import Znm, Symbol, simplify
- >>> from sympy.abc import n, m
- >>> theta = Symbol("theta")
- >>> phi = Symbol("phi")
- >>> Znm(n, m, theta, phi)
- Znm(n, m, theta, phi)
- For specific integers n and m we can evaluate the harmonics
- to more useful expressions:
- >>> simplify(Znm(0, 0, theta, phi).expand(func=True))
- 1/(2*sqrt(pi))
- >>> simplify(Znm(1, 1, theta, phi).expand(func=True))
- -sqrt(3)*sin(theta)*cos(phi)/(2*sqrt(pi))
- >>> simplify(Znm(2, 1, theta, phi).expand(func=True))
- -sqrt(15)*sin(2*theta)*cos(phi)/(4*sqrt(pi))
- See Also
- ========
- Ynm, Ynm_c
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Spherical_harmonics
- .. [2] https://mathworld.wolfram.com/SphericalHarmonic.html
- .. [3] https://functions.wolfram.com/Polynomials/SphericalHarmonicY/
- """
- @classmethod
- def eval(cls, n, m, theta, phi):
- if m.is_positive:
- zz = (Ynm(n, m, theta, phi) + Ynm_c(n, m, theta, phi)) / sqrt(2)
- return zz
- elif m.is_zero:
- return Ynm(n, m, theta, phi)
- elif m.is_negative:
- zz = (Ynm(n, m, theta, phi) - Ynm_c(n, m, theta, phi)) / (sqrt(2)*I)
- return zz
|