SWIFTsim (swift.dur.ac.uk) i/o routines for python.
Project description
SWIFTsimIO
The SWIFT astrophysical simulation code (http://swift.dur.ac.uk) is used
widely. There exists many ways of reading the data from SWIFT, which outputs
HDF5 files. These range from reading directly using h5py
to using a complex
system such as yt
; however these either are unsatisfactory (e.g. a lack of
unit information in reading HDF5), or too complex for most use-cases. This
(thin) wrapper provides an object-oriented API to read (dynamically) data
from SWIFT.
Requirements
This requires python3.6.0
or higher. No effort will be made to support
python versions below this. Please update your systems.
Python packages
h5py
unyt
Usage
Example usage is shown below, which plots a density-temperature phase diagram, with density and temperature given in CGS units:
import swiftsimio as sw
# This loads all metadata but explicitly does _not_ read any particle data yet
data = sw.load("/path/to/swift/output")
import matplotlib.pyplot as plt
data.gas.density.convert_to_cgs()
data.gas.temperature.convert_to_cgs()
plt.loglog()
plt.scatter(
data.gas.density,
data.gas.temperature,
s=1
)
plt.xlabel(fr"Gas density $\left[{data.gas.density.units.latex_repr}\right]$")
plt.ylabel(fr"Gas temperature $\left[{data.gas.temperature.units.latex_repr}\right]$")
plt.tight_layout()
plt.savefig("test_plot.png", dpi=300)
In the above it's important to note the following:
- All metadata is read in when the
load
function is called. - Only the density and temperature (corresponding to the
PartType0/Density
andPartType0/Temperature
) datasets are read in. - That data is only read in once the
convert_to_cgs
method is called. convert_to_cgs
converts data in-place; i.e. it returnsNone
.- The data is cached and not re-read in when
plt.scatter
is called.
Writing datasets
Writing datasets that are valid for consumption for cosmological codes can be
difficult, especially when considering how to best use units. SWIFT uses a
different set of internal units (specified in your parameter file) that does
not necessarily need to be the same set of units that initial conditions are
specified in. Nevertheless, it is important to ensure that units in the
initial conditions are all consistent with each other. To facilitate this,
we use unyt
arrays. The below example generates randomly placed gas
particles with uniform densities.
from swiftsimio import Writer
from swiftsimio.units import cosmo_units
import unyt
import numpy as np
# Box is 100 Mpc
boxsize = 100 * unyt.Mpc
# Generate object. cosmo_units corresponds to default Gadget-oid units
# of 10^10 Msun, Mpc, and km/s
x = Writer(cosmo_units, boxsize)
# 32^3 particles.
n_p = 32**3
# Randomly spaced coordinates from 0, 100 Mpc in each direction
x.gas.coordinates = np.random.rand(n_p, 3) * (100 * unyt.Mpc)
# Random velocities from 0 to 1 km/s
x.gas.velocities = np.random.rand(n_p, 3) * (unyt.km / unyt.s)
# Generate uniform masses as 10^6 solar masses for each particle
x.gas.masses = np.ones(n_p, dtype=float) * (1e6 * unyt.msun)
# Generate internal energy corresponding to 10^4 K
x.gas.internal_energy = np.ones(n_p, dtype=float) * (1e4 * unyt.kb * unyt.K) / (1e6 * unyt.msun)
# Generate initial guess for smoothing lengths based on MIPS
x.gas.generate_smoothing_lengths(boxsize=boxsize, dimension=3)
# If IDs are not present, this automatically generates
x.write("test.hdf5")
Then, running h5glance
on the resulting test.hdf5
produces:
test.hdf5
├Header
│ └5 attributes:
│ ├BoxSize: 100.0
│ ├Dimension: array [int64: 1]
│ ├Flag_Entropy_ICs: 0
│ ├NumPart_Total: array [int64: 6]
│ └NumPart_Total_HighWord: array [int64: 6]
├PartType0
│ ├Coordinates [float64: 32768 × 3]
│ ├InternalEnergy [float64: 32768]
│ ├Masses [float64: 32768]
│ ├ParticleIDs [float64: 32768]
│ ├SmoothingLength [float64: 32768]
│ └Velocities [float64: 32768 × 3]
└Units
└5 attributes:
├Unit current in cgs (U_I): array [float64: 1]
├Unit length in cgs (U_L): array [float64: 1]
├Unit mass in cgs (U_M): array [float64: 1]
├Unit temperature in cgs (U_T): array [float64: 1]
└Unit time in cgs (U_t): array [float64: 1]
Note you do need to be careful that your choice of unit system does not allow values over 2^31, i.e. you need to ensure that your provided values (with units) when written to the file are safe to be interpreted as (single-precision) floats. The only exception to this is coordinates which are stored in double precision.
Ahead-of-time Masking
SWIFT snapshots contain cell metadata that allow us to spatially mask the
data ahead of time. swiftsimio
provides a number of objects that help with
this. See the example below.
import swiftsimio as sw
# This creates and sets up the masking object.
mask = sw.mask("/path/to/swift/snapshot")
# This ahead-of-time creates a spatial mask based on the cell metadata.
mask.constrain_spatial([[0.2 * mask.metadata.boxsize[0], 0.7 * mask.metadata.boxsize[0]], None, None])
# Now, just for fun, we also constrain the density between 0.4 g/cm^3 and 0.8. This reads in
# the relevant data in the region, and tests it element-by-element.
density_units = mask.units.mass / mask.units.length**3
mask.constrain_mask("gas", "density", 0.4 * density_units, 0.8 * density_units)
# Now we can grab the actual data object. This includes the mask as a parameter.
data = sw.load("/Users/josh/Documents/swiftsim-add-anarchy/examples/SodShock_3D/sodShock_0001.hdf5", mask=mask)
When the attributes of this data object are accessed, transparently only the ones that belong to the masked region (in both density and spatial) are read. I.e. if I ask for the temperature of particles, it will recieve an array containing temperatures of particles that lie in the region [0.2, 0.7] and have a density between 0.4 and 0.8 g/cm^3.
User-defined particle types
It is now possible to add user-defined particle types that are not already
present in the swiftsimio
metadata. All you need to do is specify the three
names (see below) and then the particle datasets that you have provided in
SWIFT will be automatically read.
import swiftsimio as sw
import swiftsimio.metadata.particle as swp
from swiftsimio.objects import cosmo_factor, a
swp.particle_name_underscores[6] = "extratype"
swp.particle_name_class[6] = "Extratype"
swp.particle_name_text[6] = "Extratype"
data = sw.load(
"extra_test.hdf5",
)
Previously, there was a generator function API that performed this task; this has now been removed (last version was v0.8.0).
Image Creation
swiftsimio
provides some very basic visualisation software that is designed
to create projections of the entire box along the z-axis, with x and y
representing the final square. Note that it only currently supports square
boxes. These are accelerated with numba
, and we do not suggest using these
routines unless you have it installed. Finally, you can use parallel versions
of these functions (by default they run in serial) by passing the argument
parallel=True
.
You can do the following:
from swiftsimio import load
from swiftsimio.visualisation import project_gas
data = load("my_snapshot_0000.hdf5")
# This creates a grid that has units K / Mpc^2, and can be transformed like
# any other unyt quantity
temperature_map = project_gas(data, resolution=1024, project="temperature")
from matplotlib.pyplot import imsave
from matplotlib.colors import LogNorm
# There is huge dynamic range usually in temperature, so log normalize it before
# saving.
imsave("temperature_map.png", LogNorm()(temperature_map.value), cmap="twilight")
It's also possible to create videos in a fairly straightforward way:
from swiftsimio import load
from swiftsimio.visualisation import project_gas_pixel_grid
# project_gas_pixel_grid does not perform the unit calculation.
from p_tqdm import p_map
def load_and_make_image(number):
filename = f"snapshot_{number:04d}.hdf5"
data = load(filename)
image = project_gas_pixel_grid(data, 1024, "temperature")
return image
# Make frames in parallel (reading also parallel!)
frames = p_map(load_and_make_image, range(0, 120))
#... You can do your funcAnimation stuff here now you have the frames.
It is also possible to create slice plots, rather than projections.
from swiftsimio import load
from swiftsimio.visualisation import slice_gas
data = load("my_snapshot_0000.hdf5")
# This creates a grid that has units K / Mpc^3, and can be transformed like
# any other unyt quantity. The slice variable gives where we want to slice through
# as a function of the box-size, so here we slice through the centre of the box.
temperature_map = slice_gas(data, slice=0.5, resolution=1024, project="temperature")
from matplotlib.pyplot import imsave
from matplotlib.colors import LogNorm
# There is huge dynamic range usually in temperature, so log normalize it before
# saving.
imsave("temperature_map.png", LogNorm()(temperature_map.value), cmap="twilight")
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file swiftsimio-1.1.0.tar.gz
.
File metadata
- Download URL: swiftsimio-1.1.0.tar.gz
- Upload date:
- Size: 31.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/40.6.2 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.7.4
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | eff7fbe9de3a50c88eab179d88d8b81e59caf76d5f48cfe3ebeacdf49e6dcaf8 |
|
MD5 | affb37c97e1a008fc8ba694f7261b1fd |
|
BLAKE2b-256 | d2f3df4c73dbad2943776a61c0ae8f2da69cbe7a08cd3c0879d76df261a27dd6 |
File details
Details for the file swiftsimio-1.1.0-py3-none-any.whl
.
File metadata
- Download URL: swiftsimio-1.1.0-py3-none-any.whl
- Upload date:
- Size: 43.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/40.6.2 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.7.4
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | bd4e2fdac8f4b6e683e10baf64391a5721e67fd8b79510c8ae5dfd260315013e |
|
MD5 | 33203617c92d6a06c12205a5a3fe9437 |
|
BLAKE2b-256 | e39b01a03d1842d763fc82dbbb231fe671cd3a7301c9be004b91f2ff111ec0e1 |