Comoving coordinates
Source code (Click to expand)
| /**
* \file integrator_cosmology_leapfrog.c
* \brief Leapfrog integrator for cosmological simulations.
*
* \author Ching-Yin Ng
*/
#include <math.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "acceleration.h"
#include "common.h"
#include "cosmology.h"
#include "error.h"
#include "integrator.h"
#include "output.h"
#include "progress_bar.h"
#include "settings.h"
#include "system.h"
#include "utils.h"
#include "math_functions.h"
WIN32DLL_API ErrorStatus leapfrog_cosmology(
CosmologicalSystem *restrict system,
OutputParam *restrict output_param,
SimulationStatus *restrict simulation_status,
Settings *restrict settings,
const double a_final,
const int num_steps,
const int pm_grid_size
)
{
/* Declare variables */
ErrorStatus error_status;
const int num_particles = system->num_particles;
double *restrict x = system->x;
double *restrict v = system->v;
const double H0 = system->h * 100.0;
const double omega_m = system->omega_m;
const double omega_lambda = system->omega_lambda;
bool is_output = (output_param->method != OUTPUT_METHOD_DISABLED);
int *restrict output_count_ptr = &(output_param->output_count_);
const double output_interval = output_param->output_interval;
double next_output_time = output_interval;
const double t0 = log(system->scale_factor);
const double tf = log(a_final);
double *restrict t_ptr = &(simulation_status->t);
*t_ptr = t0;
int64 *restrict num_steps_ptr = &(simulation_status->num_steps);
const bool enable_progress_bar = settings->enable_progress_bar;
double H_a;
/* Allocate memory */
double *restrict momentum = malloc(num_particles * 3 * sizeof(double));
double *restrict a = malloc(num_particles * 3 * sizeof(double));
// Check if memory allocation is successful
if (!momentum || !a)
{
error_status = WRAP_RAISE_ERROR(GRAV_MEMORY_ERROR, "Failed to allocate memory for arrays");
goto err_memory;
}
/* Get mean background density */
const double G = 6.67430e-8 * (
system->unit_mass
* system->unit_time * system->unit_time
/ (system->unit_length * system->unit_length * system->unit_length)
);
/* Initial output */
if (is_output && output_param->output_initial)
{
error_status = WRAP_TRACEBACK(output_snapshot_cosmology(
output_param,
system,
simulation_status,
settings
));
if (error_status.return_code != GRAV_SUCCESS)
{
goto err_initial_output;
}
}
/* Set periodic boundary conditions */
set_periodic_boundary_conditions(system);
/* Initialize momentum */
for (int i = 0; i < num_particles; i++)
{
for (int j = 0; j < 3; j++)
{
momentum[i * 3 + j] = (system->scale_factor) * (system->scale_factor) * v[i * 3 + j];
}
}
/* Compute initial acceleration */
error_status = WRAP_TRACEBACK(acceleration_PM(
a,
system,
G,
pm_grid_size
));
if (error_status.return_code != GRAV_SUCCESS)
{
goto err_acceleration;
}
/* Main Loop */
double dt = (tf - t0) / num_steps;
int64 total_num_steps = (int64) ceil((tf - t0) / dt);
ProgressBarParam progress_bar_param;
if (enable_progress_bar)
{
error_status = WRAP_TRACEBACK(start_progress_bar(&progress_bar_param, total_num_steps));
if (error_status.return_code != GRAV_SUCCESS)
{
goto err_start_progress_bar;
}
}
simulation_status->dt = dt;
*num_steps_ptr = 0;
while (*num_steps_ptr < total_num_steps)
{
/* Check dt overshoot */
if (*t_ptr + dt > tf)
{
dt = tf - *t_ptr;
}
simulation_status->dt = dt;
/* Kick (p_1/2) */
H_a = compute_H(system->scale_factor, H0, omega_m, omega_lambda);
for (int i = 0; i < num_particles; i++)
{
for (int j = 0; j < 3; j++)
{
momentum[i * 3 + j] -= (0.5 * dt) * a[i * 3 + j] / H_a;
}
}
*t_ptr += 0.5 * dt;
system->scale_factor = exp(*t_ptr);
/* Drift (x_1) */
H_a = compute_H(system->scale_factor, H0, omega_m, omega_lambda);
for (int i = 0; i < num_particles; i++)
{
for (int j = 0; j < 3; j++)
{
x[i * 3 + j] += dt * momentum[i * 3 + j] / ((system->scale_factor) * (system->scale_factor) * H_a);
}
}
/* Set periodic boundary conditions */
set_periodic_boundary_conditions(system);
/* Kick (p_1) */
error_status = WRAP_TRACEBACK(acceleration_PM(
a,
system,
G,
pm_grid_size
));
if (error_status.return_code != GRAV_SUCCESS)
{
goto err_acceleration;
}
for (int i = 0; i < num_particles; i++)
{
for (int j = 0; j < 3; j++)
{
momentum[i * 3 + j] -= (0.5 * dt) * a[i * 3 + j] / H_a;
}
}
(*num_steps_ptr)++;
*t_ptr = t0 + (*num_steps_ptr) * dt;
system->scale_factor = exp(*t_ptr);
/* Store solution */
if (is_output && system->scale_factor >= next_output_time)
{
/* Get velocity from momentum */
for (int i = 0; i < num_particles; i++)
{
for (int j = 0; j < 3; j++)
{
v[i * 3 + j] = momentum[i * 3 + j] / (system->scale_factor * system->scale_factor);
}
}
error_status = WRAP_TRACEBACK(output_snapshot_cosmology(
output_param,
system,
simulation_status,
settings
));
if (error_status.return_code != GRAV_SUCCESS)
{
goto err_output;
}
next_output_time = (*output_count_ptr) * output_interval;
}
if (enable_progress_bar)
{
update_progress_bar(&progress_bar_param, *num_steps_ptr, false);
}
/* Check exit */
if (*(settings->is_exit_ptr))
{
break;
}
}
if (enable_progress_bar)
{
update_progress_bar(&progress_bar_param, *num_steps_ptr, true);
}
free(momentum);
free(a);
return make_success_error_status();
err_output:
err_acceleration:
err_start_progress_bar:
err_initial_output:
err_memory:
free(momentum);
free(a);
return error_status;
}
|
To account for cosmological expansion, we need to use comoving coordinates.
We first review particle motion in comoving coordinates following LSSU.
In comoving coordinates, we have
\[\begin{equation}
%\label{}
\textbf{r} = a(t) \textbf{x},
\end{equation}\]
where \(\textbf{x}\) is a vector in comoving coordinates and \(a(t)\) is the scale factor.
We may express the field equation as
\[\begin{equation}
\label{eqn:poisson_peculiar}
\nabla^2 \phi = 4 \pi G [\rho - \overline{\rho}] a^2,
\end{equation}\]
where \(\phi\) is the peculiar potential, \(\rho\) and \(\overline{\rho}\)
are the density and average background density respectively. Note that
the gradient is taken with respect to \(\textbf{x}\).
In addition, we have the proper velocity of a particle
\[\begin{equation}
%\label{}
\textbf{u} = a \dot{\textbf{x}} + \textbf{x} \dot{a},
\end{equation}\]
so that the Lagrangian for the particle motion is
\[\begin{equation}
%\label{}
\mathcal{L} = \frac{1}{2} m (a \dot{\textbf{x}} + \textbf{x} \dot{a})^2 - m \Phi.
\end{equation}\]
The canonical transformation
\[\begin{equation}
%\label{}
\mathcal{L} \to \mathcal{L} - \frac{\mathrm{d}\psi}{\mathrm{d}t},
\quad \psi = \frac{1}{2} m a \dot{a} x^2,
\end{equation}\]
reduces the Lagrangian to
\[\begin{equation}
%\label{}
\mathcal{L} = \frac{1}{2} m a^2 \dot{x}^2 - m \phi,
\quad \phi = \Phi + \frac{1}{2} a \ddot{a} x^2.
\end{equation}\]
Now, we could define the canonical momentum
\[\begin{equation}
\label{eqn:canonical_momentum}
\textbf{p} = m a^2 \dot{x},
\quad \frac{\mathrm{d}\textbf{p}}{\mathrm{d}t} = - m \nabla \phi.
\end{equation}\]
With the proper peculiar velocity
\[\begin{equation}
%\label{}
\textbf{v}_{\textnormal{pec} } = a \dot{\textbf{x}},
\end{equation}\]
we obtain from the canonical momentum equation,
\[\begin{equation}
\label{eqn:peculiar_velocity_derivative}
\frac{\mathrm{d}\textbf{v}_\textnormal{pec} }{\mathrm{d}t}
+ \textbf{v}_\textnormal{pec} \frac{\dot{a}}{a}
= - \frac{\nabla \phi}{a}.
\end{equation}\]
In our program, since matter is the
main component that contributes to the density perturbation, we rewrite the poisson equation
as
\[\begin{equation}
%\label{}
\nabla^2 \phi = \frac{4 \pi G}{a} [\rho_{m, \textnormal{comoving} } - \overline{\rho}_{m, \textnormal{comoving} }].
\end{equation}\]
where
\[\begin{equation}
%\label{}
\rho_{m, \textnormal{comoving} } - \overline{\rho}_{m, \textnormal{comoving} }
= a^3 [\rho_{m} - \overline{\rho}_{m}].
\end{equation}\]
In fact, we can even drop the background density term since we only care about the
acceleration \(\mathbf{a} = - \nabla \phi\).
Also, instead of the canonical momentum, we define a conjugate momentum
\[\begin{equation}
%\label{}
\textbf{p}' = a^2 \dot{x} = a \textbf{v}_{\textnormal{pec} },
\quad \frac{\mathrm{d}\textbf{p}'}{\mathrm{d}t} = - \nabla \phi.
\end{equation}\]
Following Gadget-4, we use \(\tau = \ln a\) as the integration variable.
We have
\[\begin{equation}
\frac{\mathrm{d} a}{\mathrm{d} \tau}
= \frac{\mathrm{d} a}{\mathrm{d} \ln a}
= \left(\frac{\mathrm{d} \ln a}{\mathrm{d} a} \right)^{-1}
= a,
\end{equation}\]
\[\begin{equation}
\frac{\mathrm{d} \textbf{p}'}{\mathrm{d} \tau}
= \frac{\mathrm{d} a}{\mathrm{d} \tau} \frac{\mathrm{d}t}{\mathrm{d}a} \frac{\mathrm{d} \textbf{p}'}{\mathrm{d} t}
= - \nabla \phi \frac{a}{\dot{a}}
= - \frac{\nabla \phi}{H(a)},
\end{equation}\]
where
\[\begin{equation}
H(a) = H_0 \sqrt{\frac{\Omega_{m, 0}}{a^{3} } + \frac{1 - \Omega}{a^{2}} + \Omega_{\Lambda, 0}}\,\,,
\end{equation}\]
assuming \(\Omega_{\textnormal{radiation} } / a^4 \sim 0\). For \(\textbf{x}\),
\[\begin{equation}
%\label{}
\frac{\partial \textbf{x}}{\partial \tau}
= \frac{\mathrm{d} a}{\mathrm{d} \tau} \frac{\mathrm{d}t}{\mathrm{d}a} \dot{x}
= \frac{a}{\dot{a}} \left( \frac{\textbf{p}'}{a^2} \right)
= \frac{\textbf{p}'}{a^2 H(a)}.
\end{equation}\]
With these relations, we could now do time integration using \(\textbf{x}\) and \(\textbf{p}'\) in comoving coordinates.