Skip to content
Snippets Groups Projects
readArray.hpp 29.95 KiB
#ifndef READ_ARRAY_HPP
#define READ_ARRAY_HPP


#include <vector>
#include <string>
#include <sstream>
#include <iterator>
#include <cmath>
#include <cassert>
#include <hdf5.h>
#include <sys/time.h>
#include <pthread.h>

#include "vector.hpp"
#include "tuple.hpp"
#include "error.hpp"

enum fileType{FOF,            //FOF group arrays
	      FOF_PARTICLES,  //Particle info in FOF files
	      SNIP_FOF,            //FOF group arrays
	      SNIP_FOF_PARTICLES,  //Particle info in FOF files
	      SNAP, 
	      SNIP, 
	      SUBFIND,        // Subfind sub-halo arrays
	      SUBFIND_GROUP,  // Subfind halo arrays
	      SUBFIND_IDS,
	      SNIP_SUBFIND,        // Subfind sub-halo arrays
	      SNIP_SUBFIND_GROUP,  // Subfind halo arrays
	      SNIP_SUBFIND_IDS,
	      PARTDATA,
	      SNIP_PARTDATA
};

template<typename T> inline const hid_t hdf5Type();
template<> inline const hid_t hdf5Type<char*>(){return H5T_C_S1; }
template<> inline const hid_t hdf5Type<int>(){return H5T_NATIVE_INT; }
template<> inline const hid_t hdf5Type<size_t>(){return H5T_NATIVE_UINT64; }
template<> inline const hid_t hdf5Type<unsigned int>(){return H5T_NATIVE_UINT32; }
template<> inline const hid_t hdf5Type<unsigned long long>(){return H5T_NATIVE_UINT64; }
template<> inline const hid_t hdf5Type<float>(){return H5T_NATIVE_FLOAT; }
template<> inline const hid_t hdf5Type<double>(){return H5T_NATIVE_DOUBLE; }
template<> inline const hid_t hdf5Type<Tuple<6, int> >(){return H5T_NATIVE_INT; }
template<> inline const hid_t hdf5Type<Tuple<6, unsigned int> >(){return H5T_NATIVE_UINT32; }
template<> inline const hid_t hdf5Type<Tuple<6, unsigned long long> >(){return H5T_NATIVE_UINT64; }
template<> inline const hid_t hdf5Type<Tuple<6, size_t> >(){return H5T_NATIVE_UINT64; }
template<> inline const hid_t hdf5Type<Tuple<6, float> >(){return H5T_NATIVE_FLOAT; }
template<> inline const hid_t hdf5Type<Tuple<6, double> >(){return H5T_NATIVE_DOUBLE; }

/**
 * \brief Returns the name of the file for a given directory, tag, fileNumber and type of file
 */
inline std::string fileName(fileType type, const std::string& dir, const std::string& tag, int i=0)
{
  std::ostringstream oss;
  switch(type)
    {
    case FOF:
    case FOF_PARTICLES:
      oss << dir << "/groups_" << tag << "/group_tab_" << tag << "." << i << ".hdf5";
      return oss.str();
    case SNIP_FOF:
    case SNIP_FOF_PARTICLES:
      oss << dir << "/groups_snip_" << tag << "/group_snip_tab_" << tag << "." << i << ".hdf5";
      return oss.str();
    case SUBFIND:
    case SUBFIND_GROUP:
    case SUBFIND_IDS:
      oss << dir << "/groups_" << tag << "/eagle_subfind_tab_" << tag << "." << i << ".hdf5";
      return oss.str();
    case SNIP_SUBFIND:
    case SNIP_SUBFIND_GROUP:
    case SNIP_SUBFIND_IDS:
      oss << dir << "/groups_snip_" << tag << "/eagle_subfind_snip_tab_" << tag << "." << i << ".hdf5";
      return oss.str();
    case SNIP:
      oss << dir << "/snipshot_" << tag << "/snip_" << tag << "." << i << ".hdf5";
      return oss.str();
    case SNAP:
      oss << dir << "/snapshot_" << tag << "/snap_" << tag << "." << i << ".hdf5";
      return oss.str();
    case PARTDATA:
      oss << dir << "/particledata_" << tag << "/eagle_subfind_particles_" << tag << "." << i << ".hdf5";
      return oss.str();
    case SNIP_PARTDATA:
      oss << dir << "/particledata_snip_" << tag << "/eagle_subfind_snip_particles_" << tag << "." << i << ".hdf5";
      return oss.str();
    default:
      error("Type of files not supported");
    }
}

