count_buffer.cpp 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. /******************************************************************************
  2. *
  3. * Copyright (C) 2020 by
  4. * The Salk Institute for Biological Studies and
  5. * Pittsburgh Supercomputing Center, Carnegie Mellon University
  6. *
  7. * Use of this source code is governed by an MIT-style
  8. * license that can be found in the LICENSE file or at
  9. * https://opensource.org/licenses/MIT.
  10. *
  11. ******************************************************************************/
  12. #include "count_buffer.h"
  13. #include <iomanip>
  14. #include <sstream>
  15. #include "logging.h"
  16. #include "util.h"
  17. #include "bng/filesystem_utils.h"
  18. using namespace std;
  19. const uint GDAT_COLUMN_WIDTH = 14;
  20. namespace MCell {
  21. void CountItem::write_as_dat(std::ostream& out) const {
  22. out << time << " " << value << "\n";
  23. }
  24. static void write_gdat_value(std::ostream& outs, double d) {
  25. // there is no way to set number of digits in an exponent
  26. // so we must do it manually
  27. // split exponent and base as string - we do not want to do
  28. // any numerical computation here
  29. stringstream ss;
  30. ss << scientific << setprecision(GDAT_COLUMN_WIDTH - 6) << d;
  31. string s = ss.str();
  32. size_t pos = s.find('e');
  33. assert(pos != string::npos);
  34. string base = s.substr(0, pos);
  35. int exponent = stoi(s.substr(pos + 1));
  36. string sign;
  37. if (exponent >= 0) {
  38. sign = "+";
  39. }
  40. else {
  41. // setfill/setw does not add '0' for negative values
  42. sign = "-";
  43. exponent = -exponent;
  44. }
  45. outs << base << "e" << sign << setfill('0') << setw(2) << exponent;
  46. }
  47. void CountBuffer::flush() {
  48. if (!fout.is_open()) {
  49. open(true);
  50. }
  51. if (output_format == CountOutputFormat::DAT) {
  52. // there is a single column
  53. assert(data.size() == 1);
  54. for (const auto& item: data[0]) {
  55. assert(item.column_index == 0);
  56. item.write_as_dat(fout);
  57. }
  58. }
  59. else {
  60. assert(data.size() >= 1);
  61. // output row
  62. // expecting that each column has the same depth
  63. size_t num_rows = data[0].size();
  64. for (size_t row = 0; row < num_rows; row++) {
  65. // simply use time from the first column
  66. double time = data[0][row].time;
  67. fout << " ";
  68. write_gdat_value(fout, time);
  69. // output each column
  70. for (size_t col = 0; col < data.size(); col++) {
  71. assert(row < data[col].size());
  72. const auto& item = data[col][row];
  73. release_assert(cmp_eq(item.time, time, SQRT_EPS) && "Mismatch in gdat column times");
  74. fout << " ";
  75. write_gdat_value(fout, item.value);
  76. }
  77. fout << "\n";
  78. }
  79. }
  80. fout.flush(); // flush the data so the user can see them
  81. for (auto& column: data) {
  82. column.clear();
  83. }
  84. }
  85. void CountBuffer::write_gdat_header() {
  86. assert(fout.is_open());
  87. assert(output_format == CountOutputFormat::GDAT);
  88. fout << "#";
  89. string time = "time";
  90. time.insert(time.begin(), GDAT_COLUMN_WIDTH - time.size(), ' ');
  91. fout << time;
  92. for (const string& name: column_names) {
  93. string col = name;
  94. if (GDAT_COLUMN_WIDTH > col.size()) {
  95. col.insert(col.begin(), GDAT_COLUMN_WIDTH - col.size() + 2, ' ');
  96. }
  97. else {
  98. col = " " + col;
  99. }
  100. fout << col;
  101. }
  102. fout << "\n";
  103. }
  104. bool CountBuffer::open(bool error_is_fatal) {
  105. FSUtils::make_dir_for_file_w_multiple_attempts(filename);
  106. if (!open_for_append) {
  107. // create an empty file so that we know that nothing was stored
  108. fout.open(filename);
  109. }
  110. else {
  111. // appending is used when restoring a checkpoint
  112. // opens a new file if the file does not exist
  113. fout.open(filename, std::ofstream::out | std::ofstream::app);
  114. }
  115. // write header
  116. if (output_format == CountOutputFormat::GDAT && !open_for_append) {
  117. write_gdat_header();
  118. }
  119. if (!fout.is_open()) {
  120. mcell_warn("Could not open file %s for writing.", filename.c_str());
  121. if (error_is_fatal) {
  122. mcell_error("Terminating due to error.");
  123. }
  124. return false;
  125. }
  126. return true;
  127. }
  128. void CountBuffer::flush_and_close() {
  129. flush();
  130. if (fout.is_open()) {
  131. fout.close();
  132. }
  133. else {
  134. bool ok = open(false);
  135. if (ok) {
  136. fout.close();
  137. }
  138. }
  139. }
  140. } /* namespace MCell */