PyUltraLight is a Python-based code designed for the study of non-relativistic ultralight dark matter dynamics in a static spacetime background.

The code uses pseudo-spectral methods to compute the evolution of a complex scalar field governed by the Schrödinger-Poisson system of coupled differential equations. Computations are performed on a fixed-grid with periodic boundary conditions, allowing for a decomposition of the field in momentum space by way of the discrete Fourier transform. The field is then evolved through a symmetrised split-step Fourier algorithm, in which nonlinear operators are applied in real space, while spatial derivatives are computed in Fourier space. Fourier transforms within PyUltraLight are handled using the pyFFTW pythonic wrapper around FFTW, the freely-available C-based FFT library.

In its current implementation, PyUltraLight is suitable for modelling interactions of several dark matter halos in the absence of baryonic physics, and for studying the orbits of dark matter halos inside a Newtonian central potential. The main code is supplemented by a Jupyter notebook, in which the user is able to easily specify the parameters for each execution. The Jupyter notebook also provides a selection of basic visualisation tools so that the user may immediately gauge the qualitative features of each simulation run. To construct initial conditions for each simulation, PyUltraLight loads a separately generated NumPy array file which encodes the solitonic solution to the Schrödinger-Poisson system. Within the main code, the scaling properties of the primary solitonic solution are then used to initialise halos of varying mass, position, velocity, and phase, while these parameters are specified by the user within the Jupyter notebook.

PyUltraLight demonstrates excellent energy conservation, and is able to reproduce many qualitative features of previous ultralight dark matter simulations conducted elsewhere. The code runs on standard desktop hardware with support for shared memory mutlithreading. It is available on GitHub under a BSD licence, and is free to use and modify provided suitable acknowledgement is given in any derivative work. PyUltraLight is described in detail in code release paper, while basic usage instructions are given below.


PyUltraLight download and usage instructions:


The complete PyUltraLight repository can be downloaded from GitHub. In order to run the code, it is necessary to install Jupyter Notebook. A full list of Python modules used within the main code and Jupyter notebook can be found in the README file in the PyUltraLight repository. Before using the code, ensure that all prerequisites listed in this file are installed and updated. Once all prerequisites are installed, the Jupyter notebook can be opened and the first input cell run. No errors should be displayed if all prerequisites are installed correctly. The subsequent input cells in the Jupyter notebook are as follows:

1. Set Axion Mass

This cell contains the assumption for the physical mass of the ultralight dark matter particle. Note that the conversion from code units to physical units is dependent upon this value.

2. Set Simulation Parameters

This cell allows the user to input the initial conditions for the simulation, as well as the units for position, mass, velocity, and time. By default, the input units are set to dimensionless code units. A detailed explanation of the dimensionless code units is given in Arxiv link to be given.

Of primary interest to the user are the parameters 'length', 'resol', 'duration'. These parameters allow the user to specify the size and resolution of the simulation grid, as well as the duration of the simulation.

The parameter 'central_mass' is also specified in this cell. Note that this value should be given in the same units as those previously specified in the 's_mass_unit' field. A non-zero value of 'central_mass' will implement a Newtonian potential corresponding to a point mass at the centre of the simulation grid with mass given by 'central_mass'. Note that this potential is static, and is treated separately from the dynamical potential due to self-interaction of the scalar field.

The desired output data and format is also specified in this cell. By default all data is saved in NumPy arrays. The user may choose to save the complete three-dimensional arrays corresponding to the complex scalar field or the density, or may choose to save the density data alone for the plane z = 0, or along the line y = z = 0. In addition, the user may choose to save energy data. The energy data will be saved as time series in four separate one-dimensional NumPy arrays, corresponding to the total system energy, the potential energy due to self interaction, the potential energy due to the central potential, and the combined contribution of the kinetic and quantum energies. Further discussion regarding energy conservation can be found in code release paper.

Finally, the user may specify the 'save_number', 'step_factor', and 'save_path' parameters. Note that if 'save_number' is large, significant disk space may be used depending on the grid resolution. The 'step_factor' parameter allows the user to accelerate simulations by increasing the size of the timestep, though care should be taken here to ensure convergence is not affected. Meanwhile, if the default 'save_path' value is changed, it is necessary that the user manually create a new directory in which to save the outputs, named accordingly.

3. Set Initial Conditions

In this section the user specifies the mass, position, velocity and phase (in radians) of each soliton initialised in the grid. The user may specify an arbitrary number of solitons, however only those appearing in the 'solitons' list will be initialised. If two or more solitons are initialised with significant overlap, a warning will be displayed at runtime.

4. Run

As the code executes, a progress bar will be displayed in the notebook to indicate the remaining simulation time. Before the main code loop begins, a new subdirectory, labelled with a date/time stamp and the simulation resolution, will be saved in the specified 'save_path' parent directory. Within this subdirectory will be a file called 'config.txt', which will specify the parameters used in the simulation so that it may be reproduced at a later time. As the code runs, the subdirectory will be populated with the outputs as specified by the user in the preceding input cell.

5. Visualisations

This section contains a limited number of basic visualisation tools for preliminary analysis. If the user has specified that the z = 0 plane data be saved, then this section enables either an animation of the density contours over time to be generated, or a static image in which the contours are plotted on the same graph (recommended only when the number of saves is small). If the user has specified that the y = z = 0 line data be saved, then an animation of the density profiles along this axis can be generated. Finally, if energy data has been saved, a plot can be created showing the time evolution of energy. Note that when large amounts of data have been saved, the notebook may take some time to generate animations and plots. Examples of the outputs from this section are given below.


Visualisation examples:



Animation of soliton collision demonstrating interference phenomenon



Animation of orbiting binary soliton system



Energy of orbiting binary soliton system



Tidal disruption of a soliton in a Newtonian central potential


Example configuration for binary soliton collision


Example configuration for tidal disruption of orbiting soliton


Example configuration for orbiting binary soliton system