MOOSE - Multiscale Object Oriented Simulation Environment
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
cnpy.hpp
Go to the documentation of this file.
1 /*
2  * =====================================================================================
3  *
4  * Filename: cnpy.h
5  *
6  * Description: Write a stl vector to numpy format 2.
7  *
8  * This program is part of MOOSE simulator.
9  *
10  * Version: 1.0
11  * Created: 05/04/2016 10:36:19 AM
12  * Revision: none
13  * Compiler: gcc
14  *
15  * Author: Dilawar Singh (), dilawars@ncbs.res.in
16  * Organization: NCBS Bangalore
17  *
18  * =====================================================================================
19  */
20 
21 #ifndef cnpy_INC
22 #define cnpy_INC
23 
24 #include <iostream>
25 #include <fstream>
26 #include <vector>
27 #include <cassert>
28 #include <complex>
29 #include <typeinfo>
30 
31 #ifdef ENABLE_CPP11
32 #include <memory>
33 #include <array>
34 #else /* ----- not ENABLE_CPP11 ----- */
35 #endif /* ----- not ENABLE_CPP11 ----- */
36 
37 #include <string>
38 
39 #include <stdint.h>
40 
41 #include "global.h"
42 
43 #include "../utility/print_function.hpp"
44 
45 
46 using namespace std;
47 
48 namespace cnpy2 {
49 
50 // Check the endian-ness of machine at run-time. This is from library
51 // https://github.com/rogersce/cnpy
52 char BigEndianTest();
53 
54 // And another function to convert given std datatype to numpy representation.
55 char map_type(const std::type_info& t);
56 
57 void split(vector<string>& strs, string& input, const string& pat);
58 
68 bool is_valid_numpy_file( FILE* fp );
69 
75 void parse_header( FILE* fp, string& header );
76 
84 void change_shape_in_header( const string& filename
85  , const size_t data_len, const size_t numcols
86  );
87 
88 static const unsigned int __pre__size__ = 8;
89 static char __pre__[__pre__size__] = {
90  (char)0x93, 'N', 'U', 'M', 'P', 'Y' /* Magic */
91  , (char)0x01, (char) 0x00 /* format */
92 };
93 
94 template< typename T>
96  FILE* fp
97  , const vector<string>& colnames
98  , vector<unsigned int>shape , char version
99  )
100 {
101  // Heder are always at the begining of file.
102  fseek( fp, 0, SEEK_SET );
103  char endianChar = cnpy2::BigEndianTest();
104  char formatChar = cnpy2::map_type( typeid(T) );
105 
106  string dict = ""; // This is the header to numpy file
107  dict += "{'descr': [";
108  for( vector<string>::const_iterator it = colnames.begin();
109  it != colnames.end(); it++ )
110  dict += "('" + *it + "' , '" + endianChar + formatChar + "'),";
111 
112  dict += "], 'fortran_order': False, 'shape': (";
113  dict += moose::toString(shape[0]);
114  for(size_t i = 1; i < shape.size(); i++)
115  {
116  dict += ",";
117  dict += moose::toString(shape[i]);
118  }
119  if( shape.size() == 1) dict += ",";
120  dict += "), }";
121 
122  // When appending to this file, we need to rewrite header. Size of header
123  // might change since shape values might change. Minimum shape from (1,)
124  // could become quite large (13132131321,) etc. For safety region, append a
125  // chunk of whitespace characters so that overwriting header does not
126  // overwrite the data. This trick would save us from copying data into
127  // memory.
128  dict += string(11, ' '); /* 32 bit number is fit */
129 
130  // pad with spaces so that preamble+headerlen+dict is modulo 16 bytes.
131  // preamble is 8 bytes, header len is 4 bytes, total 12.
132  // dict needs to end with \n
133  unsigned int remainder = 16 - (12 + dict.size()) % 16;
134  dict.insert(dict.end(),remainder,' ');
135  *(dict.end()-1) = '\n';
136 
137  if( version == '2' )
138  __pre__[6] = (char) 0x02;
139 
140  fwrite( __pre__, sizeof( char ), __pre__size__, fp );
141 
142  // Now write the size of dict. It is 2bytes long in version 1 and 4 bytes
143  // long in version 2.
144  if( version == '2' )
145  {
146  uint32_t s = dict.size();
147  fwrite( (char*)&s, sizeof( uint32_t ), 1, fp );
148  }
149  else
150  {
151  int16_t s = dict.size();
152  fwrite( (char*)&s, sizeof( uint16_t ), 1, fp );
153  }
154  fwrite( dict.c_str(), sizeof(char), dict.size(), fp );
155 }
156 
157 // write to version 1 or version 2.
158 template<typename T>
160  const string& outfile
161  , const vector<double>& vec
162  , vector<string> colnames
163  , const string openmode
164  , const char version = '1'
165  )
166 {
167 
168  // In our application, we need to write a vector as matrix. We do not
169  // support the stacking of matrices.
170  vector<unsigned int> shape;
171 
172  if( colnames.size() == 0)
173  return;
174 
175  shape.push_back( vec.size() / colnames.size());
176 
177  /* In mode "w", open the file and write a header as well. When file is open
178  * in mode "a", we assume that file is alreay a valid numpy file.
179  */
180  if( openmode == "w" )
181  {
182  FILE* fp = fopen( outfile.c_str(), "wb" );
183  if( NULL == fp )
184  {
185  moose::showWarn( "Could not open file " + outfile );
186  return;
187  }
188  write_header<T>( fp, colnames, shape, version );
189  fclose( fp );
190  }
191  else /* Append mode. */
192  {
193  // Do a sanity check if file is really a numpy file.
194  FILE* fp = fopen( outfile.c_str(), "r" );
195  if( ! fp )
196  {
197  moose::showError( "Can't open " + outfile + " to validate" );
198  return;
199  }
200  else if(! is_valid_numpy_file( fp ) )
201  {
202  moose::showWarn( outfile + " is not a valid numpy file"
203  + " I am not goind to write to it"
204  );
205  return;
206  }
207  if( fp )
208  fclose( fp );
209  // And change the shape in header.
210  change_shape_in_header( outfile, vec.size(), colnames.size() );
211  }
212 
213  FILE* fp = fopen( outfile.c_str(), "ab" );
214  if( NULL == fp )
215  {
216  moose::showWarn( "Could not open " + outfile + " to write " );
217  return;
218  }
219  fwrite( &vec[0], sizeof(T), vec.size(), fp );
220  fclose( fp );
221 
222 }
223 
224 
225 } /* Namespace cnpy2 ends. */
226 #endif /* ----- #ifndef cnpy_INC ----- */
string toString(double x)
Convert a given value to string.
Definition: global.cpp:199
char map_type(const std::type_info &t)
Definition: cnpy.cpp:36
void split(vector< string > &strs, string &input, const string &pat)
Definition: cnpy.cpp:63
static char __pre__[__pre__size__]
Definition: cnpy.hpp:89
void write_header(FILE *fp, const vector< string > &colnames, vector< unsigned int >shape, char version)
Definition: cnpy.hpp:95
void showError(string msg)
bool is_valid_numpy_file(FILE *fp)
Check if a numpy file is sane or not.
Definition: cnpy.cpp:84
void save_numpy(const string &outfile, const vector< double > &vec, vector< string > colnames, const string openmode, const char version= '1')
Definition: cnpy.hpp:159
static const unsigned int __pre__size__
Definition: cnpy.hpp:88
void parse_header(FILE *fp, string &header)
Parser header from a numpy file. Store it in vector.
Definition: cnpy.cpp:109
char BigEndianTest()
Definition: cnpy.cpp:29
void showWarn(string msg)
void change_shape_in_header(const string &filename, const size_t data_len, const size_t numcols)
Change shape in numpy header.
Definition: cnpy.cpp:130