/*******************************************************************************
* This file is part of SWIFT.
* Copyright (c) 2023 Darwin Roduit (darwin.roduit@alumni.epfl.ch)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*
******************************************************************************/
#ifndef SWIFT_POTENTIAL_MWPotential2014_H
#define SWIFT_POTENTIAL_MWPotential2014_H
/* Config parameters. */
#include
/* Some standard headers. */
#include
#include
/* Local includes. */
#include "error.h"
#include "gravity.h"
#include "integer_power.h"
#include "parser.h"
#include "part.h"
#include "physical_constants.h"
#include "space.h"
#include "units.h"
#ifdef HAVE_LIBGSL
#include
#endif
#define potential_MW2014_num_coefficients 17
/**
* @brief External Potential Properties - MWPotential2014 composed by
* NFW + Miyamoto-Nagai + Power Spherical cut-off potentials
*
* halo --> rho_NFW(r) = rho_0 / ( (r/R_s)*(1+r/R_s)^2 )
* disk --> Phi_MN(R,z) = -G * Mdisk / (R^2 + (Rdisk +
* (z^2+Zdisk^2)^1/2)^2)^(1/2) bulge --> rho_PSC(r) =
* amplitude*(r_1/r)^alpha*exp(-(r/r_c)^2)
*
* We however parametrise this in terms of c and virial_mass, Mdisk, Rdisk
* and Zdisk. Also, each potential is given a contribution amplitude such that
* the resulting potential is:
* Phi_tot = f_1 * Phi_NFW + f_2 * Phi_MN + f_3 * Phi_PSC,
* with f_1, f_2 and f_3 contained in the array f.
*
* This potential is inspired by the following article:
* galpy: A Python Library for Galactic Dynamics, Jo Bovy (2015),
* Astrophys. J. Supp., 216, 29 (arXiv/1412.3451).
*/
struct external_potential {
/*! Position of the centre of potential */
double x[3];
/*! The scale radius of the NFW potential */
double r_s;
/*! The pre-factor \f$ 4 \pi G \rho_0 \r_s^3 \f$ */
double pre_factor;
/*! Hubble parameter */
double H;
/*! The concentration parameter */
double c_200;
/*! The virial mass */
double M_200;
/*! The NFW density at rs */
double rho_0;
/*! Disk Size */
double Rdisk;
/*! Disk height */
double Zdisk;
/*! Disk Mass */
double Mdisk;
/*! Amplitude for the PSC potential */
double amplitude;
/*! Reference radius for amplitude */
double r_1;
/*! Inner power */
double alpha;
/*! Cut-off radius */
double r_c;
/*! Contribution of each potential : f[0]*NFW + f[1]*MN + f[2]*PSP */
double f[3];
/*! Prefactor \f$ 2 \pi amplitude r_1^\alpha r_c^(3-\alpha) \f$ */
double prefactor_psc_1;
/*! Prefactor \f$ 2 \pi amplitude r_1^\alpha r_c^(2-\alpha) \f$ */
double prefactor_psc_2;
/*! Are we using the dynamical friction ?*/
int with_dynamical_friction;
/*! Coulomb logarithm for the dynamical friction */
double df_lnLambda;
/*! Satellite mass for the dynamical friction in code unit */
double df_satellite_mass;
/*! Polynomial fit coefficients for the velocity dispersion model */
double df_polyfit_coeffs[potential_MW2014_num_coefficients];
/*! Minimum velocity dispersion for the velocity dispersion model */
double df_sigma_floor;
/*! Radius below which the dynamical friction vanishes */
double df_core_radius;
/*! Gamma function evaluation \f$ \Gamma((3-\alpha)/2 \f$ */
double gamma_psc;
/*! Time-step condition pre_factor, this factor is used to multiply times the
* orbital time, so in the case of 0.01 we take 1% of the orbital time as
* the time integration steps */
double timestep_mult;
/*! Time-step condition pre_factor, this factor is used to constraints
* the time-step so that the norm of v*dt is a fraction of the acceleration */
double df_timestep_mult;
/*! Minimum time step based on the orbital time at the softening times
* the timestep_mult */
double mintime;
/*! Common log term \f$ \ln(1+c_{200}) - \frac{c_{200}}{1 + c_{200}} \f$ */
double log_c200_term;
/*! Softening length */
double eps;
};
/**
* @brief Computes the time-step due to the acceleration from the NFW + MN + PSC
* potential as a fraction (timestep_mult) of the circular orbital time of that
* particle.
*
* @param time The current time.
* @param potential The #external_potential used in the run.
* @param phys_const The physical constants in internal units.
* @param g Pointer to the g-particle data.
*/
__attribute__((always_inline)) INLINE static float external_gravity_timestep(
double time, const struct external_potential* restrict potential,
const struct phys_const* restrict phys_const,
const struct gpart* restrict g) {
#ifdef HAVE_LIBGSL
const float dx = g->x[0] - potential->x[0];
const float dy = g->x[1] - potential->x[1];
const float dz = g->x[2] - potential->x[2];
const float R2 = dx * dx + dy * dy;
const float r = sqrtf(R2 + dz * dz + potential->eps * potential->eps);
/* Vcirc for NFW */
const float M_NFW = potential->pre_factor * (logf(1.0f + r / potential->r_s) -
r / (r + potential->r_s));
const float Vcirc_NFW = sqrtf((phys_const->const_newton_G * M_NFW) / r);
/* Now for MN */
const float R = sqrtf(R2);
const float sqrt_term = sqrtf(dz * dz + potential->Zdisk * potential->Zdisk);
const float MN_denominator =
powf(R2 + powf(potential->Rdisk + sqrt_term, 2.0f), 1.5f);
const float dPhi_dR_MN = potential->Mdisk * R / MN_denominator;
const float dPhi_dz_MN = potential->Mdisk * dz *
(potential->Rdisk + sqrt_term) /
(sqrt_term * MN_denominator);
const float Vcirc_MN = sqrtf(phys_const->const_newton_G * R * dPhi_dR_MN +
phys_const->const_newton_G * dz * dPhi_dz_MN);
/* Now for PSC */
const float r2 = r * r;
const float M_psc =
potential->prefactor_psc_1 *
(potential->gamma_psc -
gsl_sf_gamma_inc(1.5f - 0.5f * potential->alpha,
r2 / (potential->r_c * potential->r_c)));
const float Vcirc_PSC = sqrtf(phys_const->const_newton_G * M_psc / r);
/* Total circular velocity */
const float Vcirc = sqrtf(potential->f[0] * Vcirc_NFW * Vcirc_NFW +
potential->f[1] * Vcirc_MN * Vcirc_MN +
potential->f[2] * Vcirc_PSC * Vcirc_PSC);
const float period = 2.0f * M_PI * r / Vcirc;
/* Time-step as a fraction of the circular period */
float time_step = potential->timestep_mult * period;
/* Add dynamical friction */
if (potential->with_dynamical_friction) {
const float vx = g->v_full[0];
const float vy = g->v_full[1];
const float vz = g->v_full[2];
float v = sqrtf(vx * vx + vy * vy + vz * vz);
const float ax = g->a_grav[0];
const float ay = g->a_grav[1];
const float az = g->a_grav[2];
float a = sqrtf(ax * ax + ay * ay + az * az);
time_step = min(time_step, potential->df_timestep_mult * v / a);
}
return max(time_step, potential->mintime);
#else
error("Code not compiled with GSL. Can't compute MWPotential2014.");
return 0.0;
#endif
}
/**
* @brief Computes the mass density of the MW2014 model.
*
* @param x The x coordinate.
* @param y The y coordinate.
* @param z The y coordinate.
* @param time The current time (unused here).
* @param potential The #external_potential used in the run.
* @param phys_const Physical constants in internal units.
*/
__attribute__((always_inline)) INLINE static float external_gravity_get_density(
float x, float y, float z, double time,
const struct external_potential* potential,
const struct phys_const* const phys_const) {
/* First for the NFW profile */
const float R2 = x * x + y * y;
const float r = sqrtf(R2 + z * z + potential->eps * potential->eps);
/* First for the NFW part */
const float rho_NFW =
potential->rho_0 / ((r / potential->r_s) * (1 + r / potential->r_s) *
(1 + r / potential->r_s));
/* Second the MN disk */
const float zb = sqrtf(potential->Zdisk * potential->Zdisk + z * z);
const float azb2 = integer_pow(potential->Rdisk + zb, 2);
const float cte =
(potential->Zdisk * potential->Zdisk * potential->Mdisk) / (4 * M_PI);
const float rho_MN =
cte * (potential->Rdisk * R2 + (potential->Rdisk + 3 * zb) * azb2) /
(pow(R2 + azb2, 2.5) * zb * zb * zb);
/* Third the bulge */
const float rho_PSC = potential->amplitude *
pow(potential->r_1 / r, potential->alpha) *
exp(-integer_pow(r / potential->r_c, 2));
/* Total density */
const float density = potential->f[0] * rho_NFW + potential->f[1] * rho_MN +
potential->f[2] * rho_PSC;
return density;
}
/**
* @brief Computes the gravitational acceleration from an NFW Halo potential +
* MN disk + PSC bulge.
*
* Note that the accelerations are multiplied by Newton's G constant
* later on.
*
* a_x = 4 pi \rho_0 r_s^3 ( 1/((r+rs)*r^2) - log(1+r/rs)/r^3) * x
* - dphi_PSC/dr*x/r
* a_y = 4 pi \rho_0 r_s^3 ( 1/((r+rs)*r^2) - log(1+r/rs)/r^3) * y
* - dphi_PSC/dr*y/r
* a_z = 4 pi \rho_0 r_s^3 ( 1/((r+rs)*r^2) - log(1+r/rs)/r^3) * z
* - dphi_PSC/dr*z/r
*
*
* @param time The current time.
* @param potential The #external_potential used in the run.
* @param phys_const The physical constants in internal units.
* @param g Pointer to the g-particle data.
*/
__attribute__((always_inline)) INLINE static void external_gravity_acceleration(
double time, const struct external_potential* restrict potential,
const struct phys_const* restrict phys_const, struct gpart* restrict g) {
#ifdef HAVE_LIBGSL
const float dx = g->x[0] - potential->x[0];
const float dy = g->x[1] - potential->x[1];
const float dz = g->x[2] - potential->x[2];
/* First for the NFW part */
const float R2 = dx * dx + dy * dy;
const float r = sqrtf(R2 + dz * dz + potential->eps * potential->eps);
const float r_inv = 1.0f / r;
const float M_NFW = potential->pre_factor * (logf(1.0f + r / potential->r_s) -
r / (r + potential->r_s));
const float dpot_dr_NFW = M_NFW * r_inv * r_inv;
const float pot_nfw =
-potential->pre_factor * logf(1.0f + r / potential->r_s) * r_inv;
g->a_grav[0] -= potential->f[0] * dpot_dr_NFW * dx * r_inv;
g->a_grav[1] -= potential->f[0] * dpot_dr_NFW * dy * r_inv;
g->a_grav[2] -= potential->f[0] * dpot_dr_NFW * dz * r_inv;
gravity_add_comoving_potential(g, potential->f[0] * pot_nfw);
/* Now the the MN disk */
const float f1 = sqrtf(potential->Zdisk * potential->Zdisk + dz * dz);
const float f2 = potential->Rdisk + f1;
const float f3 = powf(R2 + f2 * f2, -1.5f);
const float mn_term = potential->Rdisk + sqrtf(potential->Zdisk + dz * dz);
const float pot_mn = -potential->Mdisk / sqrtf(R2 + mn_term * mn_term);
g->a_grav[0] -= potential->f[1] * potential->Mdisk * f3 * dx;
g->a_grav[1] -= potential->f[1] * potential->Mdisk * f3 * dy;
g->a_grav[2] -= potential->f[1] * potential->Mdisk * f3 * (f2 / f1) * dz;
gravity_add_comoving_potential(g, potential->f[1] * pot_mn);
/* Now the the PSC bulge */
const float r2 = r * r;
const float M_psc =
potential->prefactor_psc_1 *
(potential->gamma_psc -
gsl_sf_gamma_inc(1.5f - 0.5f * potential->alpha,
r2 / (potential->r_c * potential->r_c)));
const float dpot_dr = M_psc / r2;
const float pot_psc =
-M_psc / r - potential->prefactor_psc_2 *
gsl_sf_gamma_inc(1.0f - 0.5f * potential->alpha,
r2 / (potential->r_c * potential->r_c));
g->a_grav[0] -= potential->f[2] * dpot_dr * dx * r_inv;
g->a_grav[1] -= potential->f[2] * dpot_dr * dy * r_inv;
g->a_grav[2] -= potential->f[2] * dpot_dr * dz * r_inv;
gravity_add_comoving_potential(g, potential->f[2] * pot_psc);
/* Add dynamical friction */
if (potential->with_dynamical_friction) {
const float sqrtpi = sqrtf(M_PI);
const float vx = g->v_full[0];
const float vy = g->v_full[1];
const float vz = g->v_full[2];
const float v = sqrtf(vx * vx + vy * vy + vz * vz);
/* Compute the velocity dispertion as a function of the radius r, using
* using a high order polynomial interpolation.
*/
double sigma = 0;
for (int i = 0; i < potential_MW2014_num_coefficients; i++)
sigma +=
potential
->df_polyfit_coeffs[potential_MW2014_num_coefficients - 1 - i] *
integer_pow(r, i);
/* Prevent the velocity dispersion to be zero */
sigma = fmax(potential->df_sigma_floor, sigma);
/* Compute the chi parameter */
double X = v / (sqrt(2) * sigma);
double amp1 = erf(X) - ((2 * X / sqrtpi) * exp(-X * X));
/* Kill the dynamical friction at the center */
amp1 *= max(0, erf((r - potential->df_core_radius) /
potential->df_core_radius / 2.0));
/* Compute the density */
float density =
external_gravity_get_density(dx, dy, dz, time, potential, phys_const);
/* Final factor (Binney & Tremaine 2008, eq. 8.7) */
float dyn_fric_timescale_inv =
-4 * M_PI * integer_pow(phys_const->const_newton_G, 2) /
integer_pow(v, 3) * density * potential->df_lnLambda * amp1 *
potential->df_satellite_mass;
/* Sanity check */
if (dyn_fric_timescale_inv > 0)
error("dyn_fric_timescale_inv is larger than zero (%g %g %g\n) !",
dyn_fric_timescale_inv, erf((r - 10) / 20), r);
/* Acceleration is per unit of G */
dyn_fric_timescale_inv /= phys_const->const_newton_G;
g->a_grav[0] += dyn_fric_timescale_inv * vx;
g->a_grav[1] += dyn_fric_timescale_inv * vy;
g->a_grav[2] += dyn_fric_timescale_inv * vz;
}
#else
error("Code not compiled with GSL. Can't compute MWPotential2014.");
#endif
}
/**
* @brief Computes the gravitational potential energy of a particle in an
* NFW potential + MN potential.
*
* phi = f[0] * (-4 * pi * G * rho_0 * r_s^3 * ln(1+r/r_s)) - f[1] * (G * Mdisk
* / sqrt(R^2 + (Rdisk + sqrt(z^2 + Zdisk^2))^2)) + f[2] * [- G / r * (2 * pi *
* amplitude * r_1^alpha r_c^(3 - alpha) * gamma_inf((3 - alpha)/2, r^2 / r_c^2)
* ) - 2 * pi * G * amplitude * r_1^alpha * r_c^(2 - alpha)
* Gamma_sup((2-alpha)/2, r^2 / r_c^2 ) ]
*
* @param time The current time (unused here).
* @param potential The #external_potential used in the run.
* @param phys_const Physical constants in internal units.
* @param g Pointer to the particle data.
*/
__attribute__((always_inline)) INLINE static float
external_gravity_get_potential_energy(
double time, const struct external_potential* potential,
const struct phys_const* const phys_const, const struct gpart* g) {
#ifdef HAVE_LIBGSL
const float dx = g->x[0] - potential->x[0];
const float dy = g->x[1] - potential->x[1];
const float dz = g->x[2] - potential->x[2];
/* First for the NFW profile */
const float R2 = dx * dx + dy * dy;
const float r = sqrtf(R2 + dz * dz + potential->eps * potential->eps);
const float r_inv = 1.0f / r;
const float pot_nfw =
-potential->pre_factor * logf(1.0f + r / potential->r_s) * r_inv;
/* Now for the MN disk */
const float sqrt_term = sqrtf(dz * dz + potential->Zdisk * potential->Zdisk);
const float MN_denominator =
sqrtf(R2 + powf(potential->Rdisk + sqrt_term, 2.0f));
const float mn_pot = -potential->Mdisk / MN_denominator;
/* Now for PSC bulge */
const float r2 = r * r;
const float M_psc =
potential->prefactor_psc_1 *
(potential->gamma_psc -
gsl_sf_gamma_inc(1.5f - 0.5f * potential->alpha,
r2 / (potential->r_c * potential->r_c)));
const float psc_pot =
-M_psc / r - potential->prefactor_psc_2 *
gsl_sf_gamma_inc(1.0f - 0.5f * potential->alpha,
r2 / (potential->r_c * potential->r_c));
return phys_const->const_newton_G *
(potential->f[0] * pot_nfw + potential->f[1] * mn_pot +
potential->f[2] * psc_pot);
#else
error("Code not compiled with GSL. Can't compute MWPotential2014.");
return 0.0;
#endif
}
/**
* @brief Initialises the external potential properties in the internal system
* of units.
*
* @param parameter_file The parsed parameter file
* @param phys_const Physical constants in internal units
* @param us The current internal system of units
* @param potential The external potential properties to initialize
*/
static INLINE void potential_init_backend(
struct swift_params* parameter_file, const struct phys_const* phys_const,
const struct unit_system* us, const struct space* s,
struct external_potential* potential) {
#ifdef HAVE_LIBGSL
/* Read in the position of the centre of potential */
parser_get_param_double_array(
parameter_file, "MWPotential2014Potential:position", 3, potential->x);
/* Is the position absolute or relative to the centre of the box? */
const int useabspos = parser_get_param_int(
parameter_file, "MWPotential2014Potential:useabspos");
/* Define the default value in the above system of units*/
const double c_200_default = 9.823403437774843;
const double M_200_Msun_default = 147.41031542774076e10; /* M_sun */
const double H_default = 127.78254614201471e-2; /* no unit */
const double Mdisk_Msun_default = 6.8e10; /* M_sun */
const double Rdisk_kpc_default = 3.0; /* kpc */
const double Zdisk_kpc_default = 0.280; /* kpc */
const double amplitude_Msun_per_kpc3_default = 1e10; /* M_sun/kpc^3 */
const double r_1_kpc_default = 1.0; /* kpc */
const double alpha_default = 1.8; /* no unit */
const double r_c_kpc_default = 1.9; /* kpc */
potential->f[0] = 0.4367419745056084; /* no unit */
potential->f[1] = 1.002641971008805; /* no unit */
potential->f[2] = 0.022264787598364262; /* no unit */
if (!useabspos) {
potential->x[0] += s->dim[0] / 2.;
potential->x[1] += s->dim[1] / 2.;
potential->x[2] += s->dim[2] / 2.;
}
const double df_polyfit_coeffs_default[potential_MW2014_num_coefficients] = {
-2.96536595e-31, 8.88944631e-28, -1.18280578e-24, 9.29479457e-22,
-4.82805265e-19, 1.75460211e-16, -4.59976540e-14, 8.83166045e-12,
-1.24747700e-09, 1.29060404e-07, -9.65315026e-06, 5.10187806e-04,
-1.83800281e-02, 4.26501444e-01, -5.78038064e+00, 3.57956721e+01,
1.85478908e+02};
/* Read the other parameters of the model */
potential->timestep_mult = parser_get_param_double(
parameter_file, "MWPotential2014Potential:timestep_mult");
/* Bug fix : Read the softening length from the params file */
potential->eps = parser_get_param_double(parameter_file,
"MWPotential2014Potential:epsilon");
potential->c_200 = parser_get_opt_param_double(
parameter_file, "MWPotential2014Potential:concentration", c_200_default);
potential->M_200 = parser_get_opt_param_double(
parameter_file, "MWPotential2014Potential:M_200_Msun",
M_200_Msun_default);
potential->H = parser_get_opt_param_double(
parameter_file, "MWPotential2014Potential:H", H_default);
potential->Mdisk = parser_get_opt_param_double(
parameter_file, "MWPotential2014Potential:Mdisk_kpc", Mdisk_Msun_default);
potential->Rdisk = parser_get_opt_param_double(
parameter_file, "MWPotential2014Potential:Rdisk_kpc", Rdisk_kpc_default);
potential->Zdisk = parser_get_opt_param_double(
parameter_file, "MWPotential2014Potential:Zdisk_kpc", Zdisk_kpc_default);
potential->amplitude = parser_get_opt_param_double(
parameter_file, "MWPotential2014Potential:amplitude_Msun_per_kpc3",
amplitude_Msun_per_kpc3_default);
potential->r_1 = parser_get_opt_param_double(
parameter_file, "MWPotential2014Potential:r_1_kpc", r_1_kpc_default);
potential->alpha = parser_get_opt_param_double(
parameter_file, "MWPotential2014Potential:alpha", alpha_default);
potential->r_c = parser_get_opt_param_double(
parameter_file, "MWPotential2014Potential:r_c_kpc", r_c_kpc_default);
parser_get_opt_param_double_array(
parameter_file, "MWPotential2014Potential:potential_factors", 3,
potential->f);
potential->with_dynamical_friction = parser_get_opt_param_int(
parameter_file, "MWPotential2014Potential:with_dynamical_friction", 0);
potential->df_lnLambda = parser_get_opt_param_double(
parameter_file, "MWPotential2014Potential:df_lnLambda", 5.0);
potential->df_satellite_mass = parser_get_opt_param_double(
parameter_file, "MWPotential2014Potential:df_satellite_mass_in_Msun",
1e10);
potential->df_timestep_mult = parser_get_opt_param_double(
parameter_file, "MWPotential2014Potential:df_timestep_mult", 0.1);
potential->df_core_radius = parser_get_opt_param_double(
parameter_file, "MWPotential2014Potential:df_core_radius_in_kpc", 10);
potential->df_sigma_floor = parser_get_opt_param_double(
parameter_file, "MWPotential2014Potential:df_sigma_floor_km_p_s", 10.0);
/* Read all the dynamical friction coefficients */
for (int i = 0; i < potential_MW2014_num_coefficients; i++) {
char param_name[128];
sprintf(param_name, "MWPotential2014Potential:df_polyfit_coeffs%2d", i);
potential->df_polyfit_coeffs[i] = parser_get_opt_param_double(
parameter_file, param_name, df_polyfit_coeffs_default[i]);
}
/* Convert to internal system of units by using the
* physical constants defined in this system */
const double kpc = 1000. * phys_const->const_parsec;
const double kms = 1e5 / units_cgs_conversion_factor(us, UNIT_CONV_VELOCITY);
potential->M_200 *= phys_const->const_solar_mass;
potential->H *= phys_const->const_reduced_hubble;
potential->Mdisk *= phys_const->const_solar_mass;
potential->Rdisk *= kpc;
potential->Zdisk *= kpc;
potential->r_1 *= kpc;
potential->r_c *= kpc;
potential->amplitude *= phys_const->const_solar_mass / (kpc * kpc * kpc);
potential->df_sigma_floor *= kms;
potential->df_satellite_mass *= phys_const->const_solar_mass;
potential->df_core_radius *= kpc;
/* units conversion for polyfit coefficients */
for (int i = 0; i < potential_MW2014_num_coefficients; i++)
potential->df_polyfit_coeffs[potential_MW2014_num_coefficients - 1 - i] /=
integer_pow(kpc, i) * kms;
/* Compute rho_c */
const double rho_c = 3.0 * potential->H * potential->H /
(8.0 * M_PI * phys_const->const_newton_G);
/* Compute R_200 */
const double R_200 =
cbrtf(3.0 * potential->M_200 / (4. * M_PI * 200.0 * rho_c));
/* NFW scale-radius */
potential->r_s = R_200 / potential->c_200;
const double r_s3 = potential->r_s * potential->r_s * potential->r_s;
/* Log(c_200) term appearing in many expressions */
potential->log_c200_term =
log(1. + potential->c_200) - potential->c_200 / (1. + potential->c_200);
potential->rho_0 =
potential->M_200 / (4.f * M_PI * r_s3 * potential->log_c200_term);
/* Pre-factor for the accelerations (note G is multiplied in later on) */
potential->pre_factor = 4.0f * M_PI * potential->rho_0 * r_s3;
/* Prefactor for the mass of the PSC profile */
potential->prefactor_psc_1 = 2.0 * M_PI * potential->amplitude *
pow(potential->r_1, potential->alpha) *
pow(potential->r_c, 3.0 - potential->alpha);
/* Gamma function value for the mass of the PSC profile */
potential->gamma_psc = gsl_sf_gamma(1.5 - 0.5 * potential->alpha);
/* Prefactor for the potential of the PSC profile */
potential->prefactor_psc_2 = 2.0 * M_PI * potential->amplitude *
pow(potential->r_1, potential->alpha) *
pow(potential->r_c, 2.0 - potential->alpha);
/* Compute the orbital time at the softening radius */
const double sqrtgm = sqrt(phys_const->const_newton_G * potential->M_200);
const double epslnthing = log(1.0 + potential->eps / potential->r_s) -
potential->eps / (potential->eps + potential->r_s);
potential->mintime = 2. * M_PI * potential->eps * sqrtf(potential->eps) *
sqrtf(potential->log_c200_term / epslnthing) / sqrtgm *
potential->timestep_mult;
#else
error("Code not compiled with GSL. Can't compute MWPotential2014.");
#endif
}
/**
* @brief Prints the properties of the external potential to stdout.
*
* @param potential The external potential properties.
*/
static INLINE void potential_print_backend(
const struct external_potential* potential) {
message(
"External potential is 'MWPotential2014' "
"with properties (in internal units) are "
"(x,y,z) = "
"(%e, %e, %e), c_200 = %e, M_200 = %e, H = %e, M_disk = %e, R_disk = %e, "
"z_disk = %e, amplitude = %e, r_1 = %e, alpha = %e, r_c = %e, timestep "
"multiplier = %e mintime = %e",
potential->x[0], potential->x[1], potential->x[2], potential->c_200,
potential->M_200, potential->H, potential->Mdisk, potential->Rdisk,
potential->Zdisk, potential->amplitude, potential->r_1, potential->alpha,
potential->r_c, potential->timestep_mult, potential->mintime);
}
#endif /* SWIFT_POTENTIAL_MWPotential2014_H */