Hernquist & Ostriker Self-Consistent-Field-type potential

class galpy.potential.SCFPotential(amp=1.0, Acos=array([[[1]]]), Asin=None, a=1.0, tgrid=None, normalize=False, ro=None, vo=None)[source]

Class that implements the Hernquist & Ostriker (1992) Self-Consistent-Field-type potential. Note that we divide the amplitude by 2 such that \(Acos = \delta_{0n}\delta_{0l}\delta_{0m}\) and \(Asin = 0\) corresponds to Galpy’s Hernquist Potential.

\[\rho(r, \theta, \phi) = \frac{amp}{2}\sum_{n=0}^{\infty} \sum_{l=0}^{\infty} \sum_{m=0}^l N_{lm} P_{lm}(\cos(\theta)) \tilde{\rho}_{nl}(r) \left(A_{cos, nlm} \cos(m\phi) + A_{sin, nlm} \sin(m\phi)\right)\]

where

\[\tilde{\rho}_{nl}(r) = \frac{K_{nl}}{\sqrt{\pi}} \frac{(a r)^l}{(r/a) (a + r)^{2l + 3}} C_{n}^{2l + 3/2}(\xi)\]
\[\Phi(r, \theta, \phi) = \sum_{n=0}^{\infty} \sum_{l=0}^{\infty} \sum_{m=0}^l N_{lm} P_{lm}(\cos(\theta)) \tilde{\Phi}_{nl}(r) \left(A_{cos, nlm} \cos(m\phi) + A_{sin, nlm} \sin(m\phi)\right)\]

where

\[\tilde{\Phi}_{nl}(r) = -\sqrt{4 \pi}K_{nl} \frac{(ar)^l}{(a + r)^{2l + 1}} C_{n}^{2l + 3/2}(\xi)\]

where

\[\xi = \frac{r - a}{r + a} \qquad N_{lm} = \sqrt{\frac{2l + 1}{4\pi} \frac{(l - m)!}{(l + m)!}}(2 - \delta_{m0}) \qquad K_{nl} = \frac{1}{2} n (n + 4l + 3) + (l + 1)(2l + 1)\]

and \(P_{lm}\) is the Associated Legendre Polynomials whereas \(C_n^{\alpha}\) is the Gegenbauer polynomial.

Time-dependent potentials are supported by letting each expansion coefficient \(A_{\cos,nlm}\) and \(A_{\sin,nlm}\) be a function of time. This is enabled by passing a tgrid array together with either

  • Acos (and optionally Asin) as callables f(t) returning the (N,L,M) coefficient array at time t, or

  • Acos/Asin as precomputed (Nt,N,L,M) arrays sampled on tgrid,

or via from_density by passing a density that depends on time (i.e., dens(R, z, phi, t=0.)) together with a tgrid. In all cases the coefficients are sampled on tgrid and interpolated in time with a cubic spline, allowing efficient evaluation of the potential, forces, second derivatives, and density at arbitrary times within (or, by extrapolation, outside) the tgrid range in both Python and C (for orbit integration).

__init__(amp=1.0, Acos=array([[[1]]]), Asin=None, a=1.0, tgrid=None, normalize=False, ro=None, vo=None)[source]

Initialize a SCF Potential from a set of expansion coefficients (use SCFPotential.from_density to directly initialize from a density)

Parameters:
  • amp (float or Quantity, optional) – Amplitude to be applied to the potential (default: 1); can be a Quantity with units of mass or Gxmass.

  • Acos (numpy.ndarray or callable, optional) – The real part of the expansion coefficient (NxLxL matrix, or optionally NxLx1 if Asin=None). For a time-dependent potential (tgrid given), this is instead either a callable f(t) returning such an (N,L,L) / (N,L,1) array, or a precomputed (Nt,N,L,L) / (Nt,N,L,1) array sampled on tgrid.

  • Asin (numpy.ndarray or callable, optional) – The imaginary part of the expansion coefficient (NxLxL matrix or None). For a time-dependent potential, either a callable f(t) or a precomputed (Nt,N,L,L) array (or None for an axisymmetric potential).

  • a (float or Quantity, optional) – Scale length.

  • tgrid (numpy.ndarray or None, optional) – Time grid for time-dependent potentials. If provided, Acos and Asin are interpreted as time-dependent coefficients (callables f(t) or arrays sampled on tgrid): each coefficient is sampled on tgrid and interpolated in time with a cubic spline, allowing fast evaluation (in both Python and C) at arbitrary times within the tgrid range. Default: None (static potential).

  • normalize (bool or float, optional) – If True, normalize such that vc(1.,0.)=1., or, if given as a number, such that the force is this fraction of the force necessary to make vc(1.,0.)=1.

  • ro (float or Quantity, optional) – Distance scale for translation into internal units (default from configuration file).

  • vo (float or Quantity, optional) – Velocity scale for translation into internal units (default from configuration file).

Notes

  • 2016-05-13 - Written - Aladdin Seaifan (UofT)

  • 2026-07-02 - Added time-dependent support via tgrid - Bovy (UofT)

classmethod from_density(dens, N, L=None, a=1.0, symmetry=None, tgrid=None, radial_order=None, costheta_order=None, phi_order=None, ro=None, vo=None)[source]

Initialize an SCF Potential from a given density.

