#+ # VIDE -- Void IDentification and Examination -- ./python_tools/fit_hod/readsnap.py # Copyright (C) 2010-2014 Guilhem Lavaux # Copyright (C) 2011-2014 P. M. Sutter # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 of the License. # # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. #+ # routines for reading headers and data blocks from Gadget snapshot files # usage e.g.: # # import readsnap as rs # header = rs.snapshot_header("snap_063.0") # reads snapshot header # print header.massarr # mass = rs.read_block("snap_063","MASS",parttype=5) # reads mass for particles of type 5, using block names should work for both format 1 and 2 snapshots # print "mass for", mass.size, "particles read" # print mass[0:10] # # before using read_block, make sure that the description (and order if using format 1 snapshot files) of the data blocks # is correct for your configuration of Gadget # # for mutliple file snapshots give e.g. the filename "snap_063" rather than "snap_063.0" to read_block # for snapshot_header the file number should be included, e.g."snap_063.0", as the headers of the files differ # # the returned data block is ordered by particle species even when read from a multiple file snapshot import numpy as np import os import sys import math # ----- class for snapshot header ----- class snapshot_header: def __init__(self, filename): if os.path.exists(filename): curfilename = filename elif os.path.exists(filename+".0"): curfilename = filename+".0" else: print("file not found:", filename) sys.exit() self.filename = filename f = open(curfilename,'rb') blocksize = np.fromfile(f,dtype=np.int32,count=1) if blocksize[0] == 8: swap = 0 format = 2 elif blocksize[0] == 256: swap = 0 format = 1 else: blocksize.byteswap(True) if blocksize[0] == 8: swap = 1 format = 2 elif blocksize[0] == 256: swap = 1 format = 1 else: print("incorrect file format encountered when reading header of", filename) sys.exit() self.format = format self.swap = swap if format==2: f.seek(16, os.SEEK_CUR) self.npart = np.fromfile(f,dtype=np.int32,count=6) self.massarr = np.fromfile(f,dtype=np.float64,count=6) self.time = (np.fromfile(f,dtype=np.float64,count=1))[0] self.redshift = (np.fromfile(f,dtype=np.float64,count=1))[0] self.sfr = (np.fromfile(f,dtype=np.int32,count=1))[0] self.feedback = (np.fromfile(f,dtype=np.int32,count=1))[0] self.nall = np.fromfile(f,dtype=np.int32,count=6) self.cooling = (np.fromfile(f,dtype=np.int32,count=1))[0] self.filenum = (np.fromfile(f,dtype=np.int32,count=1))[0] self.boxsize = (np.fromfile(f,dtype=np.float64,count=1))[0] self.omega_m = (np.fromfile(f,dtype=np.float64,count=1))[0] self.omega_l = (np.fromfile(f,dtype=np.float64,count=1))[0] self.hubble = (np.fromfile(f,dtype=np.float64,count=1))[0] if swap: self.npart.byteswap(True) self.massarr.byteswap(True) self.time = self.time.byteswap() self.redshift = self.redshift.byteswap() self.sfr = self.sfr.byteswap() self.feedback = self.feedback.byteswap() self.nall.byteswap(True) self.cooling = self.cooling.byteswap() self.filenum = self.filenum.byteswap() self.boxsize = self.boxsize.byteswap() self.omega_m = self.omega_m.byteswap() self.omega_l = self.omega_l.byteswap() self.hubble = self.hubble.byteswap() f.close() # ----- find offset and size of data block ----- def find_block(filename, format, swap, block, block_num, only_list_blocks=False): if (not os.path.exists(filename)): print("file not found:", filename) sys.exit() f = open(filename,'rb') f.seek(0, os.SEEK_END) filesize = f.tell() f.seek(0, os.SEEK_SET) found = False curblock_num = 1 while ((not found) and (f.tell()=0 and massarr[parttype]>0: if (verbose): print("filling masses according to massarr") return np.ones(nall[parttype],dtype=dt)*massarr[parttype] elif block=="U ": data_for_type[0] = True block_num = 6-blocksub elif block=="RHO ": data_for_type[0] = True block_num = 7-blocksub elif block=="VOL ": data_for_type[0] = True block_num = 8-blocksub elif block=="CMCE": data_for_type[0] = True dt = np.dtype((np.float32,3)) block_num = 9-blocksub elif block=="AREA": data_for_type[0] = True block_num = 10-blocksub elif block=="NFAC": data_for_type[0] = True dt = np.dtype(np.int64) #depends on code version, most recent hast int32, old MyIDType block_num = 11-blocksub elif block=="NE ": data_for_type[0] = True block_num = 8+blockadd-blocksub elif block=="NH ": data_for_type[0] = True block_num = 9+blockadd-blocksub elif block=="HSML": data_for_type[0] = True block_num = 10+blockadd-blocksub elif block=="SFR ": data_for_type[0] = True block_num = 11+blockadd-blocksub elif block=="MHI ": #This is only for the bias_HI project data_for_type[0] = True #This is only for the bias_HI project block_num = 12+blockadd-blocksub #This is only for the bias_HI project elif block=="TEMP": #This is only for the bias_HI project data_for_type[0] = True #This is only for the bias_HI project block_num = 13+blockadd-blocksub #This is only for the bias_HI project elif block=="AGE ": data_for_type[4] = True block_num = 12+blockadd-blocksub elif block=="Z ": data_for_type[0] = True data_for_type[4] = True block_num = 13+blockadd-blocksub elif block=="BHMA": data_for_type[5] = True block_num = 14+blockadd-blocksub elif block=="BHMD": data_for_type[5] = True block_num = 15+blockadd-blocksub else: print("Sorry! Block type", block, "not known!") sys.exit() # - end of block description - actual_data_for_type = np.copy(data_for_type) if parttype >= 0: actual_data_for_type[:] = False actual_data_for_type[parttype] = True if data_for_type[parttype]==False: print("Error: no data for specified particle type", parttype, "in the block", block) sys.exit() elif block=="MASS": actual_data_for_type[:] = True allpartnum = np.int64(0) species_offset = np.zeros(6,np.int64) for j in range(6): species_offset[j] = allpartnum if actual_data_for_type[j]: allpartnum += nall[j] for i in range(filenum): # main loop over files if filenum>1: curfilename = filename+"."+str(i) if i>0: head = snapshot_header(curfilename) npart = head.npart del head curpartnum = np.int32(0) cur_species_offset = np.zeros(6,np.int64) for j in range(6): cur_species_offset[j] = curpartnum if data_for_type[j]: curpartnum += npart[j] if parttype>=0: actual_curpartnum = npart[parttype] add_offset = cur_species_offset[parttype] else: actual_curpartnum = curpartnum add_offset = np.int32(0) offset,blocksize = find_block(curfilename,format,swap,block,block_num) if i==0: # fix data type for ID if long IDs are used if block=="ID ": if blocksize == np.dtype(dt).itemsize*curpartnum * 2: dt = np.uint64 if np.dtype(dt).itemsize*curpartnum != blocksize: print("something wrong with blocksize! expected =",np.dtype(dt).itemsize*curpartnum,"actual =",blocksize) sys.exit() f = open(curfilename,'rb') f.seek(offset + add_offset*np.dtype(dt).itemsize, os.SEEK_CUR) curdat = np.fromfile(f,dtype=dt,count=actual_curpartnum) # read data f.close() if swap: curdat.byteswap(True) if i==0: data = np.empty(allpartnum,dt) for j in range(6): if actual_data_for_type[j]: if block=="MASS" and massarr[j]>0: # add mass block for particles for which the mass is specified in the snapshot header data[species_offset[j]:species_offset[j]+npart[j]] = massarr[j] else: if parttype>=0: data[species_offset[j]:species_offset[j]+npart[j]] = curdat else: data[species_offset[j]:species_offset[j]+npart[j]] = curdat[cur_species_offset[j]:cur_species_offset[j]+npart[j]] species_offset[j] += npart[j] del curdat if physical_velocities and block=="VEL " and redshift!=0: data *= math.sqrt(time) return data # ----- list all data blocks in a format 2 snapshot file ----- def list_format2_blocks(filename): if os.path.exists(filename): curfilename = filename elif os.path.exists(filename+".0"): curfilename = filename+".0" else: print("file not found:", filename) sys.exit() head = snapshot_header(curfilename) format = head.format swap = head.swap del head print('GADGET FORMAT ',format) if (format != 2): print("# OFFSET SIZE") else: print("# BLOCK OFFSET SIZE") print("-------------------------") find_block(curfilename, format, swap, "XXXX", 0, only_list_blocks=True) print("-------------------------") def read_gadget_header(filename): if os.path.exists(filename): curfilename = filename elif os.path.exists(filename+".0"): curfilename = filename+".0" else: print("file not found:", filename) sys.exit() head=snapshot_header(curfilename) print('npar=',head.npart) print('nall=',head.nall) print('a=',head.time) print('z=',head.redshift) print('masses=',head.massarr*1e10,'Msun/h') print('boxsize=',head.boxsize,'kpc/h') print('filenum=',head.filenum) print('cooling=',head.cooling) print('Omega_m,Omega_l=',head.omega_m,head.omega_l) print('h=',head.hubble,'\n') rhocrit=2.77536627e11 #h**2 M_sun/Mpc**3 rhocrit=rhocrit/1e9 #h**2M_sun/kpc**3 Omega_DM=head.nall[1]*head.massarr[1]*1e10/(head.boxsize**3*rhocrit) print('DM mass=',head.massarr[1]*1e10,'Omega_DM=',Omega_DM) if head.nall[2]>0 and head.massarr[2]>0: Omega_NU=head.nall[2]*head.massarr[2]*1e10/(head.boxsize**3*rhocrit) print('NU mass=',head.massarr[2]*1e10,'Omega_NU=',Omega_NU) print('Sum of neutrino masses=',Omega_NU*head.hubble**2*94.1745,'eV')