inline std::string numFilesFieldName(fileType type)
{
  switch(type)
    {
    case FOF:
    case FOF_PARTICLES:
    case SNIP_FOF:
    case SNIP_FOF_PARTICLES:
    case SUBFIND:
    case SUBFIND_GROUP:
    case SUBFIND_IDS:
    case SNIP_SUBFIND:
    case SNIP_SUBFIND_GROUP:
    case SNIP_SUBFIND_IDS:
      return "/Header/NTask";
    case SNAP:
    case SNIP:
    case PARTDATA:
    case SNIP_PARTDATA:
      return "/Header/NumFilesPerSnapshot";
    default:
      error("Type of file unknown");
    }
}


/**
 * \brief Returns the header field containg the total number of elements
 */
inline std::string totElementFieldName(fileType type)
{
  switch(type)
    {
    case FOF_PARTICLES: 
    case SNIP_FOF_PARTICLES: 
      return "/Header/ToTNids";
    case FOF: 
    case SUBFIND_GROUP:
    case SNIP_FOF: 
    case SNIP_SUBFIND_GROUP:
      return "/Header/TotNgroups";
    case SUBFIND: 
    case SNIP_SUBFIND: 
      return "/Header/TotNsubgroups";
    case SUBFIND_IDS:
    case SNIP_SUBFIND_IDS:
      return "/Header/TotNids";
    case SNAP:
    case SNIP:
    case PARTDATA: 
    case SNIP_PARTDATA: 
      return "/Header/NumPart_Total";
    default:
      error("Type of file unknown");
    }
}

/**
 * \brief Returns the header field containg the number of elements in this file
 */
inline std::string elementFieldName(fileType type)
{
  switch(type)
    {
    case FOF_PARTICLES: 
    case SUBFIND_IDS:
    case SNIP_FOF_PARTICLES: 
    case SNIP_SUBFIND_IDS:
      return "/Header/Nids";
    case FOF: 
    case SUBFIND_GROUP:
    case SNIP_FOF:
    case SNIP_SUBFIND_GROUP:
      return "/Header/Ngroups";
    case SUBFIND: 
    case SNIP_SUBFIND: 
      return "/Header/Nsubgroups";
    case SNAP:
    case SNIP:
    case PARTDATA: 
    case SNIP_PARTDATA: 
      return "/Header/NumPart_ThisFile";
    default:
      error("Type of file unknown");
    }
}

/**
 * \brief Returns the name of this fileType
 */
inline std::string fileTypeName(fileType type)
{
  switch(type)
    {
    case FOF: return "FOF";
    case SNIP_FOF: return "FOF (snipshot)";
    case FOF_PARTICLES: return "FOF (particle info)";
    case SNIP_FOF_PARTICLES: return "FOF (particle info, snipshot)";
    case SUBFIND: return "Subfind";
    case SUBFIND_GROUP: return "Subfind (group tabs)";
    case SUBFIND_IDS: return "Subfind (IDs)";
    case SNIP_SUBFIND: return "Subfind (snipshot)";
    case SNIP_SUBFIND_GROUP: return "Subfind (group tabs, snipshot)";
    case SNIP_SUBFIND_IDS: return "Subfind (IDs, snipshot)";
    case SNIP: return "Snipshot";
    case SNAP: return "Snapshot";
    case PARTDATA: return "ParticleData";
    case SNIP_PARTDATA: return "ParticleData (snipshot)";
    default:
      error("Type of file unknown");
    }
}

inline int getGadgetType(fileType type, const std::string& arrayName)
{
  switch(type)
    {
    case FOF:
    case FOF_PARTICLES:
    case SUBFIND:
    case SUBFIND_GROUP:
    case SUBFIND_IDS:
    case SNIP_FOF:
    case SNIP_FOF_PARTICLES:
    case SNIP_SUBFIND:
    case SNIP_SUBFIND_GROUP:
    case SNIP_SUBFIND_IDS:
      return 0;
    case SNIP:
    case SNAP:
    case PARTDATA:
    case SNIP_PARTDATA:
      if(arrayName[0] != '/')
	error("Ill-formed array name. Must start with a /");
      return atoi(arrayName.substr(9,1).c_str());
    default:
      error("Type of file unknown");
    }
}


/**
 *\brief  Returns a vector of strings containing the different elements of a path of the form /aaa/bbb/ccc
 */
