Source code for yt.frontends.swift.data_structures
import numpy as np
from yt.data_objects.static_output import ParticleFile
from yt.frontends.sph.data_structures import SPHDataset, SPHParticleIndex
from yt.funcs import only_on_root
from yt.utilities.logger import ytLogger as mylog
from yt.utilities.on_demand_imports import _h5py as h5py
from .fields import SwiftFieldInfo
[docs]
class SwiftParticleFile(ParticleFile):
pass
[docs]
class SwiftDataset(SPHDataset):
_load_requirements = ["h5py"]
_index_class = SPHParticleIndex
_field_info_class = SwiftFieldInfo
_file_class = SwiftParticleFile
_particle_mass_name = "Masses"
_particle_coordinates_name = "Coordinates"
_particle_velocity_name = "Velocities"
_sph_ptypes = ("PartType0",)
_suffix = ".hdf5"
def __init__(
self,
filename,
dataset_type="swift",
storage_filename=None,
units_override=None,
unit_system="cgs",
default_species_fields=None,
):
super().__init__(
filename,
dataset_type,
units_override=units_override,
unit_system=unit_system,
default_species_fields=default_species_fields,
)
self.storage_filename = storage_filename
def _set_code_unit_attributes(self):
"""
Sets the units from the SWIFT internal unit system.
Currently sets length, mass, time, and temperature.
SWIFT uses comoving coordinates without the usual h-factors.
"""
units = self._get_info_attributes("Units")
if self.cosmological_simulation == 1:
msg = "Assuming length units are in comoving centimetres"
only_on_root(mylog.info, msg)
self.length_unit = self.quan(
float(units["Unit length in cgs (U_L)"]), "cmcm"
)
else:
msg = "Assuming length units are in physical centimetres"
only_on_root(mylog.info, msg)
self.length_unit = self.quan(float(units["Unit length in cgs (U_L)"]), "cm")
self.mass_unit = self.quan(float(units["Unit mass in cgs (U_M)"]), "g")
self.time_unit = self.quan(float(units["Unit time in cgs (U_t)"]), "s")
self.temperature_unit = self.quan(
float(units["Unit temperature in cgs (U_T)"]), "K"
)
return
def _get_info_attributes(self, dataset):
"""
Gets the information from a header-style dataset and returns it as a
python dictionary.
Example: self._get_info_attributes(header) returns a dictionary of all
of the information in the Header.attrs.
"""
with h5py.File(self.filename, mode="r") as handle:
header = dict(handle[dataset].attrs)
return header
def _parse_parameter_file(self):
"""
Parse the SWIFT "parameter file" -- really this actually reads info
from the main HDF5 file as everything is replicated there and usually
parameterfiles are not transported.
The header information from the HDF5 file is stored in an un-parsed
format in self.parameters should users wish to use it.
"""
# Read from the HDF5 file, this gives us all the info we need. The rest
# of this function is just parsing.
header = self._get_info_attributes("Header")
# RuntimePars were removed from snapshots at SWIFT commit 6271388
# between SWIFT versions 0.8.5 and 0.9.0
with h5py.File(self.filename, mode="r") as handle:
has_runtime_pars = "RuntimePars" in handle.keys()
if has_runtime_pars:
runtime_parameters = self._get_info_attributes("RuntimePars")
else:
runtime_parameters = {}
policy = self._get_info_attributes("Policy")
# These are the parameterfile parameters from *.yml at runtime
parameters = self._get_info_attributes("Parameters")
# Not used in this function, but passed to parameters
hydro = self._get_info_attributes("HydroScheme")
subgrid = self._get_info_attributes("SubgridScheme")
self.domain_right_edge = header["BoxSize"]
self.domain_left_edge = np.zeros_like(self.domain_right_edge)
self.dimensionality = int(header["Dimension"])
# SWIFT is either all periodic, or not periodic at all
if has_runtime_pars:
periodic = int(runtime_parameters["PeriodicBoundariesOn"])
else:
periodic = int(parameters["InitialConditions:periodic"])
if periodic:
self._periodicity = [True] * self.dimensionality
else:
self._periodicity = [False] * self.dimensionality
# Units get attached to this
self.current_time = float(header["Time"])
# Now cosmology enters the fray, as a runtime parameter.
self.cosmological_simulation = int(policy["cosmological integration"])
if self.cosmological_simulation:
try:
self.current_redshift = float(header["Redshift"])
# These won't be present if self.cosmological_simulation is false
self.omega_lambda = float(parameters["Cosmology:Omega_lambda"])
# Cosmology:Omega_m parameter deprecated at SWIFT commit d2783c2
# Between SWIFT versions 0.9.0 and 1.0.0
if "Cosmology:Omega_cdm" in parameters:
self.omega_matter = float(parameters["Cosmology:Omega_b"]) + float(
parameters["Cosmology:Omega_cdm"]
)
else:
self.omega_matter = float(parameters["Cosmology:Omega_m"])
# This is "little h"
self.hubble_constant = float(parameters["Cosmology:h"])
except KeyError:
mylog.warning(
"Could not find cosmology information in Parameters, "
"despite having ran with -c signifying a cosmological "
"run."
)
mylog.info("Setting up as a non-cosmological run. Check this!")
self.cosmological_simulation = 0
self.current_redshift = 0.0
self.omega_lambda = 0.0
self.omega_matter = 0.0
self.hubble_constant = 0.0
else:
self.current_redshift = 0.0
self.omega_lambda = 0.0
self.omega_matter = 0.0
self.hubble_constant = 0.0
# Store the un-parsed information should people want it.
self.parameters = {
"header": header,
"policy": policy,
"parameters": parameters,
# NOTE: runtime_parameters may be empty
"runtime_parameters": runtime_parameters,
"hydro": hydro,
"subgrid": subgrid,
}
# SWIFT never has multi file snapshots
self.file_count = 1
self.filename_template = self.parameter_filename
return
@classmethod
def _is_valid(cls, filename: str, *args, **kwargs) -> bool:
"""
Checks to see if the file is a valid output from SWIFT.
This requires the file to have the Code attribute set in the
Header dataset to "SWIFT".
"""
if cls._missing_load_requirements():
return False
valid = True
# Attempt to open the file, if it's not a hdf5 then this will fail:
try:
handle = h5py.File(filename, mode="r")
valid = handle["Header"].attrs["Code"].decode("utf-8") == "SWIFT"
handle.close()
except (OSError, KeyError):
valid = False
return valid