Source code for pynbody.halo.hop
import os.path
import re
import struct
import numpy as np
from .number_array import HaloNumberCatalogue
[docs]
class HOPCatalogue(HaloNumberCatalogue):
"""Represents a HOP Catalogue as used by Ramses."""
dtype = np.int32
[docs]
def __init__(self, sim, filename=None):
"""Create a new HOP catalogue object from the given simulation object.
Parameters
----------
sim : :class:`pynbody.snapshot.SimSnap`
The simulation snapshot to which this catalogue applies.
filename : str, optional
The filename of the HOP catalogue. The filename should be either the ``grp*.tag`` file or the
``hop*.hop`` file that HOP produces. If no filename is provided, the code will attempt to find a
suitable ``grp*.tag`` file in the Ramses output folder, in the folder in which the simulation is
located, or in a ``hop`` subfolder.
"""
if filename is None:
for filename in HOPCatalogue._enumerate_hop_tag_locations_from_sim(sim):
if os.path.exists(filename):
break
if not os.path.exists(filename):
raise RuntimeError("Unable to find HOP .tag file in simulation directory")
sim._create_array('hop_grp', dtype=self.dtype)
sim['hop_grp'] = -1
with open(filename, "rb") as f:
num_part = self._get_npart_from_file(f)
if num_part != len(sim.dm):
raise RuntimeError("Mismatching number of particles between "
"snapshot %s and HOP file %s. Check your pynbody configuration for any missing"
" particle fields or partial loading" % (sim.filename, filename))
sim.dm['hop_grp'] = np.fromfile(f, self.dtype, len(sim.dm))
super().__init__(sim, array="hop_grp", ignore=-1)
@classmethod
def _get_npart_from_file(cls, f):
num_part, = struct.unpack('i', f.read(4))
if num_part == 8:
# fortran-formatted output
num_part, num_grps, _, _ = struct.unpack('iiii', f.read(16))
else:
# plain binary output
num_grps, = struct.unpack('i', f.read(4))
return num_part
@classmethod
def _can_load(cls, sim, filename=None):
# Hop output must be in output directory or in output_*/hop directory
if filename is not None:
if not os.path.exists(filename):
return False
with open(filename, "rb") as f:
num_part = cls._get_npart_from_file(f)
return num_part == len(sim.dm)
else:
exists = any([os.path.exists(fname) for fname in HOPCatalogue._enumerate_hop_tag_locations_from_sim(sim)])
return exists
@staticmethod
def _extract_hop_name_from_sim(sim):
match = re.search("output_([0-9]*)", sim.filename)
if match is None:
raise OSError("Cannot guess the HOP catalogue filename for %s" % sim.filename)
return "grp%s.tag" % match.group(1)
@staticmethod
def _enumerate_hop_tag_locations_from_sim(sim):
try:
name = HOPCatalogue._extract_hop_name_from_sim(sim)
except OSError:
return []
s_filename = os.path.abspath(sim.filename)
return [os.path.join(os.path.dirname(s_filename),name),
os.path.join(s_filename,name),
os.path.join(s_filename,'hop',name)]