inline std::vector<std::string> tokenizePath(const std::string& path)
{
  std::vector<std::string> tokens;
  const char delimiter='/';
  std::string::size_type lastPos = path.find_first_not_of(delimiter, 0);
  std::string::size_type pos     = path.find_first_of(delimiter, lastPos);
  while (pos != std::string::npos || lastPos !=std::string::npos)
    {
      tokens.push_back(path.substr(lastPos, pos - lastPos));
      lastPos = path.find_first_not_of(delimiter, pos);
      pos = path.find_first_of(delimiter, lastPos);
    }
  return tokens;
}

/**
 * \brief Returns the value of the attribute 'name' in an open HDF5 group.
 */
template<typename T>
inline T readAttributeFromGroup(hid_t grp, const std::string& name)
{
  T data;

  const hid_t h_attr = H5Aopen(grp, name.c_str(), H5P_DEFAULT);
  if(h_attr < 0)
    error("Impossible to open attribute");

  const herr_t h_err = H5Aread(h_attr, hdf5Type<T>(), &data);
  if(h_err < 0)
    error("Impossible to read attribute");

  H5Aclose(h_attr);
  return data;
}


/**
 * \brief Returns the value of the attribute 'name' in an open HDF5 file.
 */
template<typename T>
T readAttribute(hid_t h_file, const std::string& name)
{
  const std::vector<std::string> tokens = tokenizePath(name);
  std::vector<hid_t> groups(tokens.size(), 0);

  groups[0] = h_file;
  for(size_t i(1); i<groups.size(); ++i)
    groups[i] = H5Gopen1(groups[i-1], (tokens[i-1]).c_str());
    
  const T attr = readAttributeFromGroup<T>(groups.back(), tokens.back().c_str());

  for(size_t i(1); i<groups.size(); ++i)
    H5Gclose(groups[i]);
  return attr;
}

/**
 * \brief Returns the value of the attribute 'name' in an HDF5 file given by its dir and tag.
 */
