Fuzzy-dark-matter dynamical friction

As an example of using the FDMDynamicalFrictionForce detailed below, consider a dwarf galaxy:

import numpy
from astropy import units as u
from galpy.potential import NFWPotential
M = 1e9  # Msol
rs = 1.57  # kpc
c = 18.4
A_NFW = numpy.log(1 + c) - c / (1 + c)
NFWHalo = NFWPotential(amp=M / A_NFW * u.Msun, a=rs * u.kpc)

and a globular cluster within it:

Mobj = 1e6  # Msol
rhm = 1e-2  # kpc

Regular Chandrasekhar dynamical friction force can be computed using:

from galpy.potential import ChandrasekharDynamicalFrictionForce
cdf = ChandrasekharDynamicalFrictionForce(GMs=Mobj * u.Msun, rhm=rhm * u.kpc, dens=NFWHalo)

The FDM dynamical friction force for a mass of \(3\times 10^{-22}\,\mathrm{eV}\) can be computed using:

from galpy.potential import FDMDynamicalFrictionForce
m = 3e-22  # mass of the fuzzy particle in eV
fdf = FDMDynamicalFrictionForce(GMs=Mobj * u.Msun, rhm=rhm * u.kpc, dens=NFWHalo, m=m * u.eV)

We then set up an orbit and integrate it with both the CDM Chandrasekhar friction force and the FDM friction force as well as with no friction at all:

from galpy.orbit import Orbit
# initial conditions
R = 0.5
z = 0.0
phi = 0.0
Menclosed = NFWHalo.mass(R * u.kpc)
vR = 0.0
vT = NFWHalo.vcirc(R * u.kpc, use_physical=True, quantity= False)
vz = 0.0
o = Orbit([R * u.kpc,vR * u.km / u.s,vT * u.km / u.s,z * u.kpc,vz * u.km / u.s,phi * u.rad])
o_cdf = o()  # copy of orbit for dynamical friction
o_fdf = o()  # copy of orbit for fuzzy dynamical friction
# Integrate the orbit
t = numpy.linspace(0, 1, 300) * u.Gyr
o.integrate(t, NFWHalo)
o_cdf.integrate(t, NFWHalo + cdf)
o_fdf.integrate(t, NFWHalo + fdf)

The results can be plotted using:

import matplotlib.pyplot as plt
plt.figure(figsize=(10, 4))
plt.subplot(121)
plt.plot(o.x(t), o.y(t), label="No DF")
plt.plot(o_cdf.x(t), o_cdf.y(t), label="CDM DF")
plt.plot(o_fdf.x(t), o_fdf.y(t), label="Fuzzy DF")
plt.xlabel("x [kpc]", fontsize=14, fontweight="bold")
plt.ylabel("y [kpc]", fontsize=14, fontweight="bold")
plt.gca().set_facecolor("whitesmoke")
plt.grid(True, which="both", linestyle="--", linewidth=0.7, alpha=0.7)
plt.legend(fontsize=12)

plt.subplot(122)
plt.plot(t, o.R(t), label="No DF")
plt.plot(t, o_cdf.R(t), label="CDM DF")
plt.plot(t, o_fdf.R(t), label="Fuzzy DF")
plt.xlabel("t [Gyr]", fontsize=14, fontweight="bold")
plt.ylabel("R [kpc]", fontsize=14, fontweight="bold")
plt.legend(fontsize=12)
plt.tight_layout(pad=2.5)
plt.subplots_adjust(left=0.1, right=0.9, top=0.9, bottom=0.1, wspace=0.3)
plt.gca().set_facecolor("whitesmoke")
plt.grid(True, which="both", linestyle="--", linewidth=0.7, alpha=0.7)

plt.suptitle("Dynamical Friction in NFW halo", fontsize=16, fontweight="bold")
plt.show()

which gives

../_images/FDMDynamicalFrictionForce_example.png
class galpy.potential.FDMDynamicalFrictionForce(amp=1.0, GMs=0.1, gamma=1.0, rhm=0.0, m=1e-99, dens=None, sigmar=None, const_lnLambda=False, const_FDMfactor=False, minr=0.0001, maxr=25.0, nr=501, ro=None, vo=None)[source]

Implements the fuzzy dark matter (FDM) dynamical friction force.

The force is given by:

\[\vec{F}_\mathrm{FDM} = -\frac{4\pi\mathcal{G}^2 M_\mathrm{obj}^2 \rho}{v^3} C_\mathrm{FDM}(kr) \vec{v}\]