Parameters:
  • dens (function) – Density function that takes parameters R, z and phi; z and phi are optional for spherical profiles, phi is optional for axisymmetric profiles. The density function must take input positions in internal units (R/ro, z/ro), but can return densities in physical units. You can use the member dens of Potential instances or the density from evaluateDensities. For a time-dependent potential (tgrid given), the density may additionally accept a t keyword argument (e.g., dens(R, z, phi, t=0.)) or be a galpy Potential instance whose density is time-dependent.

  • N (int) – Number of radial basis functions.

  • L (int, optional) – Number of costheta basis functions; for non-axisymmetric profiles also sets the number of azimuthal (phi) basis functions to M = 2L+1).

  • a (float or Quantity, optional) – Expansion scale length.

  • symmetry ({'spherical','axisymmetry',None}, optional) – Symmetry of the profile to assume. None is the general, non-axisymmetric case.

  • tgrid (numpy.ndarray, Quantity, or None, optional) – Time grid for time-dependent potentials (a Quantity in physical time units, e.g. Gyr, is accepted). If provided, the expansion coefficients are computed at each time in tgrid (passing t to the density function when it accepts one) and interpolated in time, producing a time-dependent SCFPotential. Default: None (static potential; any t argument of the density is ignored).

  • radial_order (int, optional) – Number of sample points for the radial integral. If None, radial_order=max(20, N + 3/2L + 1).

  • costheta_order (int, optional) – Number of sample points of the costheta integral. If None, If costheta_order=max(20, L + 1).

  • phi_order (int, optional) – Number of sample points of the phi integral. If None, If costheta_order=max(20, L + 1).

  • ro (float or Quantity, optional) – Distance scale for translation into internal units (default from configuration file).

  • vo (float or Quantity, optional) – Velocity scale for translation into internal units (default from configuration file).

Return type:

SCFPotential object

Notes

  • Written - Jo Bovy (UofT) - 2022-06-20

  • 2026-07-02 - Added time-dependent support via tgrid - Bovy (UofT)

classmethod from_multipole(mult, N, a=1.0, radial_order=None, ro=None, vo=None)[source]

Initialize an SCFPotential from a MultipoleExpansionPotential.

Because both potentials expand the density in the same real spherical harmonics, the translation is purely radial: the density multipoles rho_lm(r) of the multipole expansion are projected onto the SCF radial basis (a set of 1D radial integrals), with no angular quadrature. The angular resolution (L, M) is taken from the multipole expansion; N sets the number of SCF radial basis functions. A time-dependent multipole expansion (built on a tgrid) produces a time-dependent SCFPotential on the same tgrid.

Parameters:
  • mult (MultipoleExpansionPotential) – The multipole expansion to translate.

  • N (int) – Number of radial basis functions of the SCF expansion.

  • a (float or Quantity, optional) – SCF expansion scale length.

  • radial_order (int, optional) – Number of sample points for the radial projection integral. If None, max(2*N+L, 200).

  • ro (float or Quantity, optional) – Distance scale for translation into internal units (default from configuration file).

  • vo (float or Quantity, optional) – Velocity scale for translation into internal units (default from configuration file).

Return type:

SCFPotential object

Notes

  • 2026-07-04 - Written - Bovy (UofT)

classmethod from_nbody(pos, N, L=None, mass=1.0, a=1.0, symmetry=None, tgrid=None, ro=None, vo=None)[source]

Initialize an SCFPotential from an N-body / particle representation.

Computes the expansion coefficients directly from a set of particle positions and masses (using scf_compute_coeffs_spherical_nbody and its axisymmetric and general counterparts). A time-dependent potential is built by passing multiple snapshots: give pos with shape [3,n,nt] together with a tgrid of length nt, and the coefficients are computed at each snapshot and interpolated in time (analogous to the time-dependent from_density). The particle sum is accumulated in batches so that building from a very large number of particles stays memory-bounded.

Parameters:
  • pos (numpy.ndarray or Quantity) – Positions of the particles in rectangular coordinates, with shape [3,n] (static) or [3,n,nt] (time-dependent, one snapshot per time in tgrid).

  • N (int) – Number of radial basis functions.

  • L (int, optional) – Number of costheta basis functions; for non-axisymmetric profiles also sets the number of azimuthal (phi) basis functions to M = 2L+1. Required unless symmetry='spherical'.

  • mass (float, numpy.ndarray, or Quantity, optional) – Particle masses: a scalar (all equal), an array of shape [n], or, for the time-dependent case, an array of shape [n,nt]. Default 1.0.

  • a (float or Quantity, optional) – Expansion scale length.

  • symmetry ({'spherical','axisymmetry',None}, optional) – Symmetry to assume. None is the general, non-axisymmetric case.

  • tgrid (numpy.ndarray, Quantity, or None, optional) – Time grid for a time-dependent potential (a Quantity in physical time units, e.g. Gyr, is accepted). If provided, pos must have shape [3,n,len(tgrid)] and the coefficients are computed at each snapshot and interpolated in time. Default: None (static).

  • ro (float or Quantity, optional) – Distance scale for translation into internal units (default from configuration file).

  • vo (float or Quantity, optional) – Velocity scale for translation into internal units (default from configuration file).

Return type:

SCFPotential object

Notes

  • 2026-07-04 - Written - Bovy (UofT)