2011-09-14 23 views
8

Estoy usando el HDF5 C++ API para escribir archivos de conjunto de datos en 2D. HDF Group tiene an example to create un archivo HDF5 a partir de un tamaño de matriz estáticamente definido, que he modificado para adaptarlo a mis necesidades a continuación. Sin embargo, necesito una matriz dinámica, donde NX y NY se determinan en tiempo de ejecución. He encontrado another solution to create 2D arrays using the "new" keyword para ayudar a crear una matriz dinámica. Aquí es lo que tengo:HDF5 Interfaz C++: escribir matrices 2D dinámicas

#include "StdAfx.h" 
#include "H5Cpp.h" 
using namespace H5; 

const H5std_string FILE_NAME("C:\\SDS.h5"); 
const H5std_string DATASET_NAME("FloatArray"); 
const int NX = 5; // dataset dimensions 
const int NY = 6; 

int main (void) 
{ 
    // Create a 2D array using "new" method 
    double **data = new double*[NX]; 
    for (int j = 0; j < NX; j++)   // 0 1 2 3 4 5 
    {         // 1 2 3 4 5 6 
     data[j] = new double[NY];  // 2 3 4 5 6 7 
     for (int i = 0; i < NY; i++)  // 3 4 5 6 7 8 
      data[j][i] = (float)(i + j); // 4 5 6 7 8 9 
    } 

    // Create HDF5 file and dataset 
    H5File file(FILE_NAME, H5F_ACC_TRUNC); 
    hsize_t dimsf[2] = {NX, NY}; 
    DataSpace dataspace(2, dimsf); 
    DataSet dataset = file.createDataSet(DATASET_NAME, PredType::NATIVE_DOUBLE, 
              dataspace); 
    // Attempt to write data to HDF5 file 
    dataset.write(data, PredType::NATIVE_DOUBLE); 

    // Clean up 
    for(int j = 0; j < NX; j++) 
     delete [] data[j]; 
    delete [] data; 
    return 0; 
} 

El archivo resultante, sin embargo, no es el esperado (salida de hdf5dump):

HDF5 "SDS.h5" { 
GROUP "/" { 
    DATASET "FloatArray" { 
     DATATYPE H5T_IEEE_F64LE 
     DATASPACE SIMPLE { (5, 6)/(5, 6) } 
     DATA { 
     (0,0): 4.76465e-307, 4.76541e-307, -7.84591e+298, -2.53017e-098, 0, 
     (0,5): 3.8981e-308, 
     (1,0): 4.76454e-307, 0, 2.122e-314, -7.84591e+298, 0, 1, 
     (2,0): 2, 3, 4, 5, -2.53017e-098, -2.65698e+303, 
     (3,0): 0, 3.89814e-308, 4.76492e-307, 0, 2.122e-314, -7.84591e+298, 
     (4,0): 1, 2, 3, 4, 5, 6 
     } 
    } 
} 
} 

El problema se deriva de nuevo a la forma en que se creó la matriz 2D (ya que esto el ejemplo funciona bien con un método de matriz estática). A medida que entiendo de this email thread:

La biblioteca HDF5 espera una matriz contigua de elementos, no punteros a elementos de dimensiones inferiores

como soy bastante nuevo en C++/HDF5, estoy no estoy seguro de cómo crear una matriz de tamaño dinámico en tiempo de ejecución que es una matriz contigua de elementos. No quiero hacer el método más complicado "hipereslabio" descrito en el hilo del correo electrónico, ya que parece demasiado complicado. Cualquier ayuda es apreciada.

Respuesta

7

Bueno, no sé nada de HDF5, pero las matrices 2D dinámicas en C++ con un búfer contiguo se pueden simular utilizando una matriz 1D de tamaño NX * NY. Por ejemplo:

Asignación:

double *data = new double[NX*NY]; 

elemento de acceso:

data[j*NY + i] 

(en lugar de data[j][i])

+1

Esta solución es simple de implementar y escalas muy bien (estoy usando tamaños de matriz con elementos de 21.9M). El resultado se verifica perfectamente en la salida del archivo HDF5. –

+0

Funciona como un encanto. Gracias. – CuriousCase

+0

Sé que esto es bastante viejo, pero creo que hay un error en la última oración. Debería ser: (en lugar de 'data [i] [j]') –

2

En la programación científica es común para representar matrices multidimensionales como una gran variedad de 1D y luego calculando el desplazamiento correspondiente de los índices multidimensionales, por ej. como se ve en la respuesta de Doc Brown.

Como alternativa, puede sobrecargar el operador de subíndice (operator[]()) para proporcionar una interfaz que permita el uso de índices multidimensionales respaldados por la matriz 1D. O mejor aún, use una biblioteca que hace esto, como Boost multi_array. O en el caso de que sus matrices 2D sean matrices, puede usar una buena biblioteca de álgebra lineal de C++ como Eigen.

5

Aquí es cómo escribir N matrices de dimensiones en formato HDF5

Es mucho mejor usar la clase boostmulti_array. Esto es equivalente a usar std::vector en lugar de matrices en bruto: hace toda la administración de la memoria por usted y puede acceder a los elementos con la misma eficiencia que las matrices en bruto mediante el uso de subscripciones familiares (p.data[12][13] = 46)

Aquí es un breve ejemplo:

#include <algorithm> 
#include <boost/multi_array.hpp> 
using boost::multi_array; 
using boost::extents; 

// dataset dimensions set at run time 
int NX = 5, NY = 6, NZ = 7; 


// allocate array using the "extents" helper. 
// This makes it easier to see how big the array is 
multi_array<double, 3> float_data(extents[NX][NY][NZ]); 

// use resize to change size when necessary 
// float_data.resize(extents[NX + 5][NY + 4][NZ + 3]); 