where the coefficient \(C_\mathrm{FDM}(kr)\) depends on \(kr = \frac{m v r}{\hbar}\). There are three regimes for the coefficient, depending on the value of \(kr\):

  1. Zero-velocity regime: for \(kr < M_\sigma / 2\), where \(M_\sigma = v / \sigma(r)\) is the classical Mach number, the coefficient is given by

    \[C_\mathrm{FDM}(kr) = \mathrm{Cin}(2kr) + \frac{\sin(2kr)}{2kr} - 1\]

    with

    \[\mathrm{Cin}(z) = \int_0^z \frac{1 - \cos(t)}{t} \, \mathrm{d}t\]
  2. Dispersion regime: for \(kr > 2 M_\sigma\), the coefficient is given by

    \[C_\mathrm{FDM}(kr) = \ln\left(\frac{2kr}{M_\sigma}\right) \left[ \mathrm{erf}(X) - \frac{2X}{\sqrt{\pi}} \exp(-X^2) \right]\]

    where \(X = \frac{v}{\sqrt{2} \sigma(r)}\).

  3. Intermediate regime: for \(M_\sigma / 2 < kr < 2 M_\sigma\), the coefficient is given by linear interpolation between the zero-velocity and dispersion regimes.

If the FDM coefficient \(C_\mathrm{FDM}(kr)\) is larger than the classical coefficient \(C_\mathrm{CDM}\), we use the classical coefficient as a cutoff, because it means that we are in the classical regime. The classical coefficient is given by:

\[C_\mathrm{CDM} = {\ln[ 1+\Lambda^2]\over 2} \left[ \mathrm{erf}(X) - \frac{2X}{\sqrt{\pi}} \exp(-X^2) \right]\]

See also

ChandrasekharDynamicalFrictionForce

For the implementation and documentation of the classical Chandrasekhar dynamical friction force (CDM case).

__init__(amp=1.0, GMs=0.1, gamma=1.0, rhm=0.0, m=1e-99, dens=None, sigmar=None, const_lnLambda=False, const_FDMfactor=False, minr=0.0001, maxr=25.0, nr=501, ro=None, vo=None)[source]

Initialize a FDM Dynamical Friction force [1], [2].

Parameters:
  • amp (float) – Amplitude to be applied to the potential (default: 1).

  • GMs (float or Quantity) – Satellite mass; can be a Quantity with units of mass or Gxmass; can be adjusted after initialization by setting obj.GMs= where obj is your ChandrasekharDynamicalFrictionForce instance (note that the mass of the satellite can not be changed simply by multiplying the instance by a number, because he mass is not only used as an amplitude).

  • rhm (float or Quantity) – Half-mass radius of the satellite (set to zero for a black hole); can be adjusted after initialization by setting obj.rhm= where obj is your ChandrasekharDynamicalFrictionForce instance.

  • m (float or Quantity) – Mass of the Fuzzy Dark Matter particle; can be a Quantity with units of eV; default is set to 1e-99, which is roughly 1e-22 eV.

  • gamma (float) – Free-parameter in \(\Lambda\).

  • dens (Potential instance or a combined potential formed using addition (pot1+pot2+…), optional) – Potential instance or a combined potential formed using addition (pot1+pot2+…) that represents the density [default: LogarithmicHaloPotential(normalize=1.,q=1.)].

  • sigmar (callable, optional) – Function that gives the velocity dispersion as a function of r (has to be in natural units!); if None, computed from the dens potential using the spherical Jeans equation (in galpy.df.jeans) assuming zero anisotropy; if set to a lambda function, the object cannot be pickled (so set it to a real function).

  • const_lnLambda (bool, optional) – If set to a number, use a constant ln(Lambda) instead with this value.

  • const_FDMfactor (bool, optional) – If set to a number, use a constant FDM factor instead with this value; if set to False, the FDM factor is calculated from the mass of the Fuzzy Dark Matter particle and the current radius and velocity.

  • minr (float or Quantity, optional) – Minimum r at which to apply dynamical friction: at r < minr, friction is set to zero.

  • maxr (float or Quantity, optional) – Maximum r for which sigmar gets interpolated; for best performance set this to the maximum r you will consider.

  • nr (int, optional) – Number of radii to use in the interpolation of sigmar.

  • 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

2025-05-30: Started (A. Szpilfidel, P. Boldrini, J. Bovy)

References