outputchannel.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. ///////////////////////////////////////////////////////////////////////////////////////
  2. /// \file outputchannel.cpp
  3. /// \brief Classes for formatting and printing output from the model
  4. ///
  5. /// \author Joe Siltberg
  6. /// $Date: 2013-10-10 10:20:33 +0200 (Thu, 10 Oct 2013) $
  7. ///
  8. ///////////////////////////////////////////////////////////////////////////////////////
  9. #include "config.h"
  10. #include "guess.h"
  11. #include "outputchannel.h"
  12. #include <vector>
  13. namespace GuessOutput {
  14. ColumnDescriptor::ColumnDescriptor(const char* title,
  15. int width,
  16. int precision)
  17. : t(title),
  18. w(width),
  19. p(precision) {
  20. }
  21. const std::string& ColumnDescriptor::title() const {
  22. return t;
  23. }
  24. int ColumnDescriptor::width() const {
  25. return w;
  26. }
  27. int ColumnDescriptor::precision() const {
  28. return p;
  29. }
  30. ColumnDescriptors::ColumnDescriptors(const std::vector<std::string>& titles,
  31. int width,
  32. int precision) {
  33. for (size_t i = 0; i < titles.size(); i++) {
  34. columns.push_back(ColumnDescriptor(titles[i].c_str(),
  35. width,
  36. precision));
  37. }
  38. }
  39. void ColumnDescriptors::operator+=(const ColumnDescriptor& col) {
  40. columns.push_back(col);
  41. }
  42. void ColumnDescriptors::operator+=(const ColumnDescriptors& cols) {
  43. columns.insert(columns.end(), cols.columns.begin(), cols.columns.end());
  44. }
  45. size_t ColumnDescriptors::size() const {
  46. return columns.size();
  47. }
  48. const ColumnDescriptor& ColumnDescriptors::operator[](size_t i) const {
  49. return columns[i];
  50. }
  51. TableDescriptor::TableDescriptor(const char* name,
  52. const ColumnDescriptors& columns)
  53. : n(name),
  54. cols(columns) {
  55. }
  56. const std::string& TableDescriptor::name() const {
  57. return n;
  58. }
  59. const ColumnDescriptors& TableDescriptor::columns() const {
  60. return cols;
  61. }
  62. Table::Table()
  63. : identifier(-1) {
  64. }
  65. Table::Table(int id)
  66. : identifier(id) {
  67. }
  68. int Table::id() const {
  69. return identifier;
  70. }
  71. bool Table::invalid() const {
  72. return identifier == -1;
  73. }
  74. Table OutputChannel::create_table(const TableDescriptor& descriptor) {
  75. Table table((int) table_descriptors.size());
  76. table_descriptors.push_back(descriptor);
  77. values.resize(table_descriptors.size());
  78. return table;
  79. }
  80. void OutputChannel::add_value(const Table& table, double d) {
  81. // do nothing for unused tables
  82. if (table.invalid()) {
  83. return;
  84. }
  85. // make sure this row isn't already full
  86. const TableDescriptor& td = table_descriptors[table.id()];
  87. if (values[table.id()].size() == td.columns().size()) {
  88. fail("Added too many values to a row in table %s!",
  89. td.name().c_str());
  90. }
  91. values[table.id()].push_back(d);
  92. }
  93. const TableDescriptor&
  94. OutputChannel::get_table_descriptor(const Table& table) const {
  95. return table_descriptors[table.id()];
  96. }
  97. const TableDescriptor&
  98. OutputChannel::get_table_descriptor(int id) const {
  99. return table_descriptors.at(id);
  100. }
  101. const std::vector<double>
  102. OutputChannel::get_current_row(const Table& table) const {
  103. return values[table.id()];
  104. }
  105. void OutputChannel::clear_current_row(const Table& table) {
  106. values[table.id()].clear();
  107. std::vector<double>().swap(values[table.id()]);
  108. }
  109. FileOutputChannel::FileOutputChannel(const char* out_dir,
  110. int coords_precision)
  111. : output_directory(out_dir) {
  112. // calculate suitable width for the coords columns,
  113. // longitudes take at most 4 characters (-180) before the decimal
  114. // point, add the decimal point, coords_precision and a little margin:
  115. const int LON_MAX_LEN = 4;
  116. const int MARGIN = 2;
  117. int coords_width = LON_MAX_LEN+1+coords_precision+MARGIN;
  118. xtring str;
  119. str.printf("%%%ds", coords_width);
  120. coords_title_format = (char*)str;
  121. str.printf("%%%d.%df", coords_width, coords_precision);
  122. coords_format = (char*)str;
  123. }
  124. FileOutputChannel::~FileOutputChannel() {
  125. for (size_t i = 0; i < files.size(); i++) {
  126. fclose(files[i]);
  127. }
  128. }
  129. Table FileOutputChannel::create_table(const TableDescriptor& descriptor) {
  130. Table table;
  131. FILE* file = NULL;
  132. if (descriptor.name() != "") {
  133. std::string full_path = output_directory + descriptor.name();
  134. file = fopen(full_path.c_str(), "w");
  135. if (file == NULL) {
  136. fail("Could not open %s for output\n"\
  137. "Close the file if it is open in another application",
  138. full_path.c_str());
  139. }
  140. else {
  141. table = OutputChannel::create_table(descriptor);
  142. files.push_back(file);
  143. printed_header.push_back(false);
  144. }
  145. }
  146. return table;
  147. }
  148. void FileOutputChannel::finish_row(const Table& table,
  149. double lon,
  150. double lat,
  151. int year) {
  152. finish_row(table, lon, lat, year, NOEXTRA, 0);
  153. }
  154. void FileOutputChannel::close_table(Table& table) {
  155. // do nothing for unused tables
  156. if (table.invalid()) {
  157. return;
  158. }
  159. FILE* file = files[table.id()];
  160. fclose(file);
  161. }
  162. void FileOutputChannel::finish_row(const Table& table,
  163. double lon,
  164. double lat,
  165. int year,
  166. columntype ctype,
  167. int ecvalue) {
  168. // do nothing for unused tables
  169. if (table.invalid()) {
  170. return;
  171. }
  172. FILE* file = files[table.id()];
  173. // make sure all columns have been added
  174. const std::vector<double>& row = get_current_row(table);
  175. const TableDescriptor& td = get_table_descriptor(table);
  176. if (row.size() < td.columns().size()) {
  177. fail("Too few values in a row in table %s", td.name().c_str());
  178. }
  179. // print the header if this is the first output for this file
  180. if (!printed_header[table.id()]) {
  181. // print title for coordinates and time columns
  182. fprintf(file, coords_title_format.c_str(), "Lon");
  183. fprintf(file, coords_title_format.c_str(), "Lat");
  184. fprintf(file, "%6s", "Year");
  185. if (ctype == CTDAY) {
  186. fprintf(file, "%5s", "Day");
  187. }
  188. else if (ctype == CTMONTH) {
  189. fprintf(file, "%5s", "Mth");
  190. }
  191. // print each column title
  192. int nbr_cols = (int) get_table_descriptor(table).columns().size();
  193. for (int i = 0; i < nbr_cols; i++) {
  194. fputs(format_header(table, i), file);
  195. }
  196. fprintf(file, "\n");
  197. printed_header[table.id()] = true;
  198. }
  199. // print out coordinates and time
  200. fprintf(file, coords_format.c_str(), lon);
  201. fprintf(file, coords_format.c_str(), lat);
  202. fprintf(file, "%6d", year);
  203. if (ctype != NOEXTRA) {
  204. fprintf(file, "%5d", ecvalue);
  205. }
  206. // print out the values
  207. for (size_t i = 0; i < row.size(); i++) {
  208. fprintf(file, format(table, (int)i), row[i]);
  209. }
  210. fprintf(file, "\n");
  211. fflush(file);
  212. // start on a new row
  213. clear_current_row(table);
  214. }
  215. const char* FileOutputChannel::format(const Table& table, int column) {
  216. const TableDescriptor& td = get_table_descriptor(table);
  217. const ColumnDescriptor& cd = td.columns()[column];
  218. // NB: not thread safe
  219. static char buf[100];
  220. sprintf(buf, " %%%d.%df", cd.width(), cd.precision());
  221. return buf;
  222. }
  223. const char* FileOutputChannel::format_header(const Table& table, int column) {
  224. const TableDescriptor& td = get_table_descriptor(table);
  225. const ColumnDescriptor& cd = td.columns()[column];
  226. // NB: not thread safe
  227. static char format[100];
  228. static char buf[100];
  229. sprintf(format, " %%%ds", cd.width());
  230. sprintf(buf, format, cd.title().c_str());
  231. return buf;
  232. }
  233. OutputRows::OutputRows(OutputChannel* output_channel,
  234. double longitude,
  235. double latitude,
  236. int year)
  237. : out(output_channel),
  238. lon(longitude),
  239. lat(latitude),
  240. y(year),
  241. c(NOEXTRA),
  242. v(0) {
  243. }
  244. OutputRows::OutputRows(OutputChannel* output_channel,
  245. double longitude,
  246. double latitude,
  247. int year,
  248. columntype ctype,
  249. int ecvalue)
  250. : out(output_channel),
  251. lon(longitude),
  252. lat(latitude),
  253. y(year),
  254. c(ctype),
  255. v(ecvalue) {
  256. }
  257. OutputRows::~OutputRows() {
  258. for (size_t i = 0; i < used_tables.size(); i++) {
  259. if (used_tables[i]) {
  260. if (c == NOEXTRA) {
  261. out->finish_row(Table(i), lon, lat, y);
  262. }
  263. else {
  264. out->finish_row(Table(i), lon, lat, y, c, v);
  265. }
  266. }
  267. }
  268. }
  269. void OutputRows::add_value(const Table& table,double d) {
  270. // do nothing for unused tables
  271. if (table.invalid()) {
  272. return;
  273. }
  274. int id = table.id();
  275. // remember that this table has gotten a value
  276. if (id >= static_cast<int>(used_tables.size())) {
  277. used_tables.resize(id+1);
  278. }
  279. used_tables[id] = true;
  280. // send the value to the output channel
  281. out->add_value(table, d);
  282. }
  283. #ifdef COMPRESS_OUTPUT
  284. GZFileOutputChannel::~GZFileOutputChannel() {
  285. for (size_t i = 0; i < gzfiles.size(); i++) {
  286. if ( gzfiles[i] != NULL ) {
  287. gzclose(gzfiles[i]);
  288. }
  289. else {
  290. if (!printed_header[i]) {
  291. const TableDescriptor& descriptor = get_table_descriptor(i);
  292. dprintf("warning! no output created for table %s\n", descriptor.name().c_str());
  293. }
  294. }
  295. }
  296. }
  297. Table GZFileOutputChannel::create_table(const TableDescriptor& descriptor) {
  298. Table table;
  299. if (descriptor.name() != "") {
  300. table = OutputChannel::create_table(descriptor);
  301. // file will be created when first line is written
  302. gzfiles.push_back(NULL);
  303. printed_header.push_back(false);
  304. }
  305. return table;
  306. }
  307. void GZFileOutputChannel::close_table(Table& table) {
  308. // do nothing for unused tables
  309. if (table.invalid()) {
  310. return;
  311. }
  312. // untested - destructor always used
  313. gzFile gzfile = gzfiles[table.id()];
  314. if ( gzfile != NULL ) {
  315. gzclose(gzfile);
  316. gzfiles[table.id()] = NULL;
  317. }
  318. else {
  319. if (!printed_header[table.id()]) {
  320. const TableDescriptor& descriptor = get_table_descriptor(table);
  321. dprintf("warning! no output created for table %s\n", descriptor.name().c_str());
  322. }
  323. }
  324. }
  325. void GZFileOutputChannel::finish_row(const Table& table,
  326. double lon,
  327. double lat,
  328. int year,
  329. columntype ctype,
  330. int ecvalue) {
  331. // do nothing for unused tables
  332. if (table.invalid()) {
  333. return;
  334. }
  335. gzFile gzfile = gzfiles[table.id()];
  336. // make sure all columns have been added
  337. const std::vector<double>& row = get_current_row(table);
  338. const TableDescriptor& td = get_table_descriptor(table);
  339. if (row.size() < td.columns().size()) {
  340. fail("Too few values in a row in table %s", td.name().c_str());
  341. }
  342. // print the header and open .gz file if this is the first output for this table
  343. if (!printed_header[table.id()]) {
  344. FILE* file = NULL;
  345. std::string full_path = output_directory + td.name() + ".hdr";
  346. file = fopen(full_path.c_str(), "w");
  347. if (file == NULL) {
  348. fail("Could not open %s for output\n"\
  349. "Close the file if it is open in another application",
  350. full_path.c_str());
  351. }
  352. // print title for coordinates and time columns
  353. fprintf(file, coords_title_format.c_str(), "Lon");
  354. fprintf(file, coords_title_format.c_str(), "Lat");
  355. fprintf(file, "%6s", "Year");
  356. if (ctype == CTDAY) {
  357. fprintf(file, "%5s", "Day");
  358. }
  359. else if (ctype == CTMONTH) {
  360. fprintf(file, "%5s", "Mth");
  361. }
  362. // print each column title
  363. int nbr_cols = (int) get_table_descriptor(table).columns().size();
  364. for (int i = 0; i < nbr_cols; i++) {
  365. fputs(format_header(table, i), file);
  366. }
  367. fprintf(file, "\n");
  368. printed_header[table.id()] = true;
  369. fclose(file);
  370. // create .gz file
  371. full_path = output_directory + td.name() + ".gz";
  372. gzfile = gzopen(full_path.c_str(), "wb");
  373. if (gzfile == NULL) {
  374. fail("Could not open %s for output\n" \
  375. "Close the file if it is open in another application",
  376. full_path.c_str());
  377. }
  378. else {
  379. gzfiles[table.id()] = gzfile;
  380. }
  381. }
  382. // use buffer to write only once to gzfile
  383. xtring line(1024);
  384. xtring buffer(1024);
  385. // print out coordinates and time
  386. buffer.printf(coords_format.c_str(), lon);
  387. line+=buffer;
  388. buffer.printf(coords_format.c_str(), lat);
  389. line+=buffer;
  390. buffer.printf("%6d", year);
  391. line+=buffer;
  392. if (ctype != NOEXTRA) {
  393. buffer.printf("%5d", ecvalue);
  394. line+=buffer;
  395. }
  396. // print out the values
  397. for (size_t i = 0; i < row.size(); i++) {
  398. buffer.printf(format(table, (int)i), row[i]);
  399. line+=buffer;
  400. }
  401. buffer.printf("\n");
  402. line+=buffer;
  403. // write line to file
  404. if ( gzfile == NULL ) {
  405. fail("Problem with output file for table %s\n", td.name().c_str());
  406. }
  407. gzputs(gzfile, line);
  408. //gzfile->flush(); // don't flush every line since this degrades performance
  409. // start on a new row
  410. clear_current_row(table);
  411. }
  412. #endif // COMPRESS_OUTPUT
  413. }