// This is how you would fill the entire array with a value (e.g. 3.0) 
std::fill_n(float_data.data(), float_data.num_elements(), 3.0) 

// initialise the array to some variables 
for (int ii = 0; ii != NX; ii++) 
    for (int jj = 0; jj != NY; jj++) 
     for (int kk = 0; kk != NZ; kk++) 
      float_data[ii][jj][kk] = ii + jj + kk 

// write to HDF5 format 
H5::H5File file("SDS.h5", H5F_ACC_TRUNC); 
write_hdf5(file, "doubleArray", float_data); 

La última línea llama a una función que se puede escribir multi_array s de cualquier dimensión y cualquier tipo estándar número (ints, chars, floats etc).

Aquí está el código para write_hdf5().

En primer lugar, debemos Mapa C++ tipos a los tipos HDF5 (desde el H5 C++ API):

#include <cstdint> 

//!_______________________________________________________________________________________ 
//!  
//!  map types to HDF5 types 
//!   
//!  
//!  \author lg (04 March 2013) 
//!_______________________________________________________________________________________ 

template<typename T> struct get_hdf5_data_type 
{ static H5::PredType type() 
    { 
     //static_assert(false, "Unknown HDF5 data type"); 
     return H5::PredType::NATIVE_DOUBLE; 
    } 
}; 
template<> struct get_hdf5_data_type<char>     { H5::IntType type { H5::PredType::NATIVE_CHAR  }; }; 
//template<> struct get_hdf5_data_type<unsigned char>  { H5::IntType type { H5::PredType::NATIVE_UCHAR  }; }; 
//template<> struct get_hdf5_data_type<short>    { H5::IntType type { H5::PredType::NATIVE_SHORT  }; }; 
//template<> struct get_hdf5_data_type<unsigned short>  { H5::IntType type { H5::PredType::NATIVE_USHORT  }; }; 
//template<> struct get_hdf5_data_type<int>     { H5::IntType type { H5::PredType::NATIVE_INT  }; }; 
//template<> struct get_hdf5_data_type<unsigned int>  { H5::IntType type { H5::PredType::NATIVE_UINT  }; }; 
//template<> struct get_hdf5_data_type<long>    { H5::IntType type { H5::PredType::NATIVE_LONG  }; }; 
//template<> struct get_hdf5_data_type<unsigned long>  { H5::IntType type { H5::PredType::NATIVE_ULONG  }; }; 
template<> struct get_hdf5_data_type<long long>    { H5::IntType type { H5::PredType::NATIVE_LLONG  }; }; 
template<> struct get_hdf5_data_type<unsigned long long> { H5::IntType type { H5::PredType::NATIVE_ULLONG  }; }; 
template<> struct get_hdf5_data_type<int8_t>    { H5::IntType type { H5::PredType::NATIVE_INT8  }; }; 
template<> struct get_hdf5_data_type<uint8_t>    { H5::IntType type { H5::PredType::NATIVE_UINT8  }; }; 
template<> struct get_hdf5_data_type<int16_t>    { H5::IntType type { H5::PredType::NATIVE_INT16  }; }; 
template<> struct get_hdf5_data_type<uint16_t>    { H5::IntType type { H5::PredType::NATIVE_UINT16  }; }; 
template<> struct get_hdf5_data_type<int32_t>    { H5::IntType type { H5::PredType::NATIVE_INT32  }; }; 
template<> struct get_hdf5_data_type<uint32_t>    { H5::IntType type { H5::PredType::NATIVE_UINT32  }; }; 
template<> struct get_hdf5_data_type<int64_t>    { H5::IntType type { H5::PredType::NATIVE_INT64  }; }; 
template<> struct get_hdf5_data_type<uint64_t>    { H5::IntType type { H5::PredType::NATIVE_UINT64  }; }; 
template<> struct get_hdf5_data_type<float>     { H5::FloatType type { H5::PredType::NATIVE_FLOAT  }; }; 
template<> struct get_hdf5_data_type<double>    { H5::FloatType type { H5::PredType::NATIVE_DOUBLE  }; }; 
template<> struct get_hdf5_data_type<long double>   { H5::FloatType type { H5::PredType::NATIVE_LDOUBLE }; }; 

entonces podemos usar un poco de reenvío de plantillas mágica para hacer una función del tipo correcto para dar salida a nuestra datos. Dado que este es el código de plantilla, tiene que vivir en un fichero de cabecera si se va a matrices HDF5 salida de varios archivos de origen en su programa:

//!_______________________________________________________________________________________ 
//!  
//!  write_hdf5 multi_array 
//!   
//!  \author leo Goodstadt (04 March 2013) 
//!  
//!_______________________________________________________________________________________ 
template<typename T, std::size_t DIMENSIONS, typename hdf5_data_type> 
void do_write_hdf5(H5::H5File file, const std::string& data_set_name, const boost::multi_array<T, DIMENSIONS>& data, hdf5_data_type& datatype) 
{ 
    // Little endian for x86 
    //FloatType datatype(get_hdf5_data_type<T>::type()); 
    datatype.setOrder(H5T_ORDER_LE); 

    vector<hsize_t> dimensions(data.shape(), data.shape() + DIMENSIONS); 
    H5::DataSpace dataspace(DIMENSIONS, dimensions.data()); 

    H5::DataSet dataset = file.createDataSet(data_set_name, datatype, dataspace); 

    dataset.write(data.data(), datatype); 
} 

template<typename T, std::size_t DIMENSIONS> 
void write_hdf5(H5::H5File file, const std::string& data_set_name, const boost::multi_array<T, DIMENSIONS>& data) 
{ 

    get_hdf5_data_type<T> hdf_data_type; 
    do_write_hdf5(file, data_set_name, data, hdf_data_type.type); 
}