binary_react_output.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. /* C headers */
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. /* local includes */
  6. #include "binary_react_output.h"
  7. #include "logging.h"
  8. /********************************************************************
  9. *
  10. * this function initializes the binary reaction data output,
  11. * opens the binary data files, and writes the initial file
  12. * headers.
  13. *
  14. ********************************************************************/
  15. int init_binary_reaction_data(struct output_block *block_data,
  16. struct volume *world)
  17. {
  18. /* make sure the compression level is sane */
  19. if (block_data->binary_out->compression_level < 0
  20. || block_data->binary_out->compression_level > 9)
  21. {
  22. mcell_log("The compression level for binary output files has "
  23. "to be between 0 (off) and 9 (maximal)");
  24. return 1;
  25. }
  26. if (create_binary_output_file(block_data)) return 1;
  27. if (write_binary_header(block_data, world->time_unit, world->iterations,
  28. world->chkpt_iterations, world->chkpt_seq_num, world->start_time))
  29. return 1;
  30. if (write_binary_data_info(block_data)) return 1;
  31. return 0;
  32. }
  33. /********************************************************************
  34. *
  35. * function creating the binary reaction data output files
  36. *
  37. ********************************************************************/
  38. int
  39. create_binary_output_file(struct output_block *block_data)
  40. {
  41. /* create directory if requested */
  42. char dir_name[BUFSIZ] = "";
  43. if (block_data->binary_out->directory != NULL)
  44. {
  45. if (mkdirs(block_data->binary_out->directory) != 0)
  46. return 1;
  47. strncpy(dir_name, block_data->binary_out->directory, BUFSIZ-2);
  48. strncat(dir_name, "/", 2);
  49. }
  50. /* create output file */
  51. if (block_data->binary_out->compression_level == 0)
  52. {
  53. FILE *output_file = fopen(block_data->binary_out->filename, "w");
  54. if (output_file == NULL) return 1;
  55. block_data->binary_out->output_file = output_file;
  56. }
  57. else
  58. {
  59. /* initialize compressed gzip format */
  60. if (block_data->binary_out->compression_type == COMPRESS_GZIP)
  61. {
  62. gzFile compressed_output_file =
  63. gzopen OF((block_data->binary_out->filename, "w"));
  64. if (compressed_output_file == NULL) return 1;
  65. block_data->binary_out->gz_compressed_output_file = compressed_output_file;
  66. gzsetparams OF((compressed_output_file,
  67. block_data->binary_out->compression_level,
  68. Z_DEFAULT_STRATEGY));
  69. }
  70. /* initialize compressed bzip format */
  71. else if (block_data->binary_out->compression_type == COMPRESS_BZIP2)
  72. {
  73. FILE *output_file = fopen(block_data->binary_out->filename, "wb");
  74. if (output_file == NULL) return 1;
  75. block_data->binary_out->output_file = output_file;
  76. int error = 0;
  77. BZFILE* compressed_output_file =
  78. BZ2_bzWriteOpen(&error, block_data->binary_out->output_file,
  79. block_data->binary_out->compression_level, 0, 0);
  80. if (error != BZ_OK) return 1;
  81. block_data->binary_out->bz_compressed_output_file = compressed_output_file;
  82. }
  83. else
  84. {
  85. mcell_log("Unknown compression method for binary reaction output.");
  86. return 1;
  87. }
  88. }
  89. return 0;
  90. }
  91. /***********************************************************************
  92. *
  93. * create the header file for the binary data file
  94. *
  95. * the header has the following structure:
  96. *
  97. * const char* api_tag
  98. * uint16_t output_type 1 = STEP; 2 = TIME_LIST/ITERATION_LIST
  99. * uint16_t time_list_length length of timelist/iteration list
  100. * double [] time_list list of output times/iterations; for
  101. * output type STEP only contains a single
  102. * double equal to the output time step
  103. * uint32_t buffersize size of the output buffer used to write
  104. * the output (needed for parsing the output)
  105. *
  106. **********************************************************************/
  107. int write_binary_header(struct output_block *block_data,
  108. double time_step, long long iterations,
  109. long long chkpt_iterations,
  110. u_int chkpt_seq_num, long long start_time)
  111. {
  112. char api_tag[] = "MCELL_BINARY_API_2";
  113. BINARY_WRITE(api_tag, sizeof(api_tag), block_data);
  114. /* write type of output (STEP, TIMELIST, ITERATIONS) */
  115. uint64_t num_data_items = 0;
  116. uint64_t time_list_length = 0;
  117. uint16_t output_type = 0;
  118. if (block_data->timer_type == OUTPUT_BY_STEP)
  119. {
  120. output_type = 1;
  121. time_list_length = 1;
  122. double output_step = block_data->step_time;
  123. // the number of data items is determined by the smaller of iterations
  124. // or chkpt_iterations unless checkpoint iterations is 0
  125. long long start = 0;
  126. long long end = iterations;
  127. int interStep = output_step/time_step;
  128. if (chkpt_iterations != 0) {
  129. start = start_time;
  130. start += interStep - (start % interStep);
  131. long long checkptEnd = start_time + chkpt_iterations;
  132. end = (checkptEnd < iterations) ? checkptEnd : iterations;
  133. if (end % interStep != 0) {
  134. end -= end % interStep;
  135. }
  136. }
  137. num_data_items = (uint64_t)((end - start)/interStep)+1;
  138. // make sure to account for 0th iteration when checkpointing
  139. if (chkpt_iterations != 0 && start_time == 0) {
  140. num_data_items++;
  141. }
  142. /* write info */
  143. BINARY_WRITE(&output_type, sizeof(output_type), block_data);
  144. BINARY_WRITE(&num_data_items, sizeof(num_data_items), block_data);
  145. BINARY_WRITE(&time_list_length, sizeof(time_list_length), block_data);
  146. BINARY_WRITE(&output_step, sizeof(output_step), block_data);
  147. }
  148. else if (block_data->timer_type == OUTPUT_BY_TIME_LIST
  149. || block_data->timer_type == OUTPUT_BY_ITERATION_LIST)
  150. {
  151. if (block_data->timer_type == OUTPUT_BY_TIME_LIST)
  152. {
  153. output_type = 2;
  154. }
  155. else
  156. {
  157. output_type = 3;
  158. }
  159. time_list_length = 0;
  160. struct num_expr_list *time_list = block_data->time_list_head;
  161. while (time_list != NULL)
  162. {
  163. ++time_list_length;
  164. time_list = time_list->next;
  165. }
  166. num_data_items = time_list_length;
  167. /* write info */
  168. BINARY_WRITE(&output_type, sizeof(output_type), block_data);
  169. BINARY_WRITE(&num_data_items, sizeof(num_data_items), block_data);
  170. BINARY_WRITE(&time_list_length, sizeof(time_list_length), block_data);
  171. time_list = block_data->time_list_head;
  172. while (time_list != NULL)
  173. {
  174. BINARY_WRITE(&(time_list->value), sizeof(double), block_data);
  175. time_list = time_list->next;
  176. }
  177. }
  178. /* write the output block size; the reader will need this info
  179. * to recover the data from the file */
  180. uint64_t buffersize = (uint64_t)(block_data->buffersize);
  181. BINARY_WRITE(&buffersize, sizeof(uint64_t), block_data);
  182. return 0;
  183. }
  184. /***********************************************************************
  185. *
  186. * create part of the header for the binary data file containing
  187. * information about the contained data sets
  188. *
  189. * this part of the header has the following structure:
  190. *
  191. * uint64_t num_data_files number of data files contained in file
  192. *
  193. * --- repeat num_data_files times ----
  194. * char * filename name of the contained file
  195. * uint16_t num_columns number of data columns in file
  196. * ---- repeat num_columns times -----
  197. * uint16_t column_type data type contained in column
  198. * 0 = int, 1 = double
  199. *
  200. **********************************************************************/
  201. int write_binary_data_info(struct output_block *block_data)
  202. {
  203. /* determine the total number of data files and write to file*/
  204. uint64_t num_data_files = 0;
  205. for (struct output_set *set = block_data->data_set_head; set != NULL;
  206. set=set->next)
  207. ++num_data_files;
  208. BINARY_WRITE(&num_data_files, sizeof(num_data_files), block_data);
  209. /* for each output file we extract the filename, the number of
  210. * columns and the type stored in each column (int/double) */
  211. for (struct output_set *set = block_data->data_set_head; set != NULL;
  212. set=set->next)
  213. {
  214. /* extract filename from path and write to file */
  215. char *name = set->outfile_name;
  216. char *filename_start = strrchr(name,'/');
  217. if (filename_start == NULL)
  218. filename_start = name;
  219. size_t filename_length = strlen(name)-(name-filename_start);
  220. char filename[filename_length];
  221. strncpy(filename, filename_start+1, filename_length);
  222. BINARY_WRITE(filename, strlen(filename)+1, block_data);
  223. /* extract the number of columns and their datatype */
  224. uint64_t num_columns = 0;
  225. struct output_column *col_ptr;
  226. for (col_ptr = set->column_head; col_ptr != NULL; col_ptr=col_ptr->next)
  227. ++num_columns;
  228. BINARY_WRITE(&num_columns, sizeof(num_columns), block_data);
  229. for (col_ptr = set->column_head; col_ptr != NULL; col_ptr=col_ptr->next)
  230. {
  231. uint16_t data_type = 0;
  232. if (col_ptr->data_type == COUNT_INT)
  233. {
  234. data_type = 0;
  235. }
  236. else if (col_ptr->data_type == COUNT_DBL)
  237. {
  238. data_type = 1;
  239. }
  240. else
  241. {
  242. mcell_log("Encountered unsupported output type (such as TRIGGER)\n"
  243. "in binary reaction output. Please use regular reaction\n"
  244. "data output instead.");
  245. return 1;
  246. }
  247. BINARY_WRITE(&data_type, sizeof(data_type), block_data);
  248. }
  249. }
  250. return 0;
  251. }
  252. /**************************************************************************
  253. * write_binary_reaction_output:
  254. *
  255. * this function writes reaction output data to the binary data file.
  256. *
  257. * In: the output_set we want to write to disk
  258. * the flag that signals an end to the scheduled reaction outputs
  259. * Out: 0 on success, 1 on failure.
  260. * The reaction output buffer is flushed and written to disk.
  261. * Indices are not reset; that's the job of the calling function.
  262. **************************************************************************/
  263. int write_binary_reaction_output(struct output_block *block_data,
  264. struct output_set *set)
  265. {
  266. struct output_column *column;
  267. u_int n_output;
  268. u_int i;
  269. n_output = set->block->buffersize;
  270. if (set->block->buf_index < set->block->buffersize)
  271. n_output = set->block->buf_index;
  272. /* Write data */
  273. double value;
  274. for (i=0;i<n_output;i++)
  275. {
  276. for (column=set->column_head; column!=NULL; column=column->next)
  277. {
  278. switch (column->data_type)
  279. {
  280. case COUNT_INT:
  281. value = (double)((int*)column->buffer)[i];
  282. BINARY_WRITE(&value, sizeof(double), block_data);
  283. break;
  284. case COUNT_DBL:
  285. BINARY_WRITE(&(((double*)column->buffer)[i]), sizeof(double),
  286. block_data);
  287. break;
  288. default:
  289. mcell_error("Error in write_binary_reaction_output: "
  290. "unknown reaction data type encountered.");
  291. break;
  292. }
  293. }
  294. }
  295. set->chunk_count++;
  296. return 0;
  297. }
  298. /*********************************************************************
  299. *
  300. * function for closing the binary reaction data ouput file and
  301. * doing any other necessary cleanup.
  302. *
  303. ********************************************************************/
  304. void close_binary_reaction_data(struct output_block *block_data)
  305. {
  306. free(block_data->binary_out->filename);
  307. if (block_data->binary_out->compression_level == 0)
  308. {
  309. fclose(block_data->binary_out->output_file);
  310. }
  311. else
  312. {
  313. if (block_data->binary_out->compression_type == COMPRESS_GZIP)
  314. {
  315. gzclose OF((block_data->binary_out->gz_compressed_output_file));
  316. }
  317. else
  318. {
  319. unsigned int bytes_in = 0;
  320. unsigned int bytes_out = 0;
  321. int error = 0;
  322. BZ2_bzWriteClose(&error, block_data->binary_out->bz_compressed_output_file,
  323. 0, &bytes_in, &bytes_out);
  324. if (error != BZ_OK)
  325. mcell_error("Error closing the bzlib compressed file.");
  326. /* also close the underlying file pointer */
  327. fclose(block_data->binary_out->output_file);
  328. }
  329. }
  330. }