template<typename T>
inline T readAttribute(fileType type, const std::string& dir, const std::string& tag, const std::string& name)
{
 
  const std::string fname=fileName(type, dir, tag);
  const hid_t h_file = H5Fopen(fname.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
  const T attr = readAttribute<T>(h_file, name);
  H5Fclose(h_file);
  return attr;
}



/**
 * \brief Returns the type of the attribute 'name' in an open HDF5 group.
 */
inline hid_t readAttributeTypeFromGroup(hid_t grp, const std::string& name)
{
  const hid_t h_attr = H5Aopen(grp, name.c_str(), H5P_DEFAULT);
  if(h_attr < 0)
    error("Impossible to open attribute");

  const herr_t h_type = H5Aget_type(h_attr);
  if(h_type < 0)
    error("Impossible to read attribute type");

  H5Aclose(h_attr);
  return h_type;
}


/**
 * \brief Returns the type of the attribute 'name' in an open HDF5 file.
 */
inline hid_t readAttributeType(hid_t h_file, const std::string& name)
{
  const std::vector<std::string> tokens = tokenizePath(name);
  std::vector<hid_t> groups(tokens.size(), 0);

  groups[0] = h_file;
  for(size_t i(1); i<groups.size(); ++i)
    groups[i] = H5Gopen1(groups[i-1], (tokens[i-1]).c_str());
    
  const hid_t attr = readAttributeTypeFromGroup(groups.back(), tokens.back().c_str());

  for(size_t i(1); i<groups.size(); ++i)
    H5Gclose(groups[i]);
  return attr;
}


/**
 * \brief Returns the HDF5 type of a given attribute 'name' in an HDF5 file given its dir and tag.
 */
inline hid_t readAttributeType(fileType type, const std::string& dir, const std::string& tag, const std::string& name)
{
  const std::string fname=fileName(type, dir, tag);
  const hid_t h_file = H5Fopen(fname.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
  const hid_t h_type = readAttributeType(h_file, name);
  H5Fclose(h_file);
  return h_type;
}

/**
 * \brief Returns the size of the attribute 'name' in an open HDF5 group.
 */
inline hid_t readAttributeSizeFromGroup(hid_t grp, const std::string& name)
{
  const hid_t h_attr = H5Aopen(grp, name.c_str(), H5P_DEFAULT);
  if(h_attr < 0)
    error("Impossible to open attribute");

  const hid_t h_space = H5Aget_space(h_attr);
  if(h_space < 0)
    error("Impossible to open attribute space");

  const htri_t isSimple = H5Sis_simple(h_space);
  if(isSimple < 0)
    error("Impossible to extract information about the attribute's dataspace");
  else if(isSimple == 0)
    error("Attribute's dataspace is not simple");
    
  hsize_t dims[1];
  const int ndims = H5Sget_simple_extent_dims(h_space, dims, 0);
  if(ndims < 0)
    error("Attribute's dataspace is weird");
  else if(ndims == 0)
    dims[0] = 1;

  H5Sclose(h_space);
  H5Aclose(h_attr);
  return dims[0];
}


/**
 * \brief Returns the size of the attribute 'name' in an open HDF5 file.
 */
inline int readAttributeSize(hid_t h_file, const std::string& name)
{
  const std::vector<std::string> tokens = tokenizePath(name);
  std::vector<hid_t> groups(tokens.size(), 0);

  groups[0] = h_file;
  for(size_t i(1); i<groups.size(); ++i)
    groups[i] = H5Gopen1(groups[i-1], (tokens[i-1]).c_str());
    
  const int size = readAttributeSizeFromGroup(groups.back(), tokens.back().c_str());

  for(size_t i(1); i<groups.size(); ++i)
    H5Gclose(groups[i]);
  return size;
}

/**
 * \brief Returns the size of a given attribute 'name' in an HDF5 file given its dir and tag.
 */
inline int readAttributeSize(fileType type, const std::string& dir, const std::string& tag, const std::string& name)
{
  const std::string fname=fileName(type, dir, tag);
  const hid_t h_file = H5Fopen(fname.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
  const int size = readAttributeSize(h_file, name);
  H5Fclose(h_file);
  return size;
}


/**
 * \brief Returns the value of the attribute 'name' in an open HDF5 file.
 */
template<typename T>
T readUnits(hid_t h_file, const std::string& name)
{
  const std::vector<std::string> tokens = tokenizePath(name);
  std::vector<hid_t> groups(tokens.size(), 0);

  groups[0] = h_file;
  for(size_t i(1); i<groups.size()-1; ++i)
    groups[i] = H5Gopen1(groups[i-1], (tokens[i-1]).c_str());
    
  const hid_t h_data = H5Dopen1(groups[groups.size()-2], tokens[groups.size()-2].c_str());
  const T attr = readAttributeFromGroup<T>(h_data, tokens.back().c_str());
  H5Dclose(h_data);

  for(size_t i(1); i<groups.size()-1; ++i)
    H5Gclose(groups[i]);
  return attr;
}



/**
 * \brief Returns the type of the attribute 'name' in an open HDF5 group.
 */
inline hid_t readArrayTypeFromGroup(hid_t grp, const std::string& name)
{
  const hid_t h_attr = H5Dopen(grp, name.c_str(), H5P_DEFAULT);
  if(h_attr < 0)
    error("Impossible to open array");

  const herr_t h_type = H5Dget_type(h_attr);
  if(h_type < 0)
    error("Impossible to read array type");

  H5Dclose(h_attr);
  return h_type;
}


/**
 * \brief Returns the type of the attribute 'name' in an open HDF5 file.
 */
inline hid_t readArrayType(hid_t h_file, const std::string& name)
{
  const std::vector<std::string> tokens = tokenizePath(name);
  std::vector<hid_t> groups(tokens.size(), 0);

  // for(int i(0); i<tokens.size();++i)
  //   std::cout << tokens[i] << std::endl;

  groups[0] = h_file;
  for(size_t i(1); i<groups.size(); ++i)
    {
      // std::cout << "Opening group '" << tokens[i-1] << "'" <<std::endl;
      groups[i] = H5Gopen1(groups[i-1], (tokens[i-1]).c_str());
      // std::cout << "Group open" << std::endl;
    }
    
  const hid_t attr = readArrayTypeFromGroup(groups.back(), tokens.back().c_str());

  for(size_t i(1); i<groups.size(); ++i)
    H5Gclose(groups[i]);
  return attr;
}

/**
 * \brief Returns the HDF5 type of a given attribute 'name' in an HDF5 file given its dir and tag.
 */
inline hid_t readArrayType(fileType type, const std::string& dir, const std::string& tag, const std::string& name)
{
  const std::string fname=fileName(type, dir, tag);
  const hid_t h_file = H5Fopen(fname.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
  const hid_t h_type = readArrayType(h_file, name);
  H5Fclose(h_file);
  return h_type;
}

/**
 * \brief Returns the size of the array 'name' in an open HDF5 group.
 */
inline hid_t readArraySizeFromGroup(hid_t grp, const std::string& name)
{
  const hid_t h_attr = H5Dopen(grp, name.c_str(), H5P_DEFAULT);
  if(h_attr < 0)
    error("Impossible to open dataset");

  const hid_t h_space = H5Dget_space(h_attr);
  if(h_space < 0)
    error("Impossible to open data space");

  const htri_t isSimple = H5Sis_simple(h_space);
  if(isSimple < 0)
    error("Impossible to extract information about the array's dataspace");
  else if(isSimple == 0)
    error("array's dataspace is not simple");
    
  hsize_t dims[2];
  const int ndims = H5Sget_simple_extent_dims(h_space, dims, 0);
  if(ndims < 0)
    error("Array's dataspace is weird");

  H5Sclose(h_space);
  H5Dclose(h_attr);
  if(ndims == 1)
    return 1;
  else if(ndims == 2)
    return dims[1];
  else
    error("Array's dataspace is weird");
}


/**
 * \brief Returns the size of the array 'name' in an open HDF5 file.
 */
inline int readArraySize(hid_t h_file, const std::string& name)
{
  const std::vector<std::string> tokens = tokenizePath(name);
  std::vector<hid_t> groups(tokens.size(), 0);

  groups[0] = h_file;
  for(size_t i(1); i<groups.size(); ++i)
    groups[i] = H5Gopen1(groups[i-1], (tokens[i-1]).c_str());
    
  const int size = readArraySizeFromGroup(groups.back(), tokens.back().c_str());

  for(size_t i(1); i<groups.size(); ++i)
    H5Gclose(groups[i]);
  return size;
}

/**
 * \brief Returns the size of a given array 'name' in an HDF5 file given its dir and tag.
 */
inline int readArraySize(fileType type, const std::string& dir, const std::string& tag, const std::string& name)
{
  const std::string fname=fileName(type, dir, tag);
  const hid_t h_file = H5Fopen(fname.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
  const int size = readArraySize(h_file, name);
  H5Fclose(h_file);
  return size;
}



template<typename T, typename S> 
void readArray(hid_t file, const std::string& name, S* data, size_t offset=0)
{
  const hid_t h_data = H5Dopen1(file, name.c_str());
  if(h_data < 0)
    error("Impossible to open file");

  const hid_t h_space = H5Dget_space(h_data);
  if(h_space < 0)
    error("Impossible to open dataspace");

  hsize_t dims[2], dummy[2];
  int rank = H5Sget_simple_extent_dims(h_space, dims, dummy);

  if(rank == 2)
    offset *= dims[1];

  const hid_t h_err = H5Dread(h_data, hdf5Type<T>(), H5S_ALL, H5S_ALL, H5P_DEFAULT, reinterpret_cast<T*>(data) + offset);
  if(h_err < 0)
    error("Impossible to read dataset");

  H5Dclose(h_data);
}


/**
 * \brief Reads the content of the dataset 'name' into the vector 'data' in an open HDF5 file.
 */
template<typename T, typename S> 
inline void readArray(hid_t file, const std::string& name, std::vector<S>& data, size_t offset=0)
{
  readArray(file, name, &data[0], offset);
}

template <typename S>
struct thread_info
{
  int id;
  int numSnap;
  int numThreads;
  fileType type;
  int partType;
  std::string dir;
  std::string tag;
  std::string arrayName;
  S* array;
  size_t* sizes;
  size_t* offsets;
  bool oldSubfindFix;
};

template<typename S>
void* thread_readSize(void *thread_data)
{
  thread_info<S> t_data = *reinterpret_cast<thread_info<S>*>( thread_data );

  for(int i(t_data.id); i<t_data.numSnap; i+=t_data.numThreads)
    {
      //std::cerr << "Thread " << t_data.id << "/" << t_data.numThreads << " reading file " << i << "/" << t_data.numSnap  << std::endl;
      const hid_t file = H5Fopen(fileName(t_data.type, t_data.dir, t_data.tag, i).c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
      if(file == 0)
	error("Impossible to open file");

      if(!t_data.oldSubfindFix)
	t_data.sizes[i] = readAttribute<Tuple<6, int> >(file, elementFieldName(t_data.type))[t_data.partType];
      else
	{
	  t_data.sizes[i] = readAttribute<Tuple<6, int> >(file, "/Header/NumPart_ThisFile")[2];
	}

      H5Fclose(file);
    }
  return 0;
}

template<typename T, typename S>
void* thread_readData(void *thread_data)
{
  thread_info<S> t_data = *reinterpret_cast<thread_info<S>*>( thread_data );

  for(int i(t_data.id); i<t_data.numSnap; i+=t_data.numThreads)
    {
      // std::cerr << "Thread " << t_data.id << "/" << t_data.numThreads << " reading file " << i << "/" << t_data.numSnap << " " << t_data.arrayName << " " <<  t_data.array << " " <<  t_data.offsets[i] << " " << t_data.type << std::endl;
      const hid_t file = H5Fopen(fileName(t_data.type, t_data.dir, t_data.tag, i).c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
      if(file == 0)
	error("Impossible to open file");
      if(t_data.sizes[i] > 0)
	{
	  readArray<T>(file, t_data.arrayName, t_data.array, t_data.offsets[i]);
	}
      H5Fclose(file);

      // std::cerr << "Done" << std::endl;
    }

  return 0;
}

/**
 * \brief Reads the content of the dataset 'arrayName' into the vector 'array' for a given file (type, dir, tag)
 */
template<typename T, typename S>
void readArray(fileType type, const std::string& dir, const std::string& tag, const std::string& arrayName, std::vector<S>& array, bool physicalUnits=true, bool noH=true, int numThreads=16, bool verbose=true)
{
  error("Strange function call");

  /* Check that we have at least one thread */
  if(numThreads < 1)
    error("Invalid number of threads. Value must be >0.");

 /* Open file #0 to read global information */  
  const hid_t h_file = H5Fopen(fileName(type, dir, tag).c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
  if(h_file < 0)
    error("Impossible to read file #0");

  /* Decide which of the 6 values for the number of elements to read if we are dealing with a sn[ai]pshot */
  const int partType = getGadgetType(type, arrayName);

  /* Decide which of the 6 values for the number of elements to read if we are dealing with a sn[ai]pshot */
  const int numSnap = readAttribute<int>(h_file, numFilesFieldName(type));
  const size_t totNumElem = readAttribute<Tuple<6, int> >(h_file, totElementFieldName(type))[partType];
  const double hubbleParam = readAttribute<double>(h_file, "/Header/HubbleParam");
  const double a = readAttribute<double>(h_file, "/Header/ExpansionFactor");
  H5Fclose(h_file);
 
  if(verbose)
    std::cout << "Extracting " << totNumElem << " values for '"<< arrayName << "' (PartType=" << partType << ") in " << numSnap << " " << fileTypeName(type) << " files with tag '" << tag << "' using " << numThreads << " thread(s)." << std::endl;
  
  /* Prepare array */
  array.resize(totNumElem);
  if(totNumElem == 0)
    return;

  /* Separate arrays for parallel reads */
  std::vector<size_t> sizes(numSnap, 0);
  std::vector<size_t> offsets(numSnap, 0);
  
  /* Prepare data for threads*/
  pthread_t* threads = new pthread_t[numThreads];
  thread_info<S>* thread_out = new thread_info<S>[numThreads];
  for(int i(0); i<numThreads; ++i)
    {
      thread_out[i].id = i;
      thread_out[i].numSnap = numSnap;
      thread_out[i].numThreads = numThreads;
      thread_out[i].type = type;
      thread_out[i].partType = partType;
      thread_out[i].dir = dir;
      thread_out[i].tag = tag;
      thread_out[i].arrayName = arrayName;
      thread_out[i].array = &array[0];
      thread_out[i].sizes = &sizes[0];
      thread_out[i].offsets = &offsets[0];
      thread_out[i].oldSubfindFix = false;
    }

  timeval begin, end;
  gettimeofday(&begin, NULL);

  if(verbose)
    std::cout << "Reading array lengths" << std::flush;

  /* Compute sizes for parallel reads using threads */
  for(int i(0); i<numThreads; ++i)
    {
      const int ret = pthread_create(&threads[i], 0, thread_readSize<S>, &thread_out[i]);
      if(ret != 0)
	error("Error while launching thread");
    }

  for (int i(0); i < numThreads; ++i) 
    {
      const int ret = pthread_join(threads[i], 0);
      if(ret != 0)
	error("Error while stopping thread");
    } 

  gettimeofday(&end, NULL);  
  if(verbose)
    std::cout << " took " << ((end.tv_sec-begin.tv_sec)*1000000 + end.tv_usec-begin.tv_usec) / 1e6 << "s" << std::endl;


  /* Do a scan to convert from size to offset */
  for(int i(1); i<numSnap; ++i)
    {
      offsets[i] = offsets[i-1] + sizes[i-1];
      //      std::cout << i << " " << offsets[i] << " " << sizes[i] << std::endl;
    }


  /* Read data from files using threads */
  gettimeofday(&begin, NULL);
  if(verbose)
    std::cout << "Reading data" << std::flush;

  for(int i(0); i<numThreads; ++i)
    {
      const int ret = pthread_create(&threads[i], 0, thread_readData<T,S>, &thread_out[i]);
      if(ret != 0)
	error("Error while launching thread");
    }

  for (int i(0); i < numThreads; ++i) 
    {
      const int ret = pthread_join(threads[i], 0);
      if(ret != 0)
	error("Error while stopping thread");
    } 

  gettimeofday(&end, NULL);  
  if(verbose)
    std::cout << " took " << ((end.tv_sec-begin.tv_sec)*1000000 + end.tv_usec-begin.tv_usec) / 1e6 << "s" << std::endl;

  /* Find a non-empty file for unit conversion */
  int nonEmpty = -1;
  for(int i(0); i<numSnap; ++i)
    {
      if(sizes[i] > 0)
	{
	  nonEmpty = i;
	  break;
	}
    }
  //debug_variable(nonEmpty);

  /* Read factors for unit conversion */
  const hid_t h_file2 = H5Fopen(fileName(type, dir, tag, nonEmpty).c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
  const float a_exponant = readUnits<float>(h_file2, arrayName + "/aexp-scale-exponent");
  const float h_exponant = readUnits<float>(h_file2, arrayName + "/h-scale-exponent");
  H5Fclose(h_file2);

  /* Apply comoving to physical units conversion */
  if(physicalUnits)
    {
      if(a_exponant != 0.)
	{
	  if(verbose)
	    std::cout << "Converting to physical units. (Multiplication by a^" << a_exponant << ", a=" << a  << ")" << std::endl;
	  const double factor = std::pow(a, static_cast<double>(a_exponant));
	  for(size_t i(0); i<totNumElem; ++i)
	    array[i] *= factor;
	}
      else
	{
	  if(verbose)
	    std::cout << "Converting to physical units. No conversion needed !" << std::endl;
	}
    }

  /* Apply h units conversion */
  if(noH)
    {
      if(h_exponant != 0.)
	{
	  if(verbose)
	    std::cout << "Converting to h-free units. (Multiplication by h^" << h_exponant << ", h=" << hubbleParam << ")" << std::endl;
	  const double factor = std::pow(hubbleParam, static_cast<double>(h_exponant));
	  for(size_t i(0); i<totNumElem; ++i)
	    array[i] *= factor;
	}
      else
	{
	  if(verbose)
	    std::cout << "Converting to h-free units. No conversion needed !" << std::endl;
	}
    }

  delete[] threads;
  delete[] thread_out;
}








template<typename T, typename S>
void readMultipleArrays(fileType type, const std::string& dir, const std::string& tag, const std::vector<std::string>& arrayName, std::vector<std::vector<S> >& array, bool physicalUnits=true, bool noH=true, int numThreads=16, bool verbose=true)
{
  /* Check that we have at least one thread */
  if(numThreads < 1)
    error("Invalid number of threads. Value must be >0.");

  /* Open file #0 to read global information */  
  const hid_t h_file = H5Fopen(fileName(type, dir, tag).c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
  if(h_file < 0)
    error("Impossible to read file #0");

  const size_t numArrays = arrayName.size();
  if(numArrays != array.size())
    error("'arrayName' and 'array' have different sizes");

  /* Decide which of the 6 values for the number of elements to read if we are dealing with a sn[ai]pshot */
  const int partType = getGadgetType(type, arrayName[0]);

  /* Check that the partType is the same for all arrays */
  for(size_t i(1); i<arrayName.size(); ++i)
    if(partType != getGadgetType(type, arrayName[i]))
      error("Not all arrays have the same particle type");

  /* Read general information from file #0 */
  const int numSnap = readAttribute<int>(h_file, numFilesFieldName(type));
  const int totNumElem = readAttribute<Tuple<6, int> >(h_file, totElementFieldName(type))[partType];
  const double hubbleParam = readAttribute<double>(h_file, "/Header/HubbleParam");
  const double a = readAttribute<double>(h_file, "/Header/ExpansionFactor");
  H5Fclose(h_file);

  if(verbose)
    std::cout << "Extracting " << totNumElem << " values for many arrays (PartType=" << partType << ") in " << numSnap << " " << fileTypeName(type) << " files with tag '" << tag << "' using " << numThreads << " thread(s)." << std::endl;
  
  /* Prepare array */
  for(size_t i(0); i<numArrays; ++i)
    array[i].resize(totNumElem);
  if(totNumElem == 0)
    return;

  /* Separate arrays for parallel reads */
  std::vector<size_t> sizes(numSnap, 0);
  std::vector<size_t> offsets(numSnap, 0);
  
  /* Prepare data for threads*/
  pthread_t* threads = new pthread_t[numThreads];
  thread_info<S>* thread_out = new thread_info<S>[numThreads];
  for(int i(0); i<numThreads; ++i)
    {
      thread_out[i].id = i;
      thread_out[i].numSnap = numSnap;
      thread_out[i].numThreads = numThreads;
      thread_out[i].type = type;
      thread_out[i].partType = partType;
      thread_out[i].dir = dir;
      thread_out[i].tag = tag;
      thread_out[i].arrayName = arrayName[0];
      thread_out[i].array = &array[0][0];
      thread_out[i].sizes = &sizes[0];
      thread_out[i].offsets = &offsets[0];
      thread_out[i].oldSubfindFix = false;
    }

  timeval begin, end;
  gettimeofday(&begin, NULL);

  if(verbose)
    std::cout << "Reading array lengths" << std::flush;

  /* Compute sizes for parallel reads using threads */
  for(int i(0); i<numThreads; ++i)
    {
      const int ret = pthread_create(&threads[i], 0, thread_readSize<S>, &thread_out[i]);
      if(ret != 0)
	error("Error while launching thread");
    }

  for (int i(0); i < numThreads; ++i) 
    {
      const int ret = pthread_join(threads[i], 0);
      if(ret != 0)
	error("Error while stopping thread");
    } 

  gettimeofday(&end, NULL);  
  if(verbose)
    std::cout << " took " << ((end.tv_sec-begin.tv_sec)*1000000 + end.tv_usec-begin.tv_usec) / 1e6 << "s" << std::endl;


  /* Do a scan to convert from size to offset */
  for(int i(1); i<numSnap; ++i)
    offsets[i] = offsets[i-1] + sizes[i-1];

  /* Loop over the arrays to read */
  for(size_t j(0); j<numArrays; ++j)
    {

      for(int i(0); i<numThreads; ++i)
	{
	  thread_out[i].arrayName = arrayName[j];
	  thread_out[i].array = &array[j][0];
	}

      /* Read data from files using threads */
      gettimeofday(&begin, NULL);
      if(verbose)
	std::cout << "Reading data (" << arrayName[j] << ") " << std::flush;

      for(int i(0); i<numThreads; ++i)
	{
	  const int ret = pthread_create(&threads[i], 0, thread_readData<T,S>, &thread_out[i]);
	  if(ret != 0)
	    error("Error while launching thread");
	}

      for (int i(0); i < numThreads; ++i) 
	{
	  const int ret = pthread_join(threads[i], 0);
	  if(ret != 0)
	    error("Error while stopping thread");
	} 

      gettimeofday(&end, NULL);  
      if(verbose)
	std::cout << " took " << ((end.tv_sec-begin.tv_sec)*1000000 + end.tv_usec-begin.tv_usec) / 1e6 << "s" << std::endl;
      
      /* Find a non-empty file for unit conversion */
      int nonEmpty = -1;
      for(int i(0); i<numSnap; ++i)
	{
	  if(sizes[i] > 0)
	    {
	      nonEmpty = i;
	      break;
	    }
	}

      /* Read factors for unit conversion */
      const hid_t h_file2 = H5Fopen(fileName(type, dir, tag, nonEmpty).c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
      const float a_exponant = readUnits<float>(h_file2, arrayName[j] + "/aexp-scale-exponent");
      const float h_exponant = readUnits<float>(h_file2, arrayName[j] + "/h-scale-exponent");
      H5Fclose(h_file2);

      /* Apply comoving to physical units conversion */
      if(physicalUnits && a_exponant != 0.)
	{
	  const double factor = std::pow(a, static_cast<double>(a_exponant));
	  for(int i(0); i<totNumElem; ++i)
	    array[j][i] *= factor;
	}
      
      /* Apply h units conversion */
      if(noH && h_exponant != 0.)
	{
	  const double factor = std::pow(hubbleParam, static_cast<double>(h_exponant));
	  for(int i(0); i<totNumElem; ++i)
	    array[j][i] *= factor;
	}
    }

  delete[] threads;
  delete[] thread_out;
}



#endif //READ_ARRAY